/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* Machine (and hardware) independent console driver.31*32* We expose a simple interface to the rest of the kernel: "putch" to33* print a character, "getch" to read one.34*35* As long as the device we're connected to does, we allow printing in36* an interrupt handler or with interrupts off (by polling),37* transparently to the caller. Note that getch by polling is not38* supported, although such support could be added without undue39* difficulty.40*41* Note that nothing happens until we have a device to write to. A42* buffer of size DELAYBUFSIZE is used to hold output that is43* generated before this point. This means that (1) using kprintf for44* debugging problems that occur early in initialization is awkward,45* and (2) if the system crashes before we find a console, no output46* at all may appear.47*48* Note that we have no input buffering; characters typed too rapidly49* will be lost.50*/5152#include <types.h>53#include <kern/errno.h>54#include <lib.h>55#include <uio.h>56#include <thread.h>57#include <current.h>58#include <synch.h>59#include <generic/console.h>60#include <vfs.h>61#include <device.h>62#include "autoconf.h"6364/*65* The console device.66*/67static struct con_softc *the_console = NULL;6869/*70* Lock so user I/Os are atomic.71* We use two locks so readers waiting for input don't lock out writers.72*/73static struct lock *con_userlock_read = NULL;74static struct lock *con_userlock_write = NULL;7576//////////////////////////////////////////////////7778/*79* This is for accumulating characters printed before the80* console is set up. Upon console setup they are dumped81* to the actual console; thenceforth this space is unused.82*/83#define DELAYBUFSIZE 102484static char delayed_outbuf[DELAYBUFSIZE];85static size_t delayed_outbuf_pos=0;8687static88void89putch_delayed(int ch)90{91/*92* No synchronization needed: called only during system startup93* by main thread.94*/9596KASSERT(delayed_outbuf_pos < sizeof(delayed_outbuf));97delayed_outbuf[delayed_outbuf_pos++] = ch;98}99100static101void102flush_delay_buf(void)103{104size_t i;105for (i=0; i<delayed_outbuf_pos; i++) {106putch(delayed_outbuf[i]);107}108delayed_outbuf_pos = 0;109}110111//////////////////////////////////////////////////112113/*114* Print a character, using polling instead of interrupts to wait for115* I/O completion.116*/117static118void119putch_polled(struct con_softc *cs, int ch)120{121cs->cs_sendpolled(cs->cs_devdata, ch);122}123124static125void126putch_prepare_polled(struct con_softc *cs)127{128if (cs->cs_startpolling != NULL) {129cs->cs_startpolling(cs->cs_devdata);130}131}132133static134void135putch_complete_polled(struct con_softc *cs)136{137if (cs->cs_endpolling != NULL) {138cs->cs_endpolling(cs->cs_devdata);139}140}141142//////////////////////////////////////////////////143144/*145* Print a character, using interrupts to wait for I/O completion.146*/147static148void149putch_intr(struct con_softc *cs, int ch)150{151P(cs->cs_wsem);152cs->cs_send(cs->cs_devdata, ch);153}154155/*156* Read a character, using interrupts to wait for I/O completion.157*/158static159int160getch_intr(struct con_softc *cs)161{162unsigned char ret;163164P(cs->cs_rsem);165ret = cs->cs_gotchars[cs->cs_gotchars_tail];166cs->cs_gotchars_tail =167(cs->cs_gotchars_tail + 1) % CONSOLE_INPUT_BUFFER_SIZE;168return ret;169}170171/*172* Called from underlying device when a read-ready interrupt occurs.173*174* Note: if gotchars_head == gotchars_tail, the buffer is empty. Thus175* if gotchars_head+1 == gotchars_tail, the buffer is full. A slightly176* tidier way to implement this check (that avoids wasting a slot,177* too) would be with a second semaphore used with a nonblocking P,178* but we don't have that in OS/161.179*/180void181con_input(void *vcs, int ch)182{183struct con_softc *cs = vcs;184unsigned nexthead;185186nexthead = (cs->cs_gotchars_head + 1) % CONSOLE_INPUT_BUFFER_SIZE;187if (nexthead == cs->cs_gotchars_tail) {188/* overflow; drop character */189return;190}191192cs->cs_gotchars[cs->cs_gotchars_head] = ch;193cs->cs_gotchars_head = nexthead;194195V(cs->cs_rsem);196}197198/*199* Called from underlying device when a write-done interrupt occurs.200*/201void202con_start(void *vcs)203{204struct con_softc *cs = vcs;205206V(cs->cs_wsem);207}208209//////////////////////////////////////////////////210211/*212* Exported interface.213*214* Warning: putch must work even in an interrupt handler or with215* interrupts disabled, and before the console is probed. getch need216* not, and does not.217*/218219void220putch(int ch)221{222struct con_softc *cs = the_console;223224if (cs==NULL) {225putch_delayed(ch);226}227else if (curthread->t_in_interrupt || curthread->t_iplhigh_count > 0) {228putch_polled(cs, ch);229}230else {231putch_intr(cs, ch);232}233}234235void236putch_prepare(void)237{238struct con_softc *cs = the_console;239240if (cs == NULL) {241/* nothing */242}243else if (curthread->t_in_interrupt || curthread->t_iplhigh_count > 0) {244putch_prepare_polled(cs);245}246else {247/* nothing */248}249}250251void252putch_complete(void)253{254struct con_softc *cs = the_console;255256if (cs == NULL) {257/* nothing */258}259else if (curthread->t_in_interrupt || curthread->t_iplhigh_count > 0) {260putch_complete_polled(cs);261}262else {263/* nothing */264}265}266267int268getch(void)269{270struct con_softc *cs = the_console;271KASSERT(cs != NULL);272KASSERT(!curthread->t_in_interrupt && curthread->t_iplhigh_count == 0);273274return getch_intr(cs);275}276277////////////////////////////////////////////////////////////278279/*280* VFS interface functions281*/282283static284int285con_open(struct device *dev, int openflags)286{287(void)dev;288(void)openflags;289return 0;290}291292static293int294con_close(struct device *dev)295{296(void)dev;297return 0;298}299300static301int302con_io(struct device *dev, struct uio *uio)303{304int result;305char ch;306struct lock *lk;307308(void)dev; // unused309310if (uio->uio_rw==UIO_READ) {311lk = con_userlock_read;312}313else {314lk = con_userlock_write;315}316317KASSERT(lk != NULL);318lock_acquire(lk);319320while (uio->uio_resid > 0) {321if (uio->uio_rw==UIO_READ) {322ch = getch();323if (ch=='\r') {324ch = '\n';325}326result = uiomove(&ch, 1, uio);327if (result) {328lock_release(lk);329return result;330}331if (ch=='\n') {332break;333}334}335else {336result = uiomove(&ch, 1, uio);337if (result) {338lock_release(lk);339return result;340}341if (ch=='\n') {342putch('\r');343}344putch(ch);345}346}347lock_release(lk);348return 0;349}350351static352int353con_ioctl(struct device *dev, int op, userptr_t data)354{355/* No ioctls. */356(void)dev;357(void)op;358(void)data;359return EINVAL;360}361362static363int364attach_console_to_vfs(struct con_softc *cs)365{366struct device *dev;367int result;368369dev = kmalloc(sizeof(*dev));370if (dev==NULL) {371return ENOMEM;372}373374dev->d_open = con_open;375dev->d_close = con_close;376dev->d_io = con_io;377dev->d_ioctl = con_ioctl;378dev->d_blocks = 0;379dev->d_blocksize = 1;380dev->d_data = cs;381382result = vfs_adddev("con", dev, 0);383if (result) {384kfree(dev);385return result;386}387388return 0;389}390391////////////////////////////////////////////////////////////392393/*394* Config routine called by autoconf.c after we are attached to something.395*/396397int398config_con(struct con_softc *cs, int unit)399{400struct semaphore *rsem, *wsem;401struct lock *rlk, *wlk;402403/*404* Only allow one system console.405* Further devices that could be the system console are ignored.406*407* Do not hardwire the console to be "con1" instead of "con0",408* or these asserts will go off.409*/410if (unit>0) {411KASSERT(the_console!=NULL);412return ENODEV;413}414KASSERT(the_console==NULL);415416rsem = sem_create("console read", 0);417if (rsem == NULL) {418return ENOMEM;419}420wsem = sem_create("console write", 1);421if (wsem == NULL) {422sem_destroy(rsem);423return ENOMEM;424}425rlk = lock_create("console-lock-read");426if (rlk == NULL) {427sem_destroy(rsem);428sem_destroy(wsem);429return ENOMEM;430}431wlk = lock_create("console-lock-write");432if (wlk == NULL) {433lock_destroy(rlk);434sem_destroy(rsem);435sem_destroy(wsem);436return ENOMEM;437}438439cs->cs_rsem = rsem;440cs->cs_wsem = wsem;441cs->cs_gotchars_head = 0;442cs->cs_gotchars_tail = 0;443444the_console = cs;445con_userlock_read = rlk;446con_userlock_write = wlk;447448flush_delay_buf();449450return attach_console_to_vfs(cs);451}452453454