Annotation of src/usr.bin/aucat/dev.c, Revision 1.35
1.35 ! ratchov 1: /* $OpenBSD: dev.c,v 1.34 2009/10/10 12:43:09 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
1.27 ratchov 17:
1.1 ratchov 18: #include <stdio.h>
19: #include <stdlib.h>
20: #include <unistd.h>
21:
22: #include "abuf.h"
23: #include "aproc.h"
1.27 ratchov 24: #include "conf.h"
25: #include "dev.h"
1.3 ratchov 26: #include "pipe.h"
27: #include "safile.h"
1.28 ratchov 28: #include "midi.h"
1.1 ratchov 29:
1.3 ratchov 30: unsigned dev_bufsz, dev_round, dev_rate;
1.1 ratchov 31: struct aparams dev_ipar, dev_opar;
32: struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
1.28 ratchov 33: struct aproc *dev_midi;
34:
35: /*
36: * Create a MIDI thru box as the MIDI end of the device
37: */
38: void
39: dev_thruinit(void)
40: {
41: dev_midi = thru_new("thru");
42: dev_midi->refs++;
43: }
44:
45: /*
46: * Attach a bi-directional MIDI stream to the MIDI device
47: */
48: void
49: dev_midiattach(struct abuf *ibuf, struct abuf *obuf)
50: {
51: if (ibuf)
52: aproc_setin(dev_midi, ibuf);
53: if (obuf) {
54: aproc_setout(dev_midi, obuf);
55: if (ibuf) {
56: ibuf->duplex = obuf;
57: obuf->duplex = ibuf;
58: }
59: }
60: }
1.24 ratchov 61:
62: /*
1.27 ratchov 63: * Same as dev_init(), but create a fake device that records what is
64: * played.
1.24 ratchov 65: */
66: void
67: dev_loopinit(struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
68: {
69: struct abuf *buf;
70: struct aparams par;
71: unsigned cmin, cmax, rate;
72:
73: cmin = (dipar->cmin < dopar->cmin) ? dipar->cmin : dopar->cmin;
74: cmax = (dipar->cmax > dopar->cmax) ? dipar->cmax : dopar->cmax;
75: rate = (dipar->rate > dopar->rate) ? dipar->rate : dopar->rate;
76: aparams_init(&par, cmin, cmax, rate);
77: dev_ipar = par;
78: dev_opar = par;
79: dev_round = (bufsz + 1) / 2;
80: dev_bufsz = dev_round * 2;
1.25 ratchov 81: dev_rate = rate;
1.24 ratchov 82: dev_rec = NULL;
83: dev_play = NULL;
1.25 ratchov 84:
1.24 ratchov 85: buf = abuf_new(dev_bufsz, &par);
86: dev_mix = mix_new("mix", dev_bufsz);
87: dev_mix->refs++;
88: dev_sub = sub_new("sub", dev_bufsz);
89: dev_sub->refs++;
90: aproc_setout(dev_mix, buf);
91: aproc_setin(dev_sub, buf);
92:
93: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
94: dev_sub->u.sub.flags |= SUB_AUTOQUIT;
95:
96: *dipar = dev_ipar;
97: *dopar = dev_opar;
98: }
1.1 ratchov 99:
1.20 ratchov 100: unsigned
101: dev_roundof(unsigned newrate)
1.1 ratchov 102: {
1.20 ratchov 103: return (dev_round * newrate + dev_rate / 2) / dev_rate;
1.1 ratchov 104: }
105:
106: /*
1.27 ratchov 107: * Open the device with the given hardware parameters and create a mixer
1.1 ratchov 108: * and a multiplexer connected to it with all necessary conversions
1.27 ratchov 109: * setup.
1.1 ratchov 110: */
1.26 ratchov 111: int
1.3 ratchov 112: dev_init(char *devpath,
1.20 ratchov 113: struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
1.1 ratchov 114: {
1.23 ratchov 115: struct file *f;
1.1 ratchov 116: struct aparams ipar, opar;
117: struct aproc *conv;
118: struct abuf *buf;
1.3 ratchov 119: unsigned nfr, ibufsz, obufsz;
1.25 ratchov 120:
1.3 ratchov 121: /*
1.27 ratchov 122: * Ask for 1/4 of the buffer for the kernel ring and
123: * limit the block size to 1/4 of the requested buffer.
1.3 ratchov 124: */
125: dev_bufsz = (bufsz + 3) / 4;
1.11 ratchov 126: dev_round = (bufsz + 3) / 4;
1.23 ratchov 127: f = (struct file *)safile_new(&safile_ops, devpath,
1.20 ratchov 128: dipar, dopar, &dev_bufsz, &dev_round);
1.23 ratchov 129: if (f == NULL)
1.26 ratchov 130: return 0;
1.3 ratchov 131: if (dipar) {
1.20 ratchov 132: dev_rate = dipar->rate;
1.3 ratchov 133: }
134: if (dopar) {
1.20 ratchov 135: dev_rate = dopar->rate;
1.3 ratchov 136: }
1.11 ratchov 137: ibufsz = obufsz = dev_bufsz;
138: bufsz = (bufsz > dev_bufsz) ? bufsz - dev_bufsz : 0;
1.1 ratchov 139:
140: /*
1.27 ratchov 141: * Use 1/8 of the buffer for the mixer/converters. Since we
1.11 ratchov 142: * already consumed 1/4 for the device, bufsz represents the
1.27 ratchov 143: * remaining 3/4. So 1/8 is 1/6 of 3/4.
1.11 ratchov 144: */
145: nfr = (bufsz + 5) / 6;
146: nfr += dev_round - 1;
147: nfr -= nfr % dev_round;
148: if (nfr == 0)
149: nfr = dev_round;
150:
151: /*
1.27 ratchov 152: * Create record chain.
1.1 ratchov 153: */
154: if (dipar) {
155: aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
156: /*
1.27 ratchov 157: * Create the read end.
1.1 ratchov 158: */
1.23 ratchov 159: dev_rec = rpipe_new(f);
1.22 ratchov 160: dev_rec->refs++;
1.4 ratchov 161: buf = abuf_new(nfr, dipar);
1.1 ratchov 162: aproc_setout(dev_rec, buf);
1.3 ratchov 163: ibufsz += nfr;
1.1 ratchov 164:
165: /*
1.27 ratchov 166: * Append a converter, if needed.
1.1 ratchov 167: */
1.4 ratchov 168: if (!aparams_eqenc(dipar, &ipar)) {
1.30 ratchov 169: conv = dec_new("rec", dipar);
1.1 ratchov 170: aproc_setin(conv, buf);
1.4 ratchov 171: buf = abuf_new(nfr, &ipar);
1.1 ratchov 172: aproc_setout(conv, buf);
1.3 ratchov 173: ibufsz += nfr;
1.1 ratchov 174: }
175: dev_ipar = ipar;
176:
177: /*
1.27 ratchov 178: * Append a "sub" to which clients will connect.
1.1 ratchov 179: */
1.30 ratchov 180: dev_sub = sub_new("rec", nfr);
1.22 ratchov 181: dev_sub->refs++;
1.1 ratchov 182: aproc_setin(dev_sub, buf);
183: } else {
184: dev_rec = NULL;
185: dev_sub = NULL;
186: }
187:
188: /*
1.27 ratchov 189: * Create play chain.
1.1 ratchov 190: */
191: if (dopar) {
192: aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
193: /*
1.27 ratchov 194: * Create the write end.
1.1 ratchov 195: */
1.23 ratchov 196: dev_play = wpipe_new(f);
1.22 ratchov 197: dev_play->refs++;
1.4 ratchov 198: buf = abuf_new(nfr, dopar);
1.1 ratchov 199: aproc_setin(dev_play, buf);
1.3 ratchov 200: obufsz += nfr;
1.25 ratchov 201:
1.1 ratchov 202: /*
1.27 ratchov 203: * Append a converter, if needed.
1.1 ratchov 204: */
1.4 ratchov 205: if (!aparams_eqenc(&opar, dopar)) {
1.30 ratchov 206: conv = enc_new("play", dopar);
1.1 ratchov 207: aproc_setout(conv, buf);
1.4 ratchov 208: buf = abuf_new(nfr, &opar);
1.1 ratchov 209: aproc_setin(conv, buf);
1.3 ratchov 210: obufsz += nfr;
1.1 ratchov 211: }
212: dev_opar = opar;
213:
214: /*
1.27 ratchov 215: * Append a "mix" to which clients will connect.
1.1 ratchov 216: */
1.30 ratchov 217: dev_mix = mix_new("play", nfr);
1.22 ratchov 218: dev_mix->refs++;
1.1 ratchov 219: aproc_setout(dev_mix, buf);
220: } else {
221: dev_play = NULL;
222: dev_mix = NULL;
223: }
1.3 ratchov 224: dev_bufsz = (dopar) ? obufsz : ibufsz;
1.29 ratchov 225: dev_midi = ctl_new("ctl");
226: dev_midi->refs++;
1.3 ratchov 227: dev_start();
1.26 ratchov 228: return 1;
1.1 ratchov 229: }
230:
231: /*
1.27 ratchov 232: * Cleanly stop and drain everything and close the device
233: * once both play chain and record chain are gone.
1.1 ratchov 234: */
235: void
236: dev_done(void)
237: {
238: struct file *f;
239:
1.32 ratchov 240: if (dev_midi) {
1.35 ! ratchov 241: if (!dev_sub && !dev_mix)
! 242: dev_midi->u.thru.flags |= THRU_AUTOQUIT;
1.34 ratchov 243: restart_midi:
244: LIST_FOREACH(f, &file_list, entry) {
245: if (f->rproc &&
246: aproc_depend(dev_midi, f->rproc)) {
247: file_eof(f);
248: goto restart_midi;
249: }
250: }
1.32 ratchov 251: }
1.3 ratchov 252: if (dev_mix) {
253: /*
1.32 ratchov 254: * Put the mixer in ``autoquit'' state and generate
255: * EOF on all inputs connected it. Once buffers are
256: * drained the mixer will terminate and shutdown the
257: * device.
1.3 ratchov 258: *
259: * NOTE: since file_eof() can destroy the file and
260: * reorder the file_list, we have to restart the loop
1.27 ratchov 261: * after each call to file_eof().
1.3 ratchov 262: */
1.32 ratchov 263: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
264: restart_mix:
1.3 ratchov 265: LIST_FOREACH(f, &file_list, entry) {
1.32 ratchov 266: if (f->rproc != NULL &&
267: aproc_depend(dev_mix, f->rproc)) {
1.3 ratchov 268: file_eof(f);
1.32 ratchov 269: goto restart_mix;
1.3 ratchov 270: }
271: }
1.32 ratchov 272: } else if (dev_sub) {
1.3 ratchov 273: /*
1.32 ratchov 274: * Same as above, but since there's no mixer,
275: * we generate EOF on the record-end of the
276: * device.
277: */
278: restart_sub:
279: LIST_FOREACH(f, &file_list, entry) {
280: if (f->rproc != NULL &&
281: aproc_depend(dev_sub, f->rproc)) {
282: file_eof(f);
283: goto restart_sub;
1.23 ratchov 284: }
1.32 ratchov 285: }
286: }
287: if (dev_mix) {
288: dev_mix->refs--;
289: if (dev_mix->zomb)
290: aproc_del(dev_mix);
291: dev_mix = NULL;
292: }
293: if (dev_play) {
294: dev_play->refs--;
295: if (dev_play->zomb)
1.23 ratchov 296: aproc_del(dev_play);
1.32 ratchov 297: dev_play = NULL;
1.1 ratchov 298: }
1.3 ratchov 299: if (dev_sub) {
1.22 ratchov 300: dev_sub->refs--;
1.32 ratchov 301: if (dev_sub->zomb)
302: aproc_del(dev_sub);
1.22 ratchov 303: dev_sub = NULL;
1.32 ratchov 304: }
305: if (dev_rec) {
306: dev_rec->refs--;
307: if (dev_rec->zomb)
1.23 ratchov 308: aproc_del(dev_rec);
1.32 ratchov 309: dev_rec = NULL;
1.34 ratchov 310: }
311: if (dev_midi) {
312: dev_midi->refs--;
313: if (dev_midi->zomb)
314: aproc_del(dev_midi);
315: dev_midi = NULL;
1.32 ratchov 316: }
317: for (;;) {
318: if (!file_poll())
319: break;
1.1 ratchov 320: }
321: }
322:
323: /*
1.27 ratchov 324: * Start the (paused) device. By default it's paused.
1.1 ratchov 325: */
326: void
327: dev_start(void)
328: {
1.22 ratchov 329: struct file *f;
330:
1.1 ratchov 331: if (dev_mix)
332: dev_mix->u.mix.flags |= MIX_DROP;
333: if (dev_sub)
334: dev_sub->u.sub.flags |= SUB_DROP;
1.22 ratchov 335: if (dev_play && dev_play->u.io.file) {
336: f = dev_play->u.io.file;
337: f->ops->start(f);
338: } else if (dev_rec && dev_rec->u.io.file) {
339: f = dev_rec->u.io.file;
340: f->ops->start(f);
341: }
1.1 ratchov 342: }
343:
344: /*
1.27 ratchov 345: * Pause the device.
1.1 ratchov 346: */
347: void
348: dev_stop(void)
349: {
1.22 ratchov 350: struct file *f;
351:
352: if (dev_play && dev_play->u.io.file) {
353: f = dev_play->u.io.file;
354: f->ops->stop(f);
355: } else if (dev_rec && dev_rec->u.io.file) {
356: f = dev_rec->u.io.file;
357: f->ops->stop(f);
358: }
1.1 ratchov 359: if (dev_mix)
360: dev_mix->u.mix.flags &= ~MIX_DROP;
361: if (dev_sub)
362: dev_sub->u.sub.flags &= ~SUB_DROP;
363: }
364:
365: /*
1.27 ratchov 366: * Find the end points connected to the mix/sub.
1.14 ratchov 367: */
368: int
369: dev_getep(struct abuf **sibuf, struct abuf **sobuf)
370: {
371: struct abuf *ibuf, *obuf;
372:
373: if (sibuf) {
374: ibuf = *sibuf;
375: for (;;) {
376: if (!ibuf || !ibuf->rproc) {
377: return 0;
378: }
379: if (ibuf->rproc == dev_mix)
380: break;
381: ibuf = LIST_FIRST(&ibuf->rproc->obuflist);
382: }
1.21 ratchov 383: *sibuf = ibuf;
1.14 ratchov 384: }
385: if (sobuf) {
386: obuf = *sobuf;
387: for (;;) {
388: if (!obuf || !obuf->wproc) {
389: return 0;
390: }
391: if (obuf->wproc == dev_sub)
392: break;
393: obuf = LIST_FIRST(&obuf->wproc->ibuflist);
394: }
1.21 ratchov 395: *sobuf = obuf;
1.14 ratchov 396: }
397: return 1;
398: }
399:
400: /*
1.27 ratchov 401: * Sync play buffer to rec buffer (for instance when one of
402: * them underruns/overruns).
1.1 ratchov 403: */
404: void
1.3 ratchov 405: dev_sync(struct abuf *ibuf, struct abuf *obuf)
1.1 ratchov 406: {
1.3 ratchov 407: struct abuf *pbuf, *rbuf;
408: int delta;
409:
410: if (!dev_mix || !dev_sub)
411: return;
412: pbuf = LIST_FIRST(&dev_mix->obuflist);
413: if (!pbuf)
414: return;
415: rbuf = LIST_FIRST(&dev_sub->ibuflist);
416: if (!rbuf)
417: return;
1.14 ratchov 418: if (!dev_getep(&ibuf, &obuf))
419: return;
1.3 ratchov 420:
421: /*
1.27 ratchov 422: * Calculate delta, the number of frames the play chain is ahead
1.3 ratchov 423: * of the record chain. It's necessary to schedule silences (or
424: * drops) in order to start playback and record in sync.
425: */
1.25 ratchov 426: delta =
427: rbuf->bpf * (pbuf->abspos + pbuf->used) -
1.3 ratchov 428: pbuf->bpf * rbuf->abspos;
429: delta /= pbuf->bpf * rbuf->bpf;
430: if (delta > 0) {
431: /*
1.27 ratchov 432: * If the play chain is ahead (most cases) drop some of
433: * the recorded input, to get both in sync.
1.3 ratchov 434: */
435: obuf->drop += delta * obuf->bpf;
436: abuf_ipos(obuf, -delta);
437: } else if (delta < 0) {
438: /*
1.27 ratchov 439: * If record chain is ahead (should never happen,
440: * right?) then insert silence to play.
1.3 ratchov 441: */
442: ibuf->silence += -delta * ibuf->bpf;
443: abuf_opos(ibuf, delta);
1.30 ratchov 444: }
1.1 ratchov 445: }
446:
447: /*
1.27 ratchov 448: * Attach the given input and output buffers to the mixer and the
1.1 ratchov 449: * multiplexer respectively. The operation is done synchronously, so
450: * both buffers enter in sync. If buffers do not match play
1.27 ratchov 451: * and rec.
1.1 ratchov 452: */
453: void
1.25 ratchov 454: dev_attach(char *name,
455: struct abuf *ibuf, struct aparams *sipar, unsigned underrun,
1.18 ratchov 456: struct abuf *obuf, struct aparams *sopar, unsigned overrun, int vol)
1.1 ratchov 457: {
458: struct abuf *pbuf = NULL, *rbuf = NULL;
1.12 ratchov 459: struct aparams ipar, opar;
1.1 ratchov 460: struct aproc *conv;
1.20 ratchov 461: unsigned round, nblk;
462:
1.1 ratchov 463: if (ibuf) {
1.12 ratchov 464: ipar = *sipar;
1.20 ratchov 465: pbuf = LIST_FIRST(&dev_mix->obuflist);
466: nblk = (dev_bufsz / dev_round + 3) / 4;
467: round = dev_roundof(ipar.rate);
1.10 ratchov 468: if (!aparams_eqenc(&ipar, &dev_opar)) {
469: conv = dec_new(name, &ipar);
470: ipar.bps = dev_opar.bps;
471: ipar.bits = dev_opar.bits;
472: ipar.sig = dev_opar.sig;
473: ipar.le = dev_opar.le;
474: ipar.msb = dev_opar.msb;
1.25 ratchov 475: aproc_setin(conv, ibuf);
1.20 ratchov 476: ibuf = abuf_new(nblk * round, &ipar);
1.9 ratchov 477: aproc_setout(conv, ibuf);
1.8 ratchov 478: }
1.10 ratchov 479: if (!aparams_subset(&ipar, &dev_opar)) {
480: conv = cmap_new(name, &ipar, &dev_opar);
481: ipar.cmin = dev_opar.cmin;
482: ipar.cmax = dev_opar.cmax;
1.7 ratchov 483: aproc_setin(conv, ibuf);
1.20 ratchov 484: ibuf = abuf_new(nblk * round, &ipar);
1.7 ratchov 485: aproc_setout(conv, ibuf);
1.6 ratchov 486: }
1.10 ratchov 487: if (!aparams_eqrate(&ipar, &dev_opar)) {
1.20 ratchov 488: conv = resamp_new(name, round, dev_round);
1.10 ratchov 489: ipar.rate = dev_opar.rate;
1.20 ratchov 490: round = dev_round;
1.5 ratchov 491: aproc_setin(conv, ibuf);
1.20 ratchov 492: ibuf = abuf_new(nblk * round, &ipar);
1.5 ratchov 493: aproc_setout(conv, ibuf);
1.1 ratchov 494: }
495: aproc_setin(dev_mix, ibuf);
1.3 ratchov 496: abuf_opos(ibuf, -dev_mix->u.mix.lat);
1.31 ratchov 497: ibuf->r.mix.xrun = underrun;
498: ibuf->r.mix.maxweight = vol;
1.18 ratchov 499: mix_setmaster(dev_mix);
1.1 ratchov 500: }
501: if (obuf) {
1.12 ratchov 502: opar = *sopar;
1.1 ratchov 503: rbuf = LIST_FIRST(&dev_sub->ibuflist);
1.20 ratchov 504: round = dev_roundof(opar.rate);
505: nblk = (dev_bufsz / dev_round + 3) / 4;
1.10 ratchov 506: if (!aparams_eqenc(&opar, &dev_ipar)) {
507: conv = enc_new(name, &opar);
508: opar.bps = dev_ipar.bps;
509: opar.bits = dev_ipar.bits;
510: opar.sig = dev_ipar.sig;
511: opar.le = dev_ipar.le;
512: opar.msb = dev_ipar.msb;
1.9 ratchov 513: aproc_setout(conv, obuf);
1.20 ratchov 514: obuf = abuf_new(nblk * round, &opar);
1.9 ratchov 515: aproc_setin(conv, obuf);
1.8 ratchov 516: }
1.10 ratchov 517: if (!aparams_subset(&opar, &dev_ipar)) {
518: conv = cmap_new(name, &dev_ipar, &opar);
519: opar.cmin = dev_ipar.cmin;
520: opar.cmax = dev_ipar.cmax;
1.7 ratchov 521: aproc_setout(conv, obuf);
1.20 ratchov 522: obuf = abuf_new(nblk * round, &opar);
1.7 ratchov 523: aproc_setin(conv, obuf);
1.6 ratchov 524: }
1.10 ratchov 525: if (!aparams_eqrate(&opar, &dev_ipar)) {
1.20 ratchov 526: conv = resamp_new(name, dev_round, round);
1.10 ratchov 527: opar.rate = dev_ipar.rate;
1.20 ratchov 528: round = dev_round;
1.1 ratchov 529: aproc_setout(conv, obuf);
1.20 ratchov 530: obuf = abuf_new(nblk * round, &opar);
1.1 ratchov 531: aproc_setin(conv, obuf);
532: }
533: aproc_setout(dev_sub, obuf);
1.3 ratchov 534: abuf_ipos(obuf, -dev_sub->u.sub.lat);
1.31 ratchov 535: obuf->w.sub.xrun = overrun;
1.1 ratchov 536: }
537:
538: /*
1.27 ratchov 539: * Sync play to record.
1.1 ratchov 540: */
541: if (ibuf && obuf) {
1.3 ratchov 542: ibuf->duplex = obuf;
543: obuf->duplex = ibuf;
544: dev_sync(ibuf, obuf);
1.13 ratchov 545: }
1.14 ratchov 546: }
547:
548: /*
1.27 ratchov 549: * Change the playback volume of the given stream.
1.14 ratchov 550: */
551: void
552: dev_setvol(struct abuf *ibuf, int vol)
553: {
1.16 ratchov 554: if (!dev_getep(&ibuf, NULL)) {
1.14 ratchov 555: return;
1.16 ratchov 556: }
1.31 ratchov 557: ibuf->r.mix.vol = vol;
1.13 ratchov 558: }
559:
560: /*
1.27 ratchov 561: * Clear buffers of the play and record chains so that when the device
562: * is started, playback and record start in sync.
1.13 ratchov 563: */
564: void
565: dev_clear(void)
566: {
567: struct abuf *buf;
568:
569: if (dev_mix) {
570: buf = LIST_FIRST(&dev_mix->obuflist);
571: while (buf) {
572: abuf_clear(buf);
573: buf = LIST_FIRST(&buf->rproc->obuflist);
574: }
575: mix_clear(dev_mix);
576: }
577: if (dev_sub) {
578: buf = LIST_FIRST(&dev_sub->ibuflist);
579: while (buf) {
580: abuf_clear(buf);
581: buf = LIST_FIRST(&buf->wproc->ibuflist);
582: }
583: sub_clear(dev_sub);
1.1 ratchov 584: }
585: }