1/* $OpenBSD: tty_subr.c,v 1.36 2022/08/14 01:58:28 jsg Exp $ */ 2/* $NetBSD: tty_subr.c,v 1.13 1996/02/09 19:00:43 christos Exp $ */ 3 4/* 5 * Copyright (c) 1993, 1994 Theo de Raadt 6 * All rights reserved. 7 * 8 * Per Lindqvist <pgd@compuram.bbt.se> supplied an almost fully working 9 * set of true clist functions that this is very loosely based on. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/tty.h> 35#include <sys/malloc.h> 36 37/* 38 * If TTY_QUOTE functionality isn't required by a line discipline, 39 * it can free c_cq and set it to NULL. This speeds things up, 40 * and also does not use any extra memory. This is useful for (say) 41 * a SLIP line discipline that wants a 32K ring buffer for data 42 * but doesn't need quoting. 43 */ 44#define QMEM(n) ((((n)-1)/NBBY)+1) 45 46void clrbits(u_char *, int, int); 47 48/* 49 * Initialize a particular clist. Ok, they are really ring buffers, 50 * of the specified length, with/without quoting support. 51 */ 52void 53clalloc(struct clist *clp, int size, int quot) 54{ 55 56 clp->c_cs = malloc(size, M_TTYS, M_WAITOK|M_ZERO); 57 58 if (quot) 59 clp->c_cq = malloc(QMEM(size), M_TTYS, M_WAITOK|M_ZERO); 60 else 61 clp->c_cq = NULL; 62 63 clp->c_cf = clp->c_cl = NULL; 64 clp->c_ce = clp->c_cs + size; 65 clp->c_cn = size; 66 clp->c_cc = 0; 67} 68 69void 70clfree(struct clist *clp) 71{ 72 if (clp->c_cs) { 73 explicit_bzero(clp->c_cs, clp->c_cn); 74 free(clp->c_cs, M_TTYS, clp->c_cn); 75 } 76 if (clp->c_cq) { 77 explicit_bzero(clp->c_cq, QMEM(clp->c_cn)); 78 free(clp->c_cq, M_TTYS, QMEM(clp->c_cn)); 79 } 80 clp->c_cs = clp->c_cq = NULL; 81} 82 83 84/* 85 * Get a character from a clist. 86 */ 87int 88getc(struct clist *clp) 89{ 90 int c = -1; 91 int s; 92 93 s = spltty(); 94 if (clp->c_cc == 0) 95 goto out; 96 97 c = *clp->c_cf & 0xff; 98 *clp->c_cf = 0; 99 if (clp->c_cq) { 100 if (isset(clp->c_cq, clp->c_cf - clp->c_cs)) 101 c |= TTY_QUOTE; 102 clrbit(clp->c_cq, clp->c_cf - clp->c_cs); 103 } 104 if (++clp->c_cf == clp->c_ce) 105 clp->c_cf = clp->c_cs; 106 if (--clp->c_cc == 0) 107 clp->c_cf = clp->c_cl = NULL; 108out: 109 splx(s); 110 return c; 111} 112 113/* 114 * Copy clist to buffer. 115 * Return number of bytes moved. 116 */ 117int 118q_to_b(struct clist *clp, u_char *cp, int count) 119{ 120 int cc; 121 u_char *p = cp; 122 int s; 123 124 s = spltty(); 125 /* optimize this while loop */ 126 while (count > 0 && clp->c_cc > 0) { 127 cc = clp->c_cl - clp->c_cf; 128 if (clp->c_cf >= clp->c_cl) 129 cc = clp->c_ce - clp->c_cf; 130 if (cc > count) 131 cc = count; 132 memcpy(p, clp->c_cf, cc); 133 memset(clp->c_cf, 0, cc); 134 if (clp->c_cq) 135 clrbits(clp->c_cq, clp->c_cf - clp->c_cs, cc); 136 count -= cc; 137 p += cc; 138 clp->c_cc -= cc; 139 clp->c_cf += cc; 140 if (clp->c_cf == clp->c_ce) 141 clp->c_cf = clp->c_cs; 142 } 143 if (clp->c_cc == 0) 144 clp->c_cf = clp->c_cl = NULL; 145 splx(s); 146 return p - cp; 147} 148 149/* 150 * Return count of contiguous characters in clist. 151 * Stop counting if flag&character is non-null. 152 */ 153int 154ndqb(struct clist *clp, int flag) 155{ 156 int count = 0; 157 int i; 158 int cc; 159 int s; 160 161 s = spltty(); 162 if ((cc = clp->c_cc) == 0) 163 goto out; 164 165 if (flag == 0) { 166 count = clp->c_cl - clp->c_cf; 167 if (count <= 0) 168 count = clp->c_ce - clp->c_cf; 169 goto out; 170 } 171 172 i = clp->c_cf - clp->c_cs; 173 if (flag & TTY_QUOTE) { 174 while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) || 175 isset(clp->c_cq, i))) { 176 count++; 177 if (i == clp->c_cn) 178 break; 179 } 180 } else { 181 while (cc-- > 0 && !(clp->c_cs[i++] & flag)) { 182 count++; 183 if (i == clp->c_cn) 184 break; 185 } 186 } 187out: 188 splx(s); 189 return count; 190} 191 192/* 193 * Flush count bytes from clist. 194 */ 195void 196ndflush(struct clist *clp, int count) 197{ 198 int cc; 199 int s; 200 201 s = spltty(); 202 if (count == clp->c_cc) { 203 clp->c_cc = 0; 204 clp->c_cf = clp->c_cl = NULL; 205 goto out; 206 } 207 /* optimize this while loop */ 208 while (count > 0 && clp->c_cc > 0) { 209 cc = clp->c_cl - clp->c_cf; 210 if (clp->c_cf >= clp->c_cl) 211 cc = clp->c_ce - clp->c_cf; 212 if (cc > count) 213 cc = count; 214 count -= cc; 215 clp->c_cc -= cc; 216 clp->c_cf += cc; 217 if (clp->c_cf == clp->c_ce) 218 clp->c_cf = clp->c_cs; 219 } 220 if (clp->c_cc == 0) 221 clp->c_cf = clp->c_cl = NULL; 222out: 223 splx(s); 224} 225 226/* 227 * Put a character into the output queue. 228 */ 229int 230putc(int c, struct clist *clp) 231{ 232 int i; 233 int s; 234 235 s = spltty(); 236 if (clp->c_cc == clp->c_cn) { 237 splx(s); 238 return -1; 239 } 240 241 if (clp->c_cc == 0) { 242 if (!clp->c_cs) 243 panic("%s: tty has no clist", __func__); 244 clp->c_cf = clp->c_cl = clp->c_cs; 245 } 246 247 *clp->c_cl = c & 0xff; 248 i = clp->c_cl - clp->c_cs; 249 if (clp->c_cq) { 250 if (c & TTY_QUOTE) 251 setbit(clp->c_cq, i); 252 else 253 clrbit(clp->c_cq, i); 254 } 255 clp->c_cc++; 256 clp->c_cl++; 257 if (clp->c_cl == clp->c_ce) 258 clp->c_cl = clp->c_cs; 259 splx(s); 260 return 0; 261} 262 263/* 264 * optimized version of 265 * 266 * for (i = 0; i < len; i++) 267 * clrbit(cp, off + i); 268 */ 269void 270clrbits(u_char *cp, int off, int len) 271{ 272 int sby, sbi, eby, ebi; 273 int i; 274 u_char mask; 275 276 if (len==1) { 277 clrbit(cp, off); 278 return; 279 } 280 281 sby = off / NBBY; 282 sbi = off % NBBY; 283 eby = (off+len) / NBBY; 284 ebi = (off+len) % NBBY; 285 if (sby == eby) { 286 mask = ((1 << (ebi - sbi)) - 1) << sbi; 287 cp[sby] &= ~mask; 288 } else { 289 mask = (1<<sbi) - 1; 290 cp[sby++] &= mask; 291 292 for (i = sby; i < eby; i++) 293 cp[i] = 0x00; 294 295 mask = (1<<ebi) - 1; 296 if (mask) /* if no mask, eby may be 1 too far */ 297 cp[eby] &= ~mask; 298 299 } 300} 301 302/* 303 * Copy buffer to clist. 304 * Return number of bytes not transferred. 305 */ 306int 307b_to_q(u_char *cp, int count, struct clist *clp) 308{ 309 int cc; 310 u_char *p = cp; 311 int s; 312 313 if (count <= 0) 314 return 0; 315 316 s = spltty(); 317 if (clp->c_cc == clp->c_cn) 318 goto out; 319 320 if (clp->c_cc == 0) { 321 if (!clp->c_cs) 322 panic("%s: tty has no clist", __func__); 323 clp->c_cf = clp->c_cl = clp->c_cs; 324 } 325 326 /* optimize this while loop */ 327 while (count > 0 && clp->c_cc < clp->c_cn) { 328 cc = clp->c_ce - clp->c_cl; 329 if (clp->c_cf > clp->c_cl) 330 cc = clp->c_cf - clp->c_cl; 331 if (cc > count) 332 cc = count; 333 memcpy(clp->c_cl, p, cc); 334 if (clp->c_cq) 335 clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc); 336 p += cc; 337 count -= cc; 338 clp->c_cc += cc; 339 clp->c_cl += cc; 340 if (clp->c_cl == clp->c_ce) 341 clp->c_cl = clp->c_cs; 342 } 343out: 344 splx(s); 345 return count; 346} 347 348/* 349 * Given a non-NULL pointer into the clist return the pointer 350 * to the next character in the list or return NULL if no more chars. 351 * 352 * Callers must not allow getc's to happen between firstc's and nextc's 353 * so that the pointer becomes invalid. Note that interrupts are NOT 354 * masked. 355 */ 356u_char * 357nextc(struct clist *clp, u_char *cp, int *c, int *ccp) 358{ 359 360 if (clp->c_cf == cp) { 361 /* 362 * First time initialization. 363 */ 364 *ccp = clp->c_cc; 365 } 366 if (*ccp == 0 || cp == NULL) 367 return NULL; 368 if (--(*ccp) == 0) 369 return NULL; 370 if (++cp == clp->c_ce) 371 cp = clp->c_cs; 372 *c = *cp & 0xff; 373 if (clp->c_cq) { 374 if (isset(clp->c_cq, cp - clp->c_cs)) 375 *c |= TTY_QUOTE; 376 } 377 return cp; 378} 379 380/* 381 * Given a non-NULL pointer into the clist return the pointer 382 * to the first character in the list or return NULL if no more chars. 383 * 384 * Callers must not allow getc's to happen between firstc's and nextc's 385 * so that the pointer becomes invalid. Note that interrupts are NOT 386 * masked. 387 * 388 * *c is set to the NEXT character 389 */ 390u_char * 391firstc(struct clist *clp, int *c, int *ccp) 392{ 393 u_char *cp; 394 395 *ccp = clp->c_cc; 396 if (*ccp == 0) 397 return NULL; 398 cp = clp->c_cf; 399 *c = *cp & 0xff; 400 if (clp->c_cq) { 401 if (isset(clp->c_cq, cp - clp->c_cs)) 402 *c |= TTY_QUOTE; 403 } 404 return clp->c_cf; 405} 406 407/* 408 * Remove the last character in the clist and return it. 409 */ 410int 411unputc(struct clist *clp) 412{ 413 unsigned int c = -1; 414 int s; 415 416 s = spltty(); 417 if (clp->c_cc == 0) 418 goto out; 419 420 if (clp->c_cl == clp->c_cs) 421 clp->c_cl = clp->c_ce - 1; 422 else 423 --clp->c_cl; 424 clp->c_cc--; 425 426 c = *clp->c_cl & 0xff; 427 *clp->c_cl = 0; 428 if (clp->c_cq) { 429 if (isset(clp->c_cq, clp->c_cl - clp->c_cs)) 430 c |= TTY_QUOTE; 431 clrbit(clp->c_cq, clp->c_cl - clp->c_cs); 432 } 433 if (clp->c_cc == 0) 434 clp->c_cf = clp->c_cl = NULL; 435out: 436 splx(s); 437 return c; 438} 439 440/* 441 * Put the chars in the from queue on the end of the to queue. 442 */ 443void 444catq(struct clist *from, struct clist *to) 445{ 446 int c; 447 int s; 448 449 s = spltty(); 450 if (from->c_cc == 0) { /* nothing to move */ 451 splx(s); 452 return; 453 } 454 455 /* 456 * if `to' queue is empty and the queues are the same max size, 457 * it is more efficient to just swap the clist structures. 458 */ 459 if (to->c_cc == 0 && from->c_cn == to->c_cn) { 460 struct clist tmp; 461 462 tmp = *from; 463 *from = *to; 464 *to = tmp; 465 splx(s); 466 return; 467 } 468 splx(s); 469 470 while ((c = getc(from)) != -1) 471 putc(c, to); 472} 473