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