[BACK]Return to dev.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / aucat

Annotation of src/usr.bin/aucat/dev.c, Revision 1.13

1.13    ! ratchov     1: /*     $OpenBSD: dev.c,v 1.12 2008/11/08 10:01:43 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:  */
                     17: #include <stdio.h>
                     18: #include <stdlib.h>
                     19: #include <unistd.h>
                     20:
                     21: #include "dev.h"
                     22: #include "abuf.h"
                     23: #include "aproc.h"
1.3       ratchov    24: #include "pipe.h"
1.1       ratchov    25: #include "conf.h"
1.3       ratchov    26: #include "safile.h"
1.1       ratchov    27:
1.3       ratchov    28: unsigned dev_bufsz, dev_round, dev_rate;
                     29: unsigned dev_rate_div, dev_round_div;
1.1       ratchov    30: struct aparams dev_ipar, dev_opar;
                     31: struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
1.3       ratchov    32: struct file *dev_file;
1.1       ratchov    33:
                     34: /*
1.3       ratchov    35:  * supported rates
1.1       ratchov    36:  */
1.3       ratchov    37: #define NRATES (sizeof(dev_rates) / sizeof(dev_rates[0]))
                     38: unsigned dev_rates[] = {
                     39:          6400,   7200,   8000,   9600,  11025,  12000,
                     40:         12800,  14400,  16000,  19200,  22050,  24000,
                     41:         25600,  28800,  32000,  38400,  44100,  48000,
                     42:         51200,  57600,  64000,  76800,  88200,  96000,
                     43:        102400, 115200, 128000, 153600, 176400, 192000
                     44: };
1.1       ratchov    45:
                     46: /*
1.3       ratchov    47:  * factors of supported rates
1.1       ratchov    48:  */
1.3       ratchov    49: #define NPRIMES (sizeof(dev_primes) / sizeof(dev_primes[0]))
                     50: unsigned dev_primes[] = {2, 3, 5, 7};
1.1       ratchov    51:
1.3       ratchov    52: int
                     53: dev_setrate(unsigned rate)
1.1       ratchov    54: {
1.3       ratchov    55:        unsigned i, r, p;
1.1       ratchov    56:
1.3       ratchov    57:        r = 1000 * rate;
                     58:        for (i = 0; i < NRATES; i++) {
                     59:                if (i == NRATES) {
                     60:                        fprintf(stderr, "dev_setrate: %u, unsupported\n", rate);
                     61:                        return 0;
                     62:                }
                     63:                if (r > 996 * dev_rates[i] &&
                     64:                    r < 1004 * dev_rates[i]) {
                     65:                        dev_rate = dev_rates[i];
1.1       ratchov    66:                        break;
                     67:                }
1.3       ratchov    68:        }
                     69:
                     70:        dev_rate_div = dev_rate;
                     71:        dev_round_div = dev_round;
                     72:        for (i = 0; i < NPRIMES; i++) {
                     73:                p = dev_primes[i];
                     74:                while (dev_rate_div % p == 0 && dev_round_div % p == 0) {
                     75:                        dev_rate_div /= p;
                     76:                        dev_round_div /= p;
1.1       ratchov    77:                }
                     78:        }
1.3       ratchov    79:        return 1;
1.1       ratchov    80: }
                     81:
                     82: void
1.3       ratchov    83: dev_roundrate(unsigned *newrate, unsigned *newround)
1.1       ratchov    84: {
1.3       ratchov    85:        *newrate += dev_rate_div - 1;
                     86:        *newrate -= *newrate % dev_rate_div;
                     87:        *newround = *newrate * dev_round_div / dev_rate_div;
1.1       ratchov    88: }
                     89:
                     90: /*
                     91:  * open the device with the given hardware parameters and create a mixer
                     92:  * and a multiplexer connected to it with all necessary conversions
                     93:  * setup
                     94:  */
                     95: void
