1 /* $NetBSD: tty_subr.c,v 1.31 2007/11/19 18:51:52 ad Exp $ */ 2 3 /* 4 * Copyright (c) 1993, 1994 Theo de Raadt 5 * All rights reserved. 6 * 7 * Per Lindqvist <pgd@compuram.bbt.se> supplied an almost fully working 8 * set of true clist functions that this is very loosely based on. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: tty_subr.c,v 1.31 2007/11/19 18:51:52 ad Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/buf.h> 37 #include <sys/ioctl.h> 38 #include <sys/tty.h> 39 #include <sys/malloc.h> 40 41 MALLOC_DEFINE(M_TTYS, "ttys", "allocated tty structures"); 42 43 /* 44 * At compile time, choose: 45 * There are two ways the TTY_QUOTE bit can be stored. If QBITS is 46 * defined we allocate an array of bits -- 1/8th as much memory but 47 * setbit(), clrbit(), and isset() take more CPU. If QBITS is 48 * undefined, we just use an array of bytes. 49 * 50 * If TTY_QUOTE functionality isn't required by a line discipline, 51 * it can free c_cq and set it to NULL. This speeds things up, 52 * and also does not use any extra memory. This is useful for (say) 53 * a SLIP line discipline that wants a 32K ring buffer for data 54 * but doesn't need quoting. 55 */ 56 #define QBITS 57 58 #ifdef QBITS 59 #define QMEM(n) ((((n)-1)/NBBY)+1) 60 #else 61 #define QMEM(n) (n) 62 #endif 63 64 #ifdef QBITS 65 static void clrbits(u_char *, int, int); 66 #endif 67 68 /* 69 * Initialize a particular clist. Ok, they are really ring buffers, 70 * of the specified length, with/without quoting support. 71 */ 72 int 73 clalloc(struct clist *clp, int size, int quot) 74 { 75 76 clp->c_cs = malloc(size, M_TTYS, M_WAITOK); 77 if (!clp->c_cs) 78 return (-1); 79 memset(clp->c_cs, 0, size); 80 81 if(quot) { 82 clp->c_cq = malloc(QMEM(size), M_TTYS, M_WAITOK); 83 if (!clp->c_cq) { 84 free(clp->c_cs, M_TTYS); 85 return (-1); 86 } 87 memset(clp->c_cq, 0, QMEM(size)); 88 } else 89 clp->c_cq = (u_char *)0; 90 91 clp->c_cf = clp->c_cl = (u_char *)0; 92 clp->c_ce = clp->c_cs + size; 93 clp->c_cn = size; 94 clp->c_cc = 0; 95 96 cv_init(&clp->c_cv, "tty"); 97 return (0); 98 } 99 100 void 101 clfree(struct clist *clp) 102 { 103 if(clp->c_cs) 104 free(clp->c_cs, M_TTYS); 105 if(clp->c_cq) 106 free(clp->c_cq, M_TTYS); 107 clp->c_cs = clp->c_cq = (u_char *)0; 108 cv_destroy(&clp->c_cv); 109 } 110 111 void 112 clwakeup(struct clist *clp) 113 { 114 115 cv_broadcast(&clp->c_cv); 116 } 117 118 /* 119 * Get a character from a clist. 120 */ 121 int 122 getc(struct clist *clp) 123 { 124 int c = -1; 125 int s; 126 127 s = spltty(); 128 if (clp->c_cc == 0) 129 goto out; 130 131 c = *clp->c_cf & 0xff; 132 if (clp->c_cq) { 133 #ifdef QBITS 134 if (isset(clp->c_cq, clp->c_cf - clp->c_cs) ) 135 c |= TTY_QUOTE; 136 #else 137 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 138 c |= TTY_QUOTE; 139 #endif 140 } 141 if (++clp->c_cf == clp->c_ce) 142 clp->c_cf = clp->c_cs; 143 if (--clp->c_cc == 0) 144 clp->c_cf = clp->c_cl = (u_char *)0; 145 out: 146 splx(s); 147 return c; 148 } 149 150 /* 151 * Copy clist to buffer. 152 * Return number of bytes moved. 153 */ 154 int 155 q_to_b(struct clist *clp, u_char *cp, int count) 156 { 157 int cc; 158 u_char *p = cp; 159 int s; 160 161 s = spltty(); 162 /* optimize this while loop */ 163 while (count > 0 && clp->c_cc > 0) { 164 cc = clp->c_cl - clp->c_cf; 165 if (clp->c_cf >= clp->c_cl) 166 cc = clp->c_ce - clp->c_cf; 167 if (cc > count) 168 cc = count; 169 memcpy(p, clp->c_cf, cc); 170 count -= cc; 171 p += cc; 172 clp->c_cc -= cc; 173 clp->c_cf += cc; 174 if (clp->c_cf == clp->c_ce) 175 clp->c_cf = clp->c_cs; 176 } 177 if (clp->c_cc == 0) 178 clp->c_cf = clp->c_cl = (u_char *)0; 179 splx(s); 180 return p - cp; 181 } 182 183 /* 184 * Return count of contiguous characters in clist. 185 * Stop counting if flag&character is non-null. 186 */ 187 int 188 ndqb(struct clist *clp, int flag) 189 { 190 int count = 0; 191 int i; 192 int cc; 193 int s; 194 195 s = spltty(); 196 if ((cc = clp->c_cc) == 0) 197 goto out; 198 199 if (flag == 0) { 200 count = clp->c_cl - clp->c_cf; 201 if (count <= 0) 202 count = clp->c_ce - clp->c_cf; 203 goto out; 204 } 205 206 i = clp->c_cf - clp->c_cs; 207 if (flag & TTY_QUOTE) { 208 while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) || 209 isset(clp->c_cq, i))) { 210 count++; 211 if (i == clp->c_cn) 212 break; 213 } 214 } else { 215 while (cc-- > 0 && !(clp->c_cs[i++] & flag)) { 216 count++; 217 if (i == clp->c_cn) 218 break; 219 } 220 } 221 out: 222 splx(s); 223 return count; 224 } 225 226 /* 227 * Flush count bytes from clist. 228 */ 229 void 230 ndflush(struct clist *clp, int count) 231 { 232 int cc; 233 int s; 234 235 s = spltty(); 236 if (count == clp->c_cc) { 237 clp->c_cc = 0; 238 clp->c_cf = clp->c_cl = (u_char *)0; 239 goto out; 240 } 241 /* optimize this while loop */ 242 while (count > 0 && clp->c_cc > 0) { 243 cc = clp->c_cl - clp->c_cf; 244 if (clp->c_cf >= clp->c_cl) 245 cc = clp->c_ce - clp->c_cf; 246 if (cc > count) 247 cc = count; 248 count -= cc; 249 clp->c_cc -= cc; 250 clp->c_cf += cc; 251 if (clp->c_cf == clp->c_ce) 252 clp->c_cf = clp->c_cs; 253 } 254 if (clp->c_cc == 0) 255 clp->c_cf = clp->c_cl = (u_char *)0; 256 out: 257 splx(s); 258 } 259 260 /* 261 * Put a character into the output queue. 262 */ 263 int 264 putc(int c, struct clist *clp) 265 { 266 int i; 267 int s; 268 269 s = spltty(); 270 if (clp->c_cc == clp->c_cn) 271 goto out; 272 273 if (clp->c_cc == 0) { 274 if (!clp->c_cs) { 275 #if defined(DIAGNOSTIC) || 1 276 printf("putc: required clalloc\n"); 277 #endif 278 if(clalloc(clp, 1024, 1)) { 279 out: 280 splx(s); 281 return -1; 282 } 283 } 284 clp->c_cf = clp->c_cl = clp->c_cs; 285 } 286 287 *clp->c_cl = c & 0xff; 288 i = clp->c_cl - clp->c_cs; 289 if (clp->c_cq) { 290 #ifdef QBITS 291 if (c & TTY_QUOTE) 292 setbit(clp->c_cq, i); 293 else 294 clrbit(clp->c_cq, i); 295 #else 296 q = clp->c_cq + i; 297 *q = (c & TTY_QUOTE) ? 1 : 0; 298 #endif 299 } 300 clp->c_cc++; 301 clp->c_cl++; 302 if (clp->c_cl == clp->c_ce) 303 clp->c_cl = clp->c_cs; 304 splx(s); 305 return 0; 306 } 307 308 #ifdef QBITS 309 /* 310 * optimized version of 311 * 312 * for (i = 0; i < len; i++) 313 * clrbit(cp, off + len); 314 */ 315 static void 316 clrbits(u_char *cp, int off, int len) 317 { 318 int sby, sbi, eby, ebi; 319 int i; 320 u_char mask; 321 322 if(len==1) { 323 clrbit(cp, off); 324 return; 325 } 326 327 sby = off / NBBY; 328 sbi = off % NBBY; 329 eby = (off+len) / NBBY; 330 ebi = (off+len) % NBBY; 331 if (sby == eby) { 332 mask = ((1 << (ebi - sbi)) - 1) << sbi; 333 cp[sby] &= ~mask; 334 } else { 335 mask = (1<<sbi) - 1; 336 cp[sby++] &= mask; 337 338 mask = (1<<ebi) - 1; 339 cp[eby] &= ~mask; 340 341 for (i = sby; i < eby; i++) 342 cp[i] = 0x00; 343 } 344 } 345 #endif 346 347 /* 348 * Copy buffer to clist. 349 * Return number of bytes not transfered. 350 */ 351 int 352 b_to_q(const u_char *cp, int count, struct clist *clp) 353 { 354 int cc; 355 const u_char *p = cp; 356 int s; 357 358 if (count <= 0) 359 return 0; 360 361 s = spltty(); 362 if (clp->c_cc == clp->c_cn) 363 goto out; 364 365 if (clp->c_cc == 0) { 366 if (!clp->c_cs) { 367 #if defined(DIAGNOSTIC) || 1 368 printf("b_to_q: required clalloc\n"); 369 #endif 370 if(clalloc(clp, 1024, 1)) 371 goto out; 372 } 373 clp->c_cf = clp->c_cl = clp->c_cs; 374 } 375 376 /* optimize this while loop */ 377 while (count > 0 && clp->c_cc < clp->c_cn) { 378 cc = clp->c_ce - clp->c_cl; 379 if (clp->c_cf > clp->c_cl) 380 cc = clp->c_cf - clp->c_cl; 381 if (cc > count) 382 cc = count; 383 memcpy(clp->c_cl, p, cc); 384 if (clp->c_cq) { 385 #ifdef QBITS 386 clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc); 387 #else 388 memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc); 389 #endif 390 } 391 p += cc; 392 count -= cc; 393 clp->c_cc += cc; 394 clp->c_cl += cc; 395 if (clp->c_cl == clp->c_ce) 396 clp->c_cl = clp->c_cs; 397 } 398 out: 399 splx(s); 400 return count; 401 } 402 403 static int cc; 404 405 /* 406 * Given a non-NULL pointer into the clist return the pointer 407 * to the next character in the list or return NULL if no more chars. 408 * 409 * Callers must not allow getc's to happen between firstc's and getc's 410 * so that the pointer becomes invalid. Note that interrupts are NOT 411 * masked. 412 */ 413 u_char * 414 nextc(struct clist *clp, u_char *cp, int *c) 415 { 416 417 if (clp->c_cf == cp) { 418 /* 419 * First time initialization. 420 */ 421 cc = clp->c_cc; 422 } 423 if (cc == 0 || cp == NULL) 424 return NULL; 425 if (--cc == 0) 426 return NULL; 427 if (++cp == clp->c_ce) 428 cp = clp->c_cs; 429 *c = *cp & 0xff; 430 if (clp->c_cq) { 431 #ifdef QBITS 432 if (isset(clp->c_cq, cp - clp->c_cs)) 433 *c |= TTY_QUOTE; 434 #else 435 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 436 *c |= TTY_QUOTE; 437 #endif 438 } 439 return cp; 440 } 441 442 /* 443 * Given a non-NULL pointer into the clist return the pointer 444 * to the first character in the list or return NULL if no more chars. 445 * 446 * Callers must not allow getc's to happen between firstc's and getc's 447 * so that the pointer becomes invalid. Note that interrupts are NOT 448 * masked. 449 * 450 * *c is set to the NEXT character 451 */ 452 u_char * 453 firstc(struct clist *clp, int *c) 454 { 455 u_char *cp; 456 457 cc = clp->c_cc; 458 if (cc == 0) 459 return NULL; 460 cp = clp->c_cf; 461 *c = *cp & 0xff; 462 if(clp->c_cq) { 463 #ifdef QBITS 464 if (isset(clp->c_cq, cp - clp->c_cs)) 465 *c |= TTY_QUOTE; 466 #else 467 if (*(cp - clp->c_cs + clp->c_cq)) 468 *c |= TTY_QUOTE; 469 #endif 470 } 471 return clp->c_cf; 472 } 473 474 /* 475 * Remove the last character in the clist and return it. 476 */ 477 int 478 unputc(struct clist *clp) 479 { 480 unsigned int c = -1; 481 int s; 482 483 s = spltty(); 484 if (clp->c_cc == 0) 485 goto out; 486 487 if (clp->c_cl == clp->c_cs) 488 clp->c_cl = clp->c_ce - 1; 489 else 490 --clp->c_cl; 491 clp->c_cc--; 492 493 c = *clp->c_cl & 0xff; 494 if (clp->c_cq) { 495 #ifdef QBITS 496 if (isset(clp->c_cq, clp->c_cl - clp->c_cs)) 497 c |= TTY_QUOTE; 498 #else 499 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 500 c |= TTY_QUOTE; 501 #endif 502 } 503 if (clp->c_cc == 0) 504 clp->c_cf = clp->c_cl = (u_char *)0; 505 out: 506 splx(s); 507 return c; 508 } 509 510 /* 511 * Put the chars in the from queue on the end of the to queue. 512 */ 513 void 514 catq(struct clist *from, struct clist *to) 515 { 516 int c; 517 518 while ((c = getc(from)) != -1) 519 putc(c, to); 520 } 521