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 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 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 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 * 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 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 158 kbdputc(int ch) 159 { 160 int n; 161 char buf[3]; 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 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 198 consinit(void) 199 { 200 kbdputcinit(); 201 } 202 203 static Chan* 204 consattach(char *spec) 205 { 206 return devattach('c', spec); 207 } 208 209 static Walkqid* 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 216 consstat(Chan *c, uchar *dp, int n) 217 { 218 return devstat(c, dp, n, consdir, nelem(consdir), devgen); 219 } 220 221 static Chan* 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 == OWRITE || c->mode == ORDWR) 234 c->aux = smalloc(sizeof(Snarf)); 235 break; 236 } 237 return c; 238 } 239 240 void 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 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 290 consread(Chan *c, void *buf, long n, vlong off) 291 { 292 char *cbuf; 293 int i, ch, eol; 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 if(kbd.raw){ 319 cbuf = buf; 320 if(qcanread(lineq)) 321 n = qread(lineq, buf, n); 322 else { 323 /* read as much as possible */ 324 do { 325 i = qread(kbdq, cbuf, n); 326 cbuf += i; 327 n -= i; 328 } while (n>0 && qcanread(kbdq)); 329 n = cbuf - (char*)buf; 330 } 331 } else { 332 while(!qcanread(lineq)){ 333 qread(kbdq, &kbd.line[kbd.x], 1); 334 ch = kbd.line[kbd.x]; 335 eol = 0; 336 switch(ch){ 337 case '\b': 338 if(kbd.x) 339 kbd.x--; 340 break; 341 case 0x15: 342 kbd.x = 0; 343 break; 344 case '\n': 345 case 0x04: 346 eol = 1; 347 default: 348 kbd.line[kbd.x++] = ch; 349 break; 350 } 351 if(kbd.x == sizeof(kbd.line) || eol){ 352 if(ch == 0x04) 353 kbd.x--; 354 qwrite(lineq, kbd.line, kbd.x); 355 kbd.x = 0; 356 } 357 } 358 n = qread(lineq, buf, n); 359 } 360 qunlock(&kbd); 361 poperror(); 362 return n; 363 364 default: 365 print("consread 0x%llux\n", c->qid.path); 366 error(Egreg); 367 } 368 return -1; /* never reached */ 369 } 370 371 static long 372 conswrite(Chan *c, void *va, long n, vlong) 373 { 374 Snarf *t; 375 char buf[256], *a; 376 377 switch((ulong)c->qid.path){ 378 case Qcons: 379 screenputs(va, n); 380 break; 381 382 case Qconsctl: 383 if(n >= sizeof(buf)) 384 n = sizeof(buf)-1; 385 strncpy(buf, va, n); 386 buf[n] = 0; 387 for(a = buf; a;){ 388 if(strncmp(a, "rawon", 5) == 0){ 389 qlock(&kbd); 390 if(kbd.x){ 391 qwrite(kbdq, kbd.line, kbd.x); 392 kbd.x = 0; 393 } 394 kbd.raw = 1; 395 qunlock(&kbd); 396 } else if(strncmp(a, "rawoff", 6) == 0){ 397 qlock(&kbd); 398 kbd.raw = 0; 399 kbd.x = 0; 400 qunlock(&kbd); 401 } 402 if(a = strchr(a, ' ')) 403 a++; 404 } 405 break; 406 407 case Qsnarf: 408 t = c->aux; 409 /* always append only */ 410 if(t->n > MAXSNARF) /* avoid thrashing when people cut huge text */ 411 error("snarf buffer too big"); 412 a = realloc(t->buf, t->n + n + 1); 413 if(a == nil) 414 error("snarf buffer too big"); 415 t->buf = a; 416 memmove(t->buf+t->n, va, n); 417 t->n += n; 418 t->buf[t->n] = '\0'; 419 break; 420 default: 421 print("conswrite: 0x%llux\n", c->qid.path); 422 error(Egreg); 423 } 424 return n; 425 } 426 427 Dev consdevtab = { 428 'c', 429 "cons", 430 431 devreset, 432 consinit, 433 consattach, 434 conswalk, 435 consstat, 436 consopen, 437 devcreate, 438 consclose, 439 consread, 440 devbread, 441 conswrite, 442 devbwrite, 443 devremove, 444 devwstat, 445 }; 446