1.3       ratchov    96: dev_init(char *devpath,
1.11      ratchov    97:     struct aparams *dipar, struct aparams *dopar,
                     98:     unsigned bufsz, int blkio)
1.1       ratchov    99: {
                    100:        struct aparams ipar, opar;
                    101:        struct aproc *conv;
                    102:        struct abuf *buf;
1.3       ratchov   103:        unsigned nfr, ibufsz, obufsz;
1.1       ratchov   104:
1.3       ratchov   105:        /*
1.11      ratchov   106:         * ask for 1/4 of the buffer for the kernel ring and
                    107:         * limit the block size to 1/4 of the requested buffer
1.3       ratchov   108:         */
                    109:        dev_bufsz = (bufsz + 3) / 4;
1.11      ratchov   110:        dev_round = (bufsz + 3) / 4;
1.3       ratchov   111:        dev_file = (struct file *)safile_new(&safile_ops, devpath,
1.11      ratchov   112:            dipar, dopar, &dev_bufsz, &dev_round, blkio);
1.3       ratchov   113:        if (!dev_file)
                    114:                exit(1);
                    115:        if (!dev_setrate(dipar ? dipar->rate : dopar->rate))
1.1       ratchov   116:                exit(1);
1.3       ratchov   117:        if (dipar) {
                    118:                dipar->rate = dev_rate;
                    119:                if (debug_level > 0) {
1.9       ratchov   120:                        fprintf(stderr, "dev_init: hw recording ");
1.3       ratchov   121:                        aparams_print(dipar);
1.9       ratchov   122:                        fprintf(stderr, "\n");
1.3       ratchov   123:                }
                    124:        }
                    125:        if (dopar) {
                    126:                dopar->rate = dev_rate;
                    127:                if (debug_level > 0) {
1.9       ratchov   128:                        fprintf(stderr, "dev_init: hw playing ");
1.3       ratchov   129:                        aparams_print(dopar);
1.9       ratchov   130:                        fprintf(stderr, "\n");
1.3       ratchov   131:                }
                    132:        }
1.11      ratchov   133:        ibufsz = obufsz = dev_bufsz;
                    134:        bufsz = (bufsz > dev_bufsz) ? bufsz - dev_bufsz : 0;
1.1       ratchov   135:
                    136:        /*
1.11      ratchov   137:         * use 1/8 of the buffer for the mixer/converters.  Since we
                    138:         * already consumed 1/4 for the device, bufsz represents the
                    139:         * remaining 3/4. So 1/8 is 1/6 of 3/4
                    140:         */
                    141:        nfr = (bufsz + 5) / 6;
                    142:        nfr += dev_round - 1;
                    143:        nfr -= nfr % dev_round;
                    144:        if (nfr == 0)
                    145:                nfr = dev_round;
                    146:
                    147:        /*
                    148:         * create record chain
1.1       ratchov   149:         */
                    150:        if (dipar) {
                    151:                aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
                    152:                /*
                    153:                 * create the read end
                    154:                 */
                    155:                dev_rec = rpipe_new(dev_file);
1.4       ratchov   156:                buf = abuf_new(nfr, dipar);
1.1       ratchov   157:                aproc_setout(dev_rec, buf);
1.3       ratchov   158:                ibufsz += nfr;
1.1       ratchov   159:
                    160:                /*
                    161:                 * append a converter, if needed
                    162:                 */
1.4       ratchov   163:                if (!aparams_eqenc(dipar, &ipar)) {
1.9       ratchov   164:                        conv = dec_new("subin", dipar);
1.1       ratchov   165:                        aproc_setin(conv, buf);
1.4       ratchov   166:                        buf = abuf_new(nfr, &ipar);
1.1       ratchov   167:                        aproc_setout(conv, buf);
1.3       ratchov   168:                        ibufsz += nfr;
1.1       ratchov   169:                }
                    170:                dev_ipar = ipar;
                    171:
                    172:                /*
                    173:                 * append a "sub" to which clients will connect
                    174:                 */
1.3       ratchov   175:                dev_sub = sub_new("sub", nfr);
1.1       ratchov   176:                aproc_setin(dev_sub, buf);
                    177:        } else {
                    178:                dev_rec = NULL;
                    179:                dev_sub = NULL;
                    180:        }
                    181:
                    182:        /*
                    183:         * create play chain
                    184:         */
                    185:        if (dopar) {
                    186:                aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
                    187:                /*
                    188:                 * create the write end
                    189:                 */
                    190:                dev_play = wpipe_new(dev_file);
1.4       ratchov   191:                buf = abuf_new(nfr, dopar);
1.1       ratchov   192:                aproc_setin(dev_play, buf);
1.3       ratchov   193:                obufsz += nfr;
                    194:
1.1       ratchov   195:                /*
                    196:                 * append a converter, if needed
                    197:                 */
1.4       ratchov   198:                if (!aparams_eqenc(&opar, dopar)) {
1.9       ratchov   199:                        conv = enc_new("mixout", dopar);
1.1       ratchov   200:                        aproc_setout(conv, buf);
1.4       ratchov   201:                        buf = abuf_new(nfr, &opar);
1.1       ratchov   202:                        aproc_setin(conv, buf);
1.3       ratchov   203:                        obufsz += nfr;
1.1       ratchov   204:                }
                    205:                dev_opar = opar;
                    206:
                    207:                /*
                    208:                 * append a "mix" to which clients will connect
                    209:                 */
1.3       ratchov   210:                dev_mix = mix_new("mix", nfr);
1.1       ratchov   211:                aproc_setout(dev_mix, buf);
                    212:        } else {
                    213:                dev_play = NULL;
                    214:                dev_mix = NULL;
                    215:        }
1.3       ratchov   216:        dev_bufsz = (dopar) ? obufsz : ibufsz;
                    217:        DPRINTF("dev_init: using %u fpb\n", dev_bufsz);
                    218:        dev_start();
1.1       ratchov   219: }
                    220:
                    221: /*
                    222:  * cleanly stop and drain everything and close the device
                    223:  * once both play chain and record chain are gone
                    224:  */
                    225: void
                    226: dev_done(void)
                    227: {
                    228:        struct file *f;
                    229:
1.3       ratchov   230:        if (dev_mix) {
                    231:                /*
                    232:                 * generate EOF on all inputs (but not the device), and
                    233:                 * put the mixer in ``autoquit'' state, so once buffers
                    234:                 * are drained the mixer will terminate and shutdown the
                    235:                 * write-end of the device
                    236:                 *
                    237:                 * NOTE: since file_eof() can destroy the file and
                    238:                 * reorder the file_list, we have to restart the loop
                    239:                 * after each call to file_eof()
                    240:                 */
                    241:        restart:
                    242:                LIST_FOREACH(f, &file_list, entry) {
                    243:                        if (f != dev_file && f->rproc) {
                    244:                                file_eof(f);
                    245:                                goto restart;
                    246:                        }
                    247:                }
                    248:                if (dev_mix)
                    249:                        dev_mix->u.mix.flags |= MIX_AUTOQUIT;
                    250:
                    251:                /*
                    252:                 * wait play chain to terminate
                    253:                 */
                    254:                while (dev_file->wproc != NULL) {
                    255:                        if (!file_poll())
                    256:                                break;
                    257:                }
                    258:                dev_mix = 0;
1.1       ratchov   259:        }
1.3       ratchov   260:        if (dev_sub) {
                    261:                /*
                    262:                 * same as above, but for the record chain: generate eof
                    263:                 * on the read-end of the device and wait record buffers
                    264:                 * to desappear.  We must stop the device first, because
                    265:                 * play-end will underrun (and xrun correction code will
                    266:                 * insert silence on the record-end of the device)
                    267:                 */
                    268:                dev_stop();
                    269:                file_eof(dev_file);
                    270:                if (dev_sub)
                    271:                        dev_sub->u.sub.flags |= SUB_AUTOQUIT;
                    272:                for (;;) {
                    273:                        if (!file_poll())
                    274:                                break;
                    275:                }
                    276:                dev_sub = NULL;
1.1       ratchov   277:        }
                    278: }
                    279:
                    280: /*
                    281:  * start the (paused) device. By default it's paused
                    282:  */
                    283: void
                    284: dev_start(void)
                    285: {
                    286:        if (dev_mix)
                    287:                dev_mix->u.mix.flags |= MIX_DROP;
                    288:        if (dev_sub)
                    289:                dev_sub->u.sub.flags |= SUB_DROP;
1.3       ratchov   290:        dev_file->ops->start(dev_file);
1.1       ratchov   291: }
                    292:
                    293: /*
                    294:  * pause the device
                    295:  */
                    296: void
                    297: dev_stop(void)
                    298: {
1.3       ratchov   299:        dev_file->ops->stop(dev_file);
1.1       ratchov   300:        if (dev_mix)
                    301:                dev_mix->u.mix.flags &= ~MIX_DROP;
                    302:        if (dev_sub)
                    303:                dev_sub->u.sub.flags &= ~SUB_DROP;
                    304: }
                    305:
                    306: /*
1.3       ratchov   307:  * sync play buffer to rec buffer (for instance when one of
                    308:  * them underruns/overruns)
1.1       ratchov   309:  */
                    310: void
