1 /* $NetBSD: tty_subr.c,v 1.39 2009/11/14 13:18:41 dsl 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.39 2009/11/14 13:18:41 dsl 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/kmem.h> 40 41 /* 42 * At compile time, choose: 43 * There are two ways the TTY_QUOTE bit can be stored. If QBITS is 44 * defined we allocate an array of bits -- 1/8th as much memory but 45 * setbit(), clrbit(), and isset() take more CPU. If QBITS is 46 * undefined, we just use an array of bytes. 47 * 48 * If TTY_QUOTE functionality isn't required by a line discipline, 49 * it can free c_cq and set it to NULL. This speeds things up, 50 * and also does not use any extra memory. This is useful for (say) 51 * a SLIP line discipline that wants a 32K ring buffer for data 52 * but doesn't need quoting. 53 */ 54 #define QBITS 55 56 #ifdef QBITS 57 #define QMEM(n) ((((n)-1)/NBBY)+1) 58 #else 59 #define QMEM(n) (n) 60 #endif 61 62 #ifdef QBITS 63 static void clrbits(u_char *, unsigned int, unsigned int); 64 #endif 65 66 /* 67 * Initialize a particular clist. Ok, they are really ring buffers, 68 * of the specified length, with/without quoting support. 69 */ 70 int 71 clalloc(struct clist *clp, int size, int quot) 72 { 73 74 clp->c_cs = kmem_zalloc(size, KM_SLEEP); 75 if (!clp->c_cs) 76 return (-1); 77 78 if (quot) { 79 clp->c_cq = kmem_zalloc(QMEM(size), KM_SLEEP); 80 if (!clp->c_cq) { 81 kmem_free(clp->c_cs, size); 82 return (-1); 83 } 84 } else 85 clp->c_cq = NULL; 86 87 clp->c_cf = clp->c_cl = NULL; 88 clp->c_ce = clp->c_cs + size; 89 clp->c_cn = size; 90 clp->c_cc = 0; 91 92 return (0); 93 } 94 95 void 96 clfree(struct clist *clp) 97 { 98 if (clp->c_cs) 99 kmem_free(clp->c_cs, clp->c_cn); 100 if (clp->c_cq) 101 kmem_free(clp->c_cq, QMEM(clp->c_cn)); 102 clp->c_cs = clp->c_cq = NULL; 103 } 104 105 /* 106 * Get a character from a clist. 107 */ 108 int 109 getc(struct clist *clp) 110 { 111 int c = -1; 112 int s; 113 114 s = spltty(); 115 if (clp->c_cc == 0) 116 goto out; 117 118 c = *clp->c_cf & 0xff; 119 if (clp->c_cq) { 120 #ifdef QBITS 121 if (isset(clp->c_cq, clp->c_cf - clp->c_cs) ) 122 c |= TTY_QUOTE; 123 #else 124 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 125 c |= TTY_QUOTE; 126 #endif 127 } 128 *clp->c_cf = 0; /* wipe out to avoid information disclosure */ 129 if (++clp->c_cf == clp->c_ce) 130 clp->c_cf = clp->c_cs; 131 if (--clp->c_cc == 0) 132 clp->c_cf = clp->c_cl = (u_char *)0; 133 out: 134 splx(s); 135 return c; 136 } 137 138 /* 139 * Copy clist to buffer. 140 * Return number of bytes moved. 141 */ 142 int 143 q_to_b(struct clist *clp, u_char *cp, int count) 144 { 145 int cc; 146 u_char *p = cp; 147 int s; 148 149 s = spltty(); 150 /* optimize this while loop */ 151 while (count > 0 && clp->c_cc > 0) { 152 cc = clp->c_cl - clp->c_cf; 153 if (clp->c_cf >= clp->c_cl) 154 cc = clp->c_ce - clp->c_cf; 155 if (cc > count) 156 cc = count; 157 memcpy(p, clp->c_cf, cc); 158 count -= cc; 159 p += cc; 160 clp->c_cc -= cc; 161 clp->c_cf += cc; 162 if (clp->c_cf == clp->c_ce) 163 clp->c_cf = clp->c_cs; 164 } 165 if (clp->c_cc == 0) 166 clp->c_cf = clp->c_cl = (u_char *)0; 167 splx(s); 168 return p - cp; 169 } 170 171 /* 172 * Return count of contiguous characters in clist. 173 * Stop counting if flag&character is non-null. 174 */ 175 int 176 ndqb(struct clist *clp, int flag) 177 { 178 int count = 0; 179 int i; 180 int cc; 181 int s; 182 183 s = spltty(); 184 if ((cc = clp->c_cc) == 0) 185 goto out; 186 187 if (flag == 0) { 188 count = clp->c_cl - clp->c_cf; 189 if (count <= 0) 190 count = clp->c_ce - clp->c_cf; 191 goto out; 192 } 193 194 i = clp->c_cf - clp->c_cs; 195 if (flag & TTY_QUOTE) { 196 while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) || 197 isset(clp->c_cq, i))) { 198 count++; 199 if (i == clp->c_cn) 200 break; 201 } 202 } else { 203 while (cc-- > 0 && !(clp->c_cs[i++] & flag)) { 204 count++; 205 if (i == clp->c_cn) 206 break; 207 } 208 } 209 out: 210 splx(s); 211 return count; 212 } 213 214 /* 215 * Flush count bytes from clist. 216 */ 217 void 218 ndflush(struct clist *clp, int count) 219 { 220 int cc; 221 int s; 222 223 s = spltty(); 224 if (count == clp->c_cc) { 225 clp->c_cc = 0; 226 clp->c_cf = clp->c_cl = (u_char *)0; 227 goto out; 228 } 229 /* optimize this while loop */ 230 while (count > 0 && clp->c_cc > 0) { 231 cc = clp->c_cl - clp->c_cf; 232 if (clp->c_cf >= clp->c_cl) 233 cc = clp->c_ce - clp->c_cf; 234 if (cc > count) 235 cc = count; 236 count -= cc; 237 clp->c_cc -= cc; 238 clp->c_cf += cc; 239 if (clp->c_cf == clp->c_ce) 240 clp->c_cf = clp->c_cs; 241 } 242 if (clp->c_cc == 0) 243 clp->c_cf = clp->c_cl = (u_char *)0; 244 out: 245 splx(s); 246 } 247 248 /* 249 * Put a character into the output queue. 250 */ 251 int 252 putc(int c, struct clist *clp) 253 { 254 int i; 255 int s; 256 257 s = spltty(); 258 if (clp->c_cc == clp->c_cn) 259 goto out; 260 261 if (clp->c_cc == 0) { 262 if (!clp->c_cs) { 263 #if defined(DIAGNOSTIC) || 1 264 printf("putc: required clalloc\n"); 265 #endif 266 if (clalloc(clp, 1024, 1)) { 267 out: 268 splx(s); 269 return -1; 270 } 271 } 272 clp->c_cf = clp->c_cl = clp->c_cs; 273 } 274 275 *clp->c_cl = c & 0xff; 276 i = clp->c_cl - clp->c_cs; 277 if (clp->c_cq) { 278 #ifdef QBITS 279 if (c & TTY_QUOTE) 280 setbit(clp->c_cq, i); 281 else 282 clrbit(clp->c_cq, i); 283 #else 284 q = clp->c_cq + i; 285 *q = (c & TTY_QUOTE) ? 1 : 0; 286 #endif 287 } 288 clp->c_cc++; 289 clp->c_cl++; 290 if (clp->c_cl == clp->c_ce) 291 clp->c_cl = clp->c_cs; 292 splx(s); 293 return 0; 294 } 295 296 #ifdef QBITS 297 /* 298 * optimized version of 299 * 300 * for (i = 0; i < len; i++) 301 * clrbit(cp, off + len); 302 */ 303 static void 304 clrbits(u_char *cp, unsigned int off, unsigned int len) 305 { 306 unsigned int sbi, ebi; 307 u_char *scp, *ecp; 308 unsigned int end; 309 unsigned char mask; 310 311 scp = cp + off / NBBY; 312 sbi = off % NBBY; 313 end = off + len + NBBY - 1; 314 ecp = cp + end / NBBY - 1; 315 ebi = end % NBBY + 1; 316 if (scp >= ecp) { 317 mask = ((1 << len) - 1) << sbi; 318 *scp &= ~mask; 319 } else { 320 mask = (1 << sbi) - 1; 321 *scp++ &= mask; 322 323 mask = (1 << ebi) - 1; 324 *ecp &= ~mask; 325 326 while (scp < ecp) 327 *scp++ = 0x00; 328 } 329 } 330 #endif 331 332 /* 333 * Copy buffer to clist. 334 * Return number of bytes not transfered. 335 */ 336 int 337 b_to_q(const u_char *cp, int count, struct clist *clp) 338 { 339 int cc; 340 const u_char *p = cp; 341 int s; 342 343 if (count <= 0) 344 return 0; 345 346 s = spltty(); 347 if (clp->c_cc == clp->c_cn) 348 goto out; 349 350 if (clp->c_cc == 0) { 351 if (!clp->c_cs) { 352 #if defined(DIAGNOSTIC) || 1 353 printf("b_to_q: required clalloc\n"); 354 #endif 355 if (clalloc(clp, 1024, 1)) 356 goto out; 357 } 358 clp->c_cf = clp->c_cl = clp->c_cs; 359 } 360 361 /* optimize this while loop */ 362 while (count > 0 && clp->c_cc < clp->c_cn) { 363 cc = clp->c_ce - clp->c_cl; 364 if (clp->c_cf > clp->c_cl) 365 cc = clp->c_cf - clp->c_cl; 366 if (cc > count) 367 cc = count; 368 memcpy(clp->c_cl, p, cc); 369 if (clp->c_cq) { 370 #ifdef QBITS 371 clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc); 372 #else 373 memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc); 374 #endif 375 } 376 p += cc; 377 count -= cc; 378 clp->c_cc += cc; 379 clp->c_cl += cc; 380 if (clp->c_cl == clp->c_ce) 381 clp->c_cl = clp->c_cs; 382 } 383 out: 384 splx(s); 385 return count; 386 } 387 388 static int cc; 389 390 /* 391 * Given a non-NULL pointer into the clist return the pointer 392 * to the next character in the list or return NULL if no more chars. 393 * 394 * Callers must not allow getc's to happen between firstc's and getc's 395 * so that the pointer becomes invalid. Note that interrupts are NOT 396 * masked. 397 */ 398 u_char * 399 nextc(struct clist *clp, u_char *cp, int *c) 400 { 401 402 if (clp->c_cf == cp) { 403 /* 404 * First time initialization. 405 */ 406 cc = clp->c_cc; 407 } 408 if (cc == 0 || cp == NULL) 409 return NULL; 410 if (--cc == 0) 411 return NULL; 412 if (++cp == clp->c_ce) 413 cp = clp->c_cs; 414 *c = *cp & 0xff; 415 if (clp->c_cq) { 416 #ifdef QBITS 417 if (isset(clp->c_cq, cp - clp->c_cs)) 418 *c |= TTY_QUOTE; 419 #else 420 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 421 *c |= TTY_QUOTE; 422 #endif 423 } 424 return cp; 425 } 426 427 /* 428 * Given a non-NULL pointer into the clist return the pointer 429 * to the first character in the list or return NULL if no more chars. 430 * 431 * Callers must not allow getc's to happen between firstc's and getc's 432 * so that the pointer becomes invalid. Note that interrupts are NOT 433 * masked. 434 * 435 * *c is set to the NEXT character 436 */ 437 u_char * 438 firstc(struct clist *clp, int *c) 439 { 440 u_char *cp; 441 442 cc = clp->c_cc; 443 if (cc == 0) 444 return NULL; 445 cp = clp->c_cf; 446 *c = *cp & 0xff; 447 if (clp->c_cq) { 448 #ifdef QBITS 449 if (isset(clp->c_cq, cp - clp->c_cs)) 450 *c |= TTY_QUOTE; 451 #else 452 if (*(cp - clp->c_cs + clp->c_cq)) 453 *c |= TTY_QUOTE; 454 #endif 455 } 456 return clp->c_cf; 457 } 458 459 /* 460 * Remove the last character in the clist and return it. 461 */ 462 int 463 unputc(struct clist *clp) 464 { 465 unsigned int c = -1; 466 int s; 467 468 s = spltty(); 469 if (clp->c_cc == 0) 470 goto out; 471 472 if (clp->c_cl == clp->c_cs) 473 clp->c_cl = clp->c_ce - 1; 474 else 475 --clp->c_cl; 476 clp->c_cc--; 477 478 c = *clp->c_cl & 0xff; 479 if (clp->c_cq) { 480 #ifdef QBITS 481 if (isset(clp->c_cq, clp->c_cl - clp->c_cs)) 482 c |= TTY_QUOTE; 483 #else 484 if (*(clp->c_cf - clp->c_cs + clp->c_cq)) 485 c |= TTY_QUOTE; 486 #endif 487 } 488 if (clp->c_cc == 0) 489 clp->c_cf = clp->c_cl = (u_char *)0; 490 out: 491 splx(s); 492 return c; 493 } 494 495 /* 496 * Put the chars in the from queue on the end of the to queue. 497 */ 498 void 499 catq(struct clist *from, struct clist *to) 500 { 501 int c; 502 503 while ((c = getc(from)) != -1) 504 putc(c, to); 505 } 506