Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
script3r
GitHub Repository: script3r/os161
Path: blob/master/kern/dev/generic/console.c
2098 views
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
}
453
454