1.3       ratchov   311: dev_sync(struct abuf *ibuf, struct abuf *obuf)
1.1       ratchov   312: {
1.3       ratchov   313:        struct abuf *pbuf, *rbuf;
                    314:        int delta;
                    315:
                    316:        if (!dev_mix || !dev_sub)
                    317:                return;
                    318:        pbuf = LIST_FIRST(&dev_mix->obuflist);
                    319:        if (!pbuf)
                    320:                return;
                    321:        rbuf = LIST_FIRST(&dev_sub->ibuflist);
                    322:        if (!rbuf)
                    323:                return;
                    324:        for (;;) {
                    325:                if (!ibuf || !ibuf->rproc) {
                    326:                        DPRINTF("dev_sync: reader desappeared\n");
                    327:                        return;
                    328:                }
                    329:                if (ibuf->rproc == dev_mix)
1.1       ratchov   330:                        break;
1.3       ratchov   331:                ibuf = LIST_FIRST(&ibuf->rproc->obuflist);
                    332:        }
                    333:        for (;;) {
                    334:                if (!obuf || !obuf->wproc) {
                    335:                        DPRINTF("dev_sync: writer desappeared\n");
                    336:                        return;
                    337:                }
                    338:                if (obuf->wproc == dev_sub)
1.1       ratchov   339:                        break;
1.3       ratchov   340:                obuf = LIST_FIRST(&obuf->wproc->ibuflist);
1.1       ratchov   341:        }
1.3       ratchov   342:
                    343:        /*
                    344:         * calculate delta, the number of frames the play chain is ahead
                    345:         * of the record chain. It's necessary to schedule silences (or
                    346:         * drops) in order to start playback and record in sync.
                    347:         */
                    348:        delta =
                    349:            rbuf->bpf * (pbuf->abspos + pbuf->used) -
                    350:            pbuf->bpf *  rbuf->abspos;
                    351:        delta /= pbuf->bpf * rbuf->bpf;
                    352:        DPRINTF("dev_sync: delta = %d, ppos = %u, pused = %u, rpos = %u\n",
                    353:            delta, pbuf->abspos, pbuf->used, rbuf->abspos);
                    354:
                    355:        if (delta > 0) {
                    356:                /*
                    357:                 * if the play chain is ahead (most cases) drop some of
                    358:                 * the recorded input, to get both in sync
                    359:                 */
                    360:                obuf->drop += delta * obuf->bpf;
                    361:                abuf_ipos(obuf, -delta);
                    362:        } else if (delta < 0) {
                    363:                /*
                    364:                 * if record chain is ahead (should never happen,
                    365:                 * right?) then insert silence to play
                    366:                 */
                    367:                ibuf->silence += -delta * ibuf->bpf;
                    368:                abuf_opos(ibuf, delta);
                    369:        } else
                    370:                DPRINTF("dev_sync: nothing to do\n");
1.1       ratchov   371: }
                    372:
                    373: /*
                    374:  * attach the given input and output buffers to the mixer and the
                    375:  * multiplexer respectively. The operation is done synchronously, so
                    376:  * both buffers enter in sync. If buffers do not match play
                    377:  * and rec
                    378:  */
                    379: void
                    380: dev_attach(char *name,
1.10      ratchov   381:     struct abuf *ibuf, struct aparams *sipar, unsigned underrun,
                    382:     struct abuf *obuf, struct aparams *sopar, unsigned overrun)
