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