1 #include <u.h>
2 #include <libc.h>
3 #include "compat.h"
4 #include "kbd.h"
5 #include "error.h"
6
7 typedef struct Queue Queue;
8 struct Queue
9 {
10 QLock qwait;
11 Rendez rwait;
12
13 Lock lock;
14 int notempty;
15 char buf[1024];
16 char *w;
17 char *r;
18 char *e;
19 };
20
21 Queue* kbdq; /* unprocessed console input */
22 Queue* lineq; /* processed console input */
23 Snarf snarf = {
24 .vers = 1
25 };
26
27 static struct
28 {
29 QLock;
30 int raw; /* true if we shouldn't process input */
31 int ctl; /* number of opens to the control file */
32 int x; /* index into line */
33 char line[1024]; /* current input line */
34 } kbd;
35
36 /*
37 * cheapo fixed-length queues
38 */
39 static void
qwrite(Queue * q,void * v,int n)40 qwrite(Queue *q, void *v, int n)
41 {
42 char *buf, *next;
43 int i;
44
45 buf = v;
46 lock(&q->lock);
47 for(i = 0; i < n; i++){
48 next = q->w+1;
49 if(next >= q->e)
50 next = q->buf;
51 if(next == q->r)
52 break;
53 *q->w = buf[i];
54 q->w = next;
55 }
56 q->notempty = 1;
57 unlock(&q->lock);
58 rendwakeup(&q->rwait);
59 }
60
61 static int
qcanread(void * vq)62 qcanread(void *vq)
63 {
64 Queue *q;
65 int ne;
66
67 q = vq;
68 lock(&q->lock);
69 ne = q->notempty;
70 unlock(&q->lock);
71 return ne;
72 }
73
74 static int
qread(Queue * q,void * v,int n)75 qread(Queue *q, void *v, int n)
76 {
77 char *a;
78 int nn, notempty;
79
80 if(n == 0)
81 return 0;
82 a = v;
83 nn = 0;
84 for(;;){
85 lock(&q->lock);
86
87 while(nn < n && q->r != q->w){
88 a[nn++] = *q->r++;
89 if(q->r >= q->e)
90 q->r = q->buf;
91 }
92
93 notempty = q->notempty;
94 q->notempty = q->r != q->w;
95 unlock(&q->lock);
96 if(notempty)
97 break;
98
99 /*
100 * wait for something to show up in the kbd buffer.
101 */
102 qlock(&q->qwait);
103 if(waserror()){
104 qunlock(&q->qwait);
105 nexterror();
106 }
107 rendsleep(&q->rwait, qcanread, q);
108 qunlock(&q->qwait);
109 poperror();
110 }
111 return nn;
112 }
113
114 static Queue *
mkqueue(void)115 mkqueue(void)
116 {
117 Queue *q;
118
119 q = smalloc(sizeof(Queue));
120 q->r = q->buf;
121 q->w = q->r;
122 q->e = &q->buf[sizeof q->buf];
123 q->notempty = 0;
124 return q;
125 }
126
127 static void
echoscreen(char * buf,int n)128 echoscreen(char *buf, int n)
129 {
130 char *e, *p;
131 char ebuf[128];
132 int x;
133
134 p = ebuf;
135 e = ebuf + sizeof(ebuf) - 4;
136 while(n-- > 0){
137 if(p >= e){
138 screenputs(ebuf, p - ebuf);
139 p = ebuf;
140 }
141 x = *buf++;
142 if(x == 0x15){
143 *p++ = '^';
144 *p++ = 'U';
145 *p++ = '\n';
146 } else
147 *p++ = x;
148 }
149 if(p != ebuf)
150 screenputs(ebuf, p - ebuf);
151 }
152
153 /*
154 * Put character, possibly a rune, into read queue at interrupt time.
155 * Called at interrupt time to process a character.
156 */
157 void
kbdputc(int ch)158 kbdputc(int ch)
159 {
160 int n;
161 char buf[UTFmax];
162 Rune r;
163
164 r = ch;
165 n = runetochar(buf, &r);
166 qwrite(kbdq, buf, n);
167 if(!kbd.raw)
168 echoscreen(buf, n);
169 }
170
171 static void
kbdputcinit(void)172 kbdputcinit(void)
173 {
174 kbdq = mkqueue();
175 lineq = mkqueue();
176 kbd.raw = 0;
177 kbd.ctl = 0;
178 kbd.x = 0;
179 }
180
181 enum{
182 Qdir,
183 Qcons,
184 Qconsctl,
185 Qsnarf,
186 Qwinname,
187 };
188
189 static Dirtab consdir[]={
190 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
191 "cons", {Qcons}, 0, 0660,
192 "consctl", {Qconsctl}, 0, 0220,
193 "snarf", {Qsnarf}, 0, 0600,
194 "winname", {Qwinname}, 0, 0000,
195 };
196
197 static void
consinit(void)198 consinit(void)
199 {
200 kbdputcinit();
201 }
202
203 static Chan*
consattach(char * spec)204 consattach(char *spec)
205 {
206 return devattach('c', spec);
207 }
208
209 static Walkqid*
conswalk(Chan * c,Chan * nc,char ** name,int nname)210 conswalk(Chan *c, Chan *nc, char **name, int nname)
211 {
212 return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
213 }
214
215 static int
consstat(Chan * c,uchar * dp,int n)216 consstat(Chan *c, uchar *dp, int n)
217 {
218 return devstat(c, dp, n, consdir, nelem(consdir), devgen);
219 }
220
221 static Chan*
consopen(Chan * c,int omode)222 consopen(Chan *c, int omode)
223 {
224 c->aux = nil;
225 c = devopen(c, omode, consdir, nelem(consdir), devgen);
226 switch((ulong)c->qid.path){
227 case Qconsctl:
228 qlock(&kbd);
229 kbd.ctl++;
230 qunlock(&kbd);
231 break;
232 case Qsnarf:
233 if((c->mode&3) == OWRITE || (c->mode&3) == ORDWR)
234 c->aux = smalloc(sizeof(Snarf));
235 break;
236 }
237 return c;
238 }
239
240 void
setsnarf(char * buf,int n,int * vers)241 setsnarf(char *buf, int n, int *vers)
242 {
243 int i;
244
245 qlock(&snarf);
246 snarf.vers++;
247 if(vers)
248 *vers = snarf.vers;
249 for(i = 0; i < nelem(consdir); i++){
250 if(consdir[i].qid.type == Qsnarf){
251 consdir[i].qid.vers = snarf.vers;
252 break;
253 }
254 }
255 free(snarf.buf);
256 snarf.n = n;
257 snarf.buf = buf;
258 qunlock(&snarf);
259 }
260
261 static void
consclose(Chan * c)262 consclose(Chan *c)
263 {
264 Snarf *t;
265
266 switch((ulong)c->qid.path){
267 /* last close of control file turns off raw */
268 case Qconsctl:
269 if(c->flag&COPEN){
270 qlock(&kbd);
271 if(--kbd.ctl == 0)
272 kbd.raw = 0;
273 qunlock(&kbd);
274 }
275 break;
276 /* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */
277 case Qsnarf:
278 t = c->aux;
279 if(t == nil)
280 break;
281 setsnarf(t->buf, t->n, 0);
282 t->buf = nil; /* setsnarf took it */
283 free(t);
284 c->aux = nil;
285 break;
286 }
287 }
288
289 static long
consread(Chan * c,void * buf,long n,vlong off)290 consread(Chan *c, void *buf, long n, vlong off)
291 {
292 char ch;
293 int send;
294
295 if(n <= 0)
296 return n;
297 switch((ulong)c->qid.path){
298 case Qsnarf:
299 qlock(&snarf);
300 if(off < snarf.n){
301 if(off + n > snarf.n)
302 n = snarf.n - off;
303 memmove(buf, snarf.buf+off, n);
304 }else
305 n = 0;
306 qunlock(&snarf);
307 return n;
308
309 case Qdir:
310 return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
311
312 case Qcons:
313 qlock(&kbd);
314 if(waserror()){
315 qunlock(&kbd);
316 nexterror();
317 }
318 while(!qcanread(lineq)){
319 qread(kbdq, &ch, 1);
320 send = 0;
321 if(ch == 0){
322 /* flush output on rawoff -> rawon */
323 if(kbd.x > 0)
324 send = !qcanread(kbdq);
325 }else if(kbd.raw){
326 kbd.line[kbd.x++] = ch;
327 send = !qcanread(kbdq);
328 }else{
329 switch(ch){
330 case '\b':
331 if(kbd.x > 0)
332 kbd.x--;
333 break;
334 case 0x15: /* ^U */
335 kbd.x = 0;
336 break;
337 case '\n':
338 case 0x04: /* ^D */
339 send = 1;
340 default:
341 if(ch != 0x04)
342 kbd.line[kbd.x++] = ch;
343 break;
344 }
345 }
346 if(send || kbd.x == sizeof kbd.line){
347 qwrite(lineq, kbd.line, kbd.x);
348 kbd.x = 0;
349 }
350 }
351 n = qread(lineq, buf, n);
352 qunlock(&kbd);
353 poperror();
354 return n;
355
356 default:
357 print("consread 0x%llux\n", c->qid.path);
358 error(Egreg);
359 }
360 return -1; /* never reached */
361 }
362
363 static long
conswrite(Chan * c,void * va,long n,vlong)364 conswrite(Chan *c, void *va, long n, vlong)
365 {
366 Snarf *t;
367 char buf[256], *a;
368 char ch;
369
370 switch((ulong)c->qid.path){
371 case Qcons:
372 screenputs(va, n);
373 break;
374
375 case Qconsctl:
376 if(n >= sizeof(buf))
377 n = sizeof(buf)-1;
378 strncpy(buf, va, n);
379 buf[n] = 0;
380 for(a = buf; a;){
381 if(strncmp(a, "rawon", 5) == 0){
382 kbd.raw = 1;
383 /* clumsy hack - wake up reader */
384 ch = 0;
385 qwrite(kbdq, &ch, 1);
386 } else if(strncmp(a, "rawoff", 6) == 0){
387 kbd.raw = 0;
388 }
389 if(a = strchr(a, ' '))
390 a++;
391 }
392 break;
393
394 case Qsnarf:
395 t = c->aux;
396 /* always append only */
397 if(t->n > MAXSNARF) /* avoid thrashing when people cut huge text */
398 error("snarf buffer too big");
399 a = realloc(t->buf, t->n + n + 1);
400 if(a == nil)
401 error("snarf buffer too big");
402 t->buf = a;
403 memmove(t->buf+t->n, va, n);
404 t->n += n;
405 t->buf[t->n] = '\0';
406 break;
407 default:
408 print("conswrite: 0x%llux\n", c->qid.path);
409 error(Egreg);
410 }
411 return n;
412 }
413
414 Dev consdevtab = {
415 'c',
416 "cons",
417
418 devreset,
419 consinit,
420 consattach,
421 conswalk,
422 consstat,
423 consopen,
424 devcreate,
425 consclose,
426 consread,
427 devbread,
428 conswrite,
429 devbwrite,
430 devremove,
431 devwstat,
432 };
433