root/kern/dev/generic/console.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. putch_delayed
  2. flush_delay_buf
  3. putch_polled
  4. putch_prepare_polled
  5. putch_complete_polled
  6. putch_intr
  7. getch_intr
  8. con_input
  9. con_start
  10. putch
  11. putch_prepare
  12. putch_complete
  13. getch
  14. con_open
  15. con_close
  16. con_io
  17. con_ioctl
  18. attach_console_to_vfs
  19. config_con

   1 /*
   2  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
   3  *      The President and Fellows of Harvard College.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the University nor the names of its contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * Machine (and hardware) independent console driver.
  32  *
  33  * We expose a simple interface to the rest of the kernel: "putch" to
  34  * print a character, "getch" to read one.
  35  *
  36  * As long as the device we're connected to does, we allow printing in
  37  * an interrupt handler or with interrupts off (by polling),
  38  * transparently to the caller. Note that getch by polling is not
  39  * supported, although such support could be added without undue
  40  * difficulty.
  41  *
  42  * Note that nothing happens until we have a device to write to. A
  43  * buffer of size DELAYBUFSIZE is used to hold output that is
  44  * generated before this point. This means that (1) using kprintf for
  45  * debugging problems that occur early in initialization is awkward,
  46  * and (2) if the system crashes before we find a console, no output
  47  * at all may appear.
  48  *
  49  * Note that we have no input buffering; characters typed too rapidly
  50  * will be lost.
  51  */
  52 
  53 #include <types.h>
  54 #include <kern/errno.h>
  55 #include <lib.h>
  56 #include <uio.h>
  57 #include <thread.h>
  58 #include <current.h>
  59 #include <synch.h>
  60 #include <generic/console.h>
  61 #include <vfs.h>
  62 #include <device.h>
  63 #include "autoconf.h"
  64 
  65 /*
  66  * The console device.
  67  */
  68 static struct con_softc *the_console = NULL;
  69 
  70 /*
  71  * Lock so user I/Os are atomic.
  72  * We use two locks so readers waiting for input don't lock out writers.
  73  */
  74 static struct lock *con_userlock_read = NULL;
  75 static struct lock *con_userlock_write = NULL;
  76 
  77 //////////////////////////////////////////////////
  78 
  79 /*
  80  * This is for accumulating characters printed before the
  81  * console is set up. Upon console setup they are dumped 
  82  * to the actual console; thenceforth this space is unused.
  83  */
  84 #define DELAYBUFSIZE  1024
  85 static char delayed_outbuf[DELAYBUFSIZE];
  86 static size_t delayed_outbuf_pos=0;
  87 
  88 static
  89 void
  90 putch_delayed(int ch)
  91 {
  92         /*
  93          * No synchronization needed: called only during system startup
  94          * by main thread.
  95          */
  96 
  97         KASSERT(delayed_outbuf_pos < sizeof(delayed_outbuf));
  98         delayed_outbuf[delayed_outbuf_pos++] = ch;
  99 }
 100 
 101 static
 102 void
 103 flush_delay_buf(void)
 104 {
 105         size_t i;
 106         for (i=0; i<delayed_outbuf_pos; i++) {
 107                 putch(delayed_outbuf[i]);
 108         }
 109         delayed_outbuf_pos = 0;
 110 }
 111 
 112 //////////////////////////////////////////////////
 113 
 114 /*
 115  * Print a character, using polling instead of interrupts to wait for
 116  * I/O completion.
 117  */
 118 static
 119 void
 120 putch_polled(struct con_softc *cs, int ch)
 121 {
 122         cs->cs_sendpolled(cs->cs_devdata, ch);
 123 }
 124 
 125 static
 126 void
 127 putch_prepare_polled(struct con_softc *cs)
 128 {
 129         if (cs->cs_startpolling != NULL) {
 130                 cs->cs_startpolling(cs->cs_devdata);
 131         }
 132 }
 133 
 134 static
 135 void
 136 putch_complete_polled(struct con_softc *cs)
 137 {
 138         if (cs->cs_endpolling != NULL) {
 139                 cs->cs_endpolling(cs->cs_devdata);
 140         }
 141 }
 142 
 143 //////////////////////////////////////////////////
 144 
 145 /*
 146  * Print a character, using interrupts to wait for I/O completion.
 147  */
 148 static
 149 void
 150 putch_intr(struct con_softc *cs, int ch)
 151 {
 152         P(cs->cs_wsem);
 153         cs->cs_send(cs->cs_devdata, ch);
 154 }
 155 
 156 /*
 157  * Read a character, using interrupts to wait for I/O completion.
 158  */
 159 static
 160 int
 161 getch_intr(struct con_softc *cs)
 162 {
 163         unsigned char ret;
 164 
 165         P(cs->cs_rsem);
 166         ret = cs->cs_gotchars[cs->cs_gotchars_tail];
 167         cs->cs_gotchars_tail =
 168                 (cs->cs_gotchars_tail + 1) % CONSOLE_INPUT_BUFFER_SIZE;
 169         return ret;
 170 }
 171 
 172 /*
 173  * Called from underlying device when a read-ready interrupt occurs.
 174  *
 175  * Note: if gotchars_head == gotchars_tail, the buffer is empty. Thus
 176  * if gotchars_head+1 == gotchars_tail, the buffer is full. A slightly
 177  * tidier way to implement this check (that avoids wasting a slot,
 178  * too) would be with a second semaphore used with a nonblocking P,
 179  * but we don't have that in OS/161.
 180  */
 181 void
 182 con_input(void *vcs, int ch)
 183 {
 184         struct con_softc *cs = vcs;
 185         unsigned nexthead;
 186 
 187         nexthead = (cs->cs_gotchars_head + 1) % CONSOLE_INPUT_BUFFER_SIZE;
 188         if (nexthead == cs->cs_gotchars_tail) {
 189                 /* overflow; drop character */
 190                 return;
 191         }
 192 
 193         cs->cs_gotchars[cs->cs_gotchars_head] = ch;
 194         cs->cs_gotchars_head = nexthead;
 195                 
 196         V(cs->cs_rsem);
 197 }
 198 
 199 /*
 200  * Called from underlying device when a write-done interrupt occurs.
 201  */
 202 void
 203 con_start(void *vcs)
 204 {
 205         struct con_softc *cs = vcs;
 206 
 207         V(cs->cs_wsem);
 208 }
 209 
 210 //////////////////////////////////////////////////
 211 
 212 /*
 213  * Exported interface.
 214  * 
 215  * Warning: putch must work even in an interrupt handler or with
 216  * interrupts disabled, and before the console is probed. getch need
 217  * not, and does not.
 218  */
 219 
 220 void
 221 putch(int ch)
 222 {
 223         struct con_softc *cs = the_console;
 224 
 225         if (cs==NULL) {
 226                 putch_delayed(ch);
 227         }
 228         else if (curthread->t_in_interrupt || curthread->t_iplhigh_count > 0) {
 229                 putch_polled(cs, ch);
 230         }
 231         else {
 232                 putch_intr(cs, ch);
 233         }
 234 }
 235 
 236 void
 237 putch_prepare(void)
 238 {
 239         struct con_softc *cs = the_console;
 240 
 241         if (cs == NULL) {
 242                 /* nothing */
 243         }
 244         else if (curthread->t_in_interrupt || curthread->t_iplhigh_count > 0) {
 245                 putch_prepare_polled(cs);
 246         }
 247         else {
 248                 /* nothing */
 249         }
 250 }
 251 
 252 void
 253 putch_complete(void)
 254 {
 255         struct con_softc *cs = the_console;
 256 
 257         if (cs == NULL) {
 258                 /* nothing */
 259         }
 260         else if (curthread->t_in_interrupt || curthread->t_iplhigh_count > 0) {
 261                 putch_complete_polled(cs);
 262         }
 263         else {
 264                 /* nothing */
 265         }
 266 }
 267 
 268 int
 269 getch(void)
 270 {
 271         struct con_softc *cs = the_console;
 272         KASSERT(cs != NULL);
 273         KASSERT(!curthread->t_in_interrupt && curthread->t_iplhigh_count == 0);
 274 
 275         return getch_intr(cs);
 276 }
 277 
 278 ////////////////////////////////////////////////////////////
 279 
 280 /*
 281  * VFS interface functions
 282  */
 283 
 284 static
 285 int
 286 con_open(struct device *dev, int openflags)
 287 {
 288         (void)dev;
 289         (void)openflags;
 290         return 0;
 291 }
 292 
 293 static
 294 int
 295 con_close(struct device *dev)
 296 {
 297         (void)dev;
 298         return 0;
 299 }
 300 
 301 static
 302 int
 303 con_io(struct device *dev, struct uio *uio)
 304 {
 305         int result;
 306         char ch;
 307         struct lock *lk;
 308 
 309         (void)dev;  // unused
 310 
 311         if (uio->uio_rw==UIO_READ) {
 312                 lk = con_userlock_read;
 313         }
 314         else {
 315                 lk = con_userlock_write;
 316         }
 317 
 318         KASSERT(lk != NULL);
 319         lock_acquire(lk);
 320 
 321         while (uio->uio_resid > 0) {
 322                 if (uio->uio_rw==UIO_READ) {
 323                         ch = getch();
 324                         if (ch=='\r') {
 325                                 ch = '\n';
 326                         }
 327                         result = uiomove(&ch, 1, uio);
 328                         if (result) {
 329                                 lock_release(lk);
 330                                 return result;
 331                         }
 332                         if (ch=='\n') {
 333                                 break;
 334                         }
 335                 }
 336                 else {
 337                         result = uiomove(&ch, 1, uio);
 338                         if (result) {
 339                                 lock_release(lk);
 340                                 return result;
 341                         }
 342                         if (ch=='\n') {
 343                                 putch('\r');
 344                         }
 345                         putch(ch);
 346                 }
 347         }
 348         lock_release(lk);
 349         return 0;
 350 }
 351 
 352 static
 353 int
 354 con_ioctl(struct device *dev, int op, userptr_t data)
 355 {
 356         /* No ioctls. */
 357         (void)dev;
 358         (void)op;
 359         (void)data;
 360         return EINVAL;
 361 }
 362 
 363 static
 364 int
 365 attach_console_to_vfs(struct con_softc *cs)
 366 {
 367         struct device *dev;
 368         int result;
 369 
 370         dev = kmalloc(sizeof(*dev));
 371         if (dev==NULL) {
 372                 return ENOMEM;
 373         }
 374 
 375         dev->d_open = con_open;
 376         dev->d_close = con_close;
 377         dev->d_io = con_io;
 378         dev->d_ioctl = con_ioctl;
 379         dev->d_blocks = 0;
 380         dev->d_blocksize = 1;
 381         dev->d_data = cs;
 382 
 383         result = vfs_adddev("con", dev, 0);
 384         if (result) {
 385                 kfree(dev);
 386                 return result;
 387         }
 388 
 389         return 0;
 390 }
 391 
 392 ////////////////////////////////////////////////////////////
 393 
 394 /*
 395  * Config routine called by autoconf.c after we are attached to something.
 396  */
 397 
 398 int
 399 config_con(struct con_softc *cs, int unit)
 400 {
 401         struct semaphore *rsem, *wsem;
 402         struct lock *rlk, *wlk;
 403 
 404         /*
 405          * Only allow one system console.
 406          * Further devices that could be the system console are ignored.
 407          *
 408          * Do not hardwire the console to be "con1" instead of "con0",
 409          * or these asserts will go off.
 410          */
 411         if (unit>0) {
 412                 KASSERT(the_console!=NULL);
 413                 return ENODEV;
 414         }
 415         KASSERT(the_console==NULL);
 416 
 417         rsem = sem_create("console read", 0);
 418         if (rsem == NULL) {
 419                 return ENOMEM;
 420         }
 421         wsem = sem_create("console write", 1);
 422         if (wsem == NULL) {
 423                 sem_destroy(rsem);
 424                 return ENOMEM;
 425         }
 426         rlk = lock_create("console-lock-read");
 427         if (rlk == NULL) {
 428                 sem_destroy(rsem);
 429                 sem_destroy(wsem);
 430                 return ENOMEM;
 431         }
 432         wlk = lock_create("console-lock-write");
 433         if (wlk == NULL) {
 434                 lock_destroy(rlk);
 435                 sem_destroy(rsem);
 436                 sem_destroy(wsem);
 437                 return ENOMEM;
 438         }
 439 
 440         cs->cs_rsem = rsem; 
 441         cs->cs_wsem = wsem; 
 442         cs->cs_gotchars_head = 0;
 443         cs->cs_gotchars_tail = 0;
 444 
 445         the_console = cs;
 446         con_userlock_read = rlk;
 447         con_userlock_write = wlk;
 448 
 449         flush_delay_buf();
 450 
 451         return attach_console_to_vfs(cs);
 452 }

/* [<][>][^][v][top][bottom][index][help] */