1.1       ratchov   383: {
                    384:        struct abuf *pbuf = NULL, *rbuf = NULL;
1.12      ratchov   385:        struct aparams ipar, opar;
1.1       ratchov   386:        struct aproc *conv;
1.3       ratchov   387:        unsigned nfr;
1.1       ratchov   388:
                    389:        if (ibuf) {
1.12      ratchov   390:                ipar = *sipar;
1.1       ratchov   391:                pbuf = LIST_FIRST(&dev_mix->obuflist);
1.10      ratchov   392:                if (!aparams_eqenc(&ipar, &dev_opar)) {
1.3       ratchov   393:                        nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
                    394:                        nfr -= nfr % dev_round;
1.10      ratchov   395:                        conv = dec_new(name, &ipar);
                    396:                        ipar.bps = dev_opar.bps;
                    397:                        ipar.bits = dev_opar.bits;
                    398:                        ipar.sig = dev_opar.sig;
                    399:                        ipar.le = dev_opar.le;
                    400:                        ipar.msb = dev_opar.msb;
1.9       ratchov   401:                        aproc_setin(conv, ibuf);
1.10      ratchov   402:                        ibuf = abuf_new(nfr, &ipar);
1.9       ratchov   403:                        aproc_setout(conv, ibuf);
1.8       ratchov   404:                }
1.10      ratchov   405:                if (!aparams_subset(&ipar, &dev_opar)) {
1.7       ratchov   406:                        nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
                    407:                        nfr -= nfr % dev_round;
1.10      ratchov   408:                        conv = cmap_new(name, &ipar, &dev_opar);
                    409:                        ipar.cmin = dev_opar.cmin;
                    410:                        ipar.cmax = dev_opar.cmax;
1.7       ratchov   411:                        aproc_setin(conv, ibuf);
1.10      ratchov   412:                        ibuf = abuf_new(nfr, &ipar);
1.7       ratchov   413:                        aproc_setout(conv, ibuf);
1.6       ratchov   414:                }
1.10      ratchov   415:                if (!aparams_eqrate(&ipar, &dev_opar)) {
1.5       ratchov   416:                        nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
                    417:                        nfr -= nfr % dev_round;
1.10      ratchov   418:                        conv = resamp_new(name, &ipar, &dev_opar);
                    419:                        ipar.rate = dev_opar.rate;
1.5       ratchov   420:                        aproc_setin(conv, ibuf);
1.10      ratchov   421:                        ibuf = abuf_new(nfr, &ipar);
1.5       ratchov   422:                        aproc_setout(conv, ibuf);
1.1       ratchov   423:                }
                    424:                aproc_setin(dev_mix, ibuf);
1.3       ratchov   425:                abuf_opos(ibuf, -dev_mix->u.mix.lat);
1.1       ratchov   426:                ibuf->xrun = underrun;
                    427:        }
                    428:        if (obuf) {
1.12      ratchov   429:                opar = *sopar;
1.1       ratchov   430:                rbuf = LIST_FIRST(&dev_sub->ibuflist);
1.10      ratchov   431:                if (!aparams_eqenc(&opar, &dev_ipar)) {
1.3       ratchov   432:                        nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
                    433:                        nfr -= nfr % dev_round;
1.10      ratchov   434:                        conv = enc_new(name, &opar);
                    435:                        opar.bps = dev_ipar.bps;
                    436:                        opar.bits = dev_ipar.bits;
                    437:                        opar.sig = dev_ipar.sig;
                    438:                        opar.le = dev_ipar.le;
                    439:                        opar.msb = dev_ipar.msb;
1.9       ratchov   440:                        aproc_setout(conv, obuf);
1.10      ratchov   441:                        obuf = abuf_new(nfr, &opar);
1.9       ratchov   442:                        aproc_setin(conv, obuf);
1.8       ratchov   443:                }
1.10      ratchov   444:                if (!aparams_subset(&opar, &dev_ipar)) {
1.7       ratchov   445:                        nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
                    446:                        nfr -= nfr % dev_round;
1.10      ratchov   447:                        conv = cmap_new(name, &dev_ipar, &opar);
                    448:                        opar.cmin = dev_ipar.cmin;
                    449:                        opar.cmax = dev_ipar.cmax;
1.7       ratchov   450:                        aproc_setout(conv, obuf);
1.10      ratchov   451:                        obuf = abuf_new(nfr, &opar);
1.7       ratchov   452:                        aproc_setin(conv, obuf);
1.6       ratchov   453:                }
1.10      ratchov   454:                if (!aparams_eqrate(&opar, &dev_ipar)) {
1.5       ratchov   455:                        nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
                    456:                        nfr -= nfr % dev_round;
1.10      ratchov   457:                        conv = resamp_new(name, &dev_ipar, &opar);
                    458:                        opar.rate = dev_ipar.rate;
1.1       ratchov   459:                        aproc_setout(conv, obuf);
1.10      ratchov   460:                        obuf = abuf_new(nfr, &opar);
1.1       ratchov   461:                        aproc_setin(conv, obuf);
                    462:                }
                    463:                aproc_setout(dev_sub, obuf);
1.3       ratchov   464:                abuf_ipos(obuf, -dev_sub->u.sub.lat);
1.1       ratchov   465:                obuf->xrun = overrun;
                    466:        }
                    467:
                    468:        /*
1.3       ratchov   469:         * sync play to record
1.1       ratchov   470:         */
                    471:        if (ibuf && obuf) {
1.3       ratchov   472:                ibuf->duplex = obuf;
                    473:                obuf->duplex = ibuf;
                    474:                dev_sync(ibuf, obuf);
1.13    ! ratchov   475:        }
        !           476: }
        !           477:
        !           478: /*
        !           479:  * clear buffers of the play and record chains so that when the device
        !           480:  * is started, playback and record start in sync
        !           481:  */
        !           482: void
        !           483: dev_clear(void)
        !           484: {
        !           485:        struct abuf *buf;
        !           486:
        !           487:        if (dev_mix) {
        !           488:                if (!LIST_EMPTY(&dev_mix->ibuflist)) {
        !           489:                        fprintf(stderr, "dev_clear: mixer not idle\n");
        !           490:                        abort();
        !           491:                }
        !           492:                buf = LIST_FIRST(&dev_mix->obuflist);
        !           493:                while (buf) {
        !           494:                        abuf_clear(buf);
        !           495:                        buf = LIST_FIRST(&buf->rproc->obuflist);
        !           496:                }
        !           497:                mix_clear(dev_mix);
        !           498:        }
        !           499:        if (dev_sub) {
        !           500:                if (!LIST_EMPTY(&dev_sub->obuflist)) {
        !           501:                        fprintf(stderr, "dev_suspend: demux not idle\n");
        !           502:                        abort();
        !           503:                }
        !           504:                buf = LIST_FIRST(&dev_sub->ibuflist);
        !           505:                while (buf) {
        !           506:                        abuf_clear(buf);
        !           507:                        buf = LIST_FIRST(&buf->wproc->ibuflist);
        !           508:                }
        !           509:                sub_clear(dev_sub);
1.1       ratchov   510:        }
                    511: }