/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- putch_delayed
- flush_delay_buf
- putch_polled
- putch_prepare_polled
- putch_complete_polled
- putch_intr
- getch_intr
- con_input
- con_start
- putch
- putch_prepare
- putch_complete
- getch
- con_open
- con_close
- con_io
- con_ioctl
- attach_console_to_vfs
- 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 }