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