1 /* $NetBSD: tty_subr.c,v 1.34 2008/07/16 18:27:49 drochner 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.34 2008/07/16 18:27:49 drochner 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 return (0); 97 } 98 99 void 100 clfree(struct clist *clp) 101 { 102 if(clp->c_cs) 103 free(clp->c_cs, M_TTYS); 104 if(clp->c_cq) 105 free(clp->c_cq, M_TTYS); 106 clp->c_cs = clp->c_cq = (u_char *)0; 107 } 108 109 /* 110 * Get a character from a clist. 111 */ 112 int 113 getc(struct clist *clp) 114 { 115 int c = -1; 116 int s; 117 118 s = spltty(); 119 if (clp->c_cc == 0) 120 goto out; 121 122 c = *clp->c_cf & 0xff; 123 if (clp->c_cq) { 124 #ifdef QBITS 125 if (isset(clp->c_cq, clp->c_cf - clp->c_cs) ) 126 c |= TTY_QUOTE; 127 #else 128 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 129 c |= TTY_QUOTE; 130 #endif 131 } 132 *clp->c_cf = 0; /* wipe out to avoid information disclosure */ 133 if (++clp->c_cf == clp->c_ce) 134 clp->c_cf = clp->c_cs; 135 if (--clp->c_cc == 0) 136 clp->c_cf = clp->c_cl = (u_char *)0; 137 out: 138 splx(s); 139 return c; 140 } 141 142 /* 143 * Copy clist to buffer. 144 * Return number of bytes moved. 145 */ 146 int 147 q_to_b(struct clist *clp, u_char *cp, int count) 148 { 149 int cc; 150 u_char *p = cp; 151 int s; 152 153 s = spltty(); 154 /* optimize this while loop */ 155 while (count > 0 && clp->c_cc > 0) { 156 cc = clp->c_cl - clp->c_cf; 157 if (clp->c_cf >= clp->c_cl) 158 cc = clp->c_ce - clp->c_cf; 159 if (cc > count) 160 cc = count; 161 memcpy(p, clp->c_cf, cc); 162 count -= cc; 163 p += cc; 164 clp->c_cc -= cc; 165 clp->c_cf += cc; 166 if (clp->c_cf == clp->c_ce) 167 clp->c_cf = clp->c_cs; 168 } 169 if (clp->c_cc == 0) 170 clp->c_cf = clp->c_cl = (u_char *)0; 171 splx(s); 172 return p - cp; 173 } 174 175 /* 176 * Return count of contiguous characters in clist. 177 * Stop counting if flag&character is non-null. 178 */ 179 int 180 ndqb(struct clist *clp, int flag) 181 { 182 int count = 0; 183 int i; 184 int cc; 185 int s; 186 187 s = spltty(); 188 if ((cc = clp->c_cc) == 0) 189 goto out; 190 191 if (flag == 0) { 192 count = clp->c_cl - clp->c_cf; 193 if (count <= 0) 194 count = clp->c_ce - clp->c_cf; 195 goto out; 196 } 197 198 i = clp->c_cf - clp->c_cs; 199 if (flag & TTY_QUOTE) { 200 while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) || 201 isset(clp->c_cq, i))) { 202 count++; 203 if (i == clp->c_cn) 204 break; 205 } 206 } else { 207 while (cc-- > 0 && !(clp->c_cs[i++] & flag)) { 208 count++; 209 if (i == clp->c_cn) 210 break; 211 } 212 } 213 out: 214 splx(s); 215 return count; 216 } 217 218 /* 219 * Flush count bytes from clist. 220 */ 221 void 222 ndflush(struct clist *clp, int count) 223 { 224 int cc; 225 int s; 226 227 s = spltty(); 228 if (count == clp->c_cc) { 229 clp->c_cc = 0; 230 clp->c_cf = clp->c_cl = (u_char *)0; 231 goto out; 232 } 233 /* optimize this while loop */ 234 while (count > 0 && clp->c_cc > 0) { 235 cc = clp->c_cl - clp->c_cf; 236 if (clp->c_cf >= clp->c_cl) 237 cc = clp->c_ce - clp->c_cf; 238 if (cc > count) 239 cc = count; 240 count -= cc; 241 clp->c_cc -= cc; 242 clp->c_cf += cc; 243 if (clp->c_cf == clp->c_ce) 244 clp->c_cf = clp->c_cs; 245 } 246 if (clp->c_cc == 0) 247 clp->c_cf = clp->c_cl = (u_char *)0; 248 out: 249 splx(s); 250 } 251 252 /* 253 * Put a character into the output queue. 254 */ 255 int 256 putc(int c, struct clist *clp) 257 { 258 int i; 259 int s; 260 261 s = spltty(); 262 if (clp->c_cc == clp->c_cn) 263 goto out; 264 265 if (clp->c_cc == 0) { 266 if (!clp->c_cs) { 267 #if defined(DIAGNOSTIC) || 1 268 printf("putc: required clalloc\n"); 269 #endif 270 if(clalloc(clp, 1024, 1)) { 271 out: 272 splx(s); 273 return -1; 274 } 275 } 276 clp->c_cf = clp->c_cl = clp->c_cs; 277 } 278 279 *clp->c_cl = c & 0xff; 280 i = clp->c_cl - clp->c_cs; 281 if (clp->c_cq) { 282 #ifdef QBITS 283 if (c & TTY_QUOTE) 284 setbit(clp->c_cq, i); 285 else 286 clrbit(clp->c_cq, i); 287 #else 288 q = clp->c_cq + i; 289 *q = (c & TTY_QUOTE) ? 1 : 0; 290 #endif 291 } 292 clp->c_cc++; 293 clp->c_cl++; 294 if (clp->c_cl == clp->c_ce) 295 clp->c_cl = clp->c_cs; 296 splx(s); 297 return 0; 298 } 299 300 #ifdef QBITS 301 /* 302 * optimized version of 303 * 304 * for (i = 0; i < len; i++) 305 * clrbit(cp, off + len); 306 */ 307 static void 308 clrbits(u_char *cp, int off, int len) 309 { 310 int sby, sbi, eby, ebi; 311 int i; 312 u_char mask; 313 314 if(len==1) { 315 clrbit(cp, off); 316 return; 317 } 318 319 sby = off / NBBY; 320 sbi = off % NBBY; 321 eby = (off+len) / NBBY; 322 ebi = (off+len) % NBBY; 323 if (sby == eby) { 324 mask = ((1 << (ebi - sbi)) - 1) << sbi; 325 cp[sby] &= ~mask; 326 } else { 327 mask = (1<<sbi) - 1; 328 cp[sby++] &= mask; 329 330 mask = (1<<ebi) - 1; 331 cp[eby] &= ~mask; 332 333 for (i = sby; i < eby; i++) 334 cp[i] = 0x00; 335 } 336 } 337 #endif 338 339 /* 340 * Copy buffer to clist. 341 * Return number of bytes not transfered. 342 */ 343 int 344 b_to_q(const u_char *cp, int count, struct clist *clp) 345 { 346 int cc; 347 const u_char *p = cp; 348 int s; 349 350 if (count <= 0) 351 return 0; 352 353 s = spltty(); 354 if (clp->c_cc == clp->c_cn) 355 goto out; 356 357 if (clp->c_cc == 0) { 358 if (!clp->c_cs) { 359 #if defined(DIAGNOSTIC) || 1 360 printf("b_to_q: required clalloc\n"); 361 #endif 362 if(clalloc(clp, 1024, 1)) 363 goto out; 364 } 365 clp->c_cf = clp->c_cl = clp->c_cs; 366 } 367 368 /* optimize this while loop */ 369 while (count > 0 && clp->c_cc < clp->c_cn) { 370 cc = clp->c_ce - clp->c_cl; 371 if (clp->c_cf > clp->c_cl) 372 cc = clp->c_cf - clp->c_cl; 373 if (cc > count) 374 cc = count; 375 memcpy(clp->c_cl, p, cc); 376 if (clp->c_cq) { 377 #ifdef QBITS 378 clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc); 379 #else 380 memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc); 381 #endif 382 } 383 p += cc; 384 count -= cc; 385 clp->c_cc += cc; 386 clp->c_cl += cc; 387 if (clp->c_cl == clp->c_ce) 388 clp->c_cl = clp->c_cs; 389 } 390 out: 391 splx(s); 392 return count; 393 } 394 395 static int cc; 396 397 /* 398 * Given a non-NULL pointer into the clist return the pointer 399 * to the next character in the list or return NULL if no more chars. 400 * 401 * Callers must not allow getc's to happen between firstc's and getc's 402 * so that the pointer becomes invalid. Note that interrupts are NOT 403 * masked. 404 */ 405 u_char * 406 nextc(struct clist *clp, u_char *cp, int *c) 407 { 408 409 if (clp->c_cf == cp) { 410 /* 411 * First time initialization. 412 */ 413 cc = clp->c_cc; 414 } 415 if (cc == 0 || cp == NULL) 416 return NULL; 417 if (--cc == 0) 418 return NULL; 419 if (++cp == clp->c_ce) 420 cp = clp->c_cs; 421 *c = *cp & 0xff; 422 if (clp->c_cq) { 423 #ifdef QBITS 424 if (isset(clp->c_cq, cp - clp->c_cs)) 425 *c |= TTY_QUOTE; 426 #else 427 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 428 *c |= TTY_QUOTE; 429 #endif 430 } 431 return cp; 432 } 433 434 /* 435 * Given a non-NULL pointer into the clist return the pointer 436 * to the first character in the list or return NULL if no more chars. 437 * 438 * Callers must not allow getc's to happen between firstc's and getc's 439 * so that the pointer becomes invalid. Note that interrupts are NOT 440 * masked. 441 * 442 * *c is set to the NEXT character 443 */ 444 u_char * 445 firstc(struct clist *clp, int *c) 446 { 447 u_char *cp; 448 449 cc = clp->c_cc; 450 if (cc == 0) 451 return NULL; 452 cp = clp->c_cf; 453 *c = *cp & 0xff; 454 if(clp->c_cq) { 455 #ifdef QBITS 456 if (isset(clp->c_cq, cp - clp->c_cs)) 457 *c |= TTY_QUOTE; 458 #else 459 if (*(cp - clp->c_cs + clp->c_cq)) 460 *c |= TTY_QUOTE; 461 #endif 462 } 463 return clp->c_cf; 464 } 465 466 /* 467 * Remove the last character in the clist and return it. 468 */ 469 int 470 unputc(struct clist *clp) 471 { 472 unsigned int c = -1; 473 int s; 474 475 s = spltty(); 476 if (clp->c_cc == 0) 477 goto out; 478 479 if (clp->c_cl == clp->c_cs) 480 clp->c_cl = clp->c_ce - 1; 481 else 482 --clp->c_cl; 483 clp->c_cc--; 484 485 c = *clp->c_cl & 0xff; 486 if (clp->c_cq) { 487 #ifdef QBITS 488 if (isset(clp->c_cq, clp->c_cl - clp->c_cs)) 489 c |= TTY_QUOTE; 490 #else 491 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 492 c |= TTY_QUOTE; 493 #endif 494 } 495 if (clp->c_cc == 0) 496 clp->c_cf = clp->c_cl = (u_char *)0; 497 out: 498 splx(s); 499 return c; 500 } 501 502 /* 503 * Put the chars in the from queue on the end of the to queue. 504 */ 505 void 506 catq(struct clist *from, struct clist *to) 507 { 508 int c; 509 510 while ((c = getc(from)) != -1) 511 putc(c, to); 512 } 513