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

Annotation of src/usr.bin/sndiod/siofile.c, Revision 1.8

1.8     ! ratchov     1: /*     $OpenBSD: siofile.c,v 1.7 2015/07/24 08:43:04 ratchov Exp $     */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2008-2012 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 <sys/time.h>
                     18: #include <sys/types.h>
                     19:
                     20: #include <poll.h>
                     21: #include <sndio.h>
                     22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
                     26: #include "abuf.h"
                     27: #include "defs.h"
                     28: #include "dev.h"
                     29: #include "dsp.h"
                     30: #include "file.h"
                     31: #include "siofile.h"
                     32: #include "utils.h"
                     33:
1.2       ratchov    34: #define WATCHDOG_USEC  2000000         /* 2 seconds */
                     35:
1.3       ratchov    36: void dev_sio_onmove(void *, int);
                     37: void dev_sio_timeout(void *);
1.1       ratchov    38: int dev_sio_pollfd(void *, struct pollfd *);
                     39: int dev_sio_revents(void *, struct pollfd *);
                     40: void dev_sio_run(void *);
                     41: void dev_sio_hup(void *);
                     42:
                     43: struct fileops dev_sio_ops = {
                     44:        "sio",
                     45:        dev_sio_pollfd,
                     46:        dev_sio_revents,
                     47:        dev_sio_run,
                     48:        dev_sio_run,
                     49:        dev_sio_hup
                     50: };
                     51:
                     52: void
                     53: dev_sio_onmove(void *arg, int delta)
                     54: {
                     55:        struct dev *d = arg;
                     56:
                     57: #ifdef DEBUG
                     58:        if (log_level >= 4) {
                     59:                dev_log(d);
                     60:                log_puts(": tick, delta = ");
                     61:                log_puti(delta);
                     62:                log_puts("\n");
                     63:        }
                     64:        d->sio.sum_utime += file_utime - d->sio.utime;
                     65:        d->sio.sum_wtime += file_wtime - d->sio.wtime;
                     66:        d->sio.wtime = file_wtime;
                     67:        d->sio.utime = file_utime;
                     68:        if (d->mode & MODE_PLAY)
                     69:                d->sio.pused -= delta;
                     70:        if (d->mode & MODE_REC)
                     71:                d->sio.rused += delta;
                     72: #endif
                     73:        dev_onmove(d, delta);
                     74: }
                     75:
1.2       ratchov    76: void
                     77: dev_sio_timeout(void *arg)
                     78: {
                     79:        struct dev *d = arg;
                     80:
                     81:        dev_log(d);
                     82:        log_puts(": watchdog timeout\n");
                     83:        dev_close(d);
                     84: }
                     85:
1.1       ratchov    86: /*
                     87:  * open the device.
                     88:  */
                     89: int
                     90: dev_sio_open(struct dev *d)
                     91: {
                     92:        struct sio_par par;
                     93:        unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
                     94:
                     95:        d->sio.hdl = sio_open(d->path, mode, 1);
                     96:        if (d->sio.hdl == NULL) {
                     97:                if (mode != (SIO_PLAY | SIO_REC))
                     98:                        return 0;
                     99:                d->sio.hdl = sio_open(d->path, SIO_PLAY, 1);
                    100:                if (d->sio.hdl != NULL)
                    101:                        mode = SIO_PLAY;
                    102:                else {
                    103:                        d->sio.hdl = sio_open(d->path, SIO_REC, 1);
                    104:                        if (d->sio.hdl != NULL)
                    105:                                mode = SIO_REC;
                    106:                        else
                    107:                                return 0;
                    108:                }
                    109:                if (log_level >= 1) {
                    110:                        log_puts("warning, device opened in ");
                    111:                        log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
                    112:                        log_puts(" mode\n");
                    113:                }
                    114:        }
                    115:        sio_initpar(&par);
                    116:        par.bits = d->par.bits;
                    117:        par.bps = d->par.bps;
                    118:        par.sig = d->par.sig;
                    119:        par.le = d->par.le;
                    120:        par.msb = d->par.msb;
                    121:        if (mode & SIO_PLAY)
                    122:                par.pchan = d->pchan;
                    123:        if (mode & SIO_REC)
                    124:                par.rchan = d->rchan;
                    125:        if (d->bufsz)
                    126:                par.appbufsz = d->bufsz;
                    127:        if (d->round)
                    128:                par.round = d->round;
                    129:        if (d->rate)
                    130:                par.rate = d->rate;
                    131:        if (!sio_setpar(d->sio.hdl, &par))
                    132:                goto bad_close;
                    133:        if (!sio_getpar(d->sio.hdl, &par))
                    134:                goto bad_close;
1.5       ratchov   135:
                    136: #ifdef DEBUG
                    137:        /*
                    138:         * We support any parameters combination exposed by the kernel,
                    139:         * and we have no other choice than trusting the kernel for
                    140:         * returning correct parameters. But let's check parameters
                    141:         * early and nicely report kernel bugs rather than crashing
                    142:         * later in memset(), malloc() or alike.
                    143:         */
                    144:
                    145:        if (par.bits > BITS_MAX) {
                    146:                log_puts(d->path);
                    147:                log_puts(": ");
                    148:                log_putu(par.bits);
                    149:                log_puts(": unsupported number of bits\n");
                    150:                goto bad_close;
                    151:        }
                    152:        if (par.bps > SIO_BPS(BITS_MAX)) {
                    153:                log_puts(d->path);
                    154:                log_puts(": ");
                    155:                log_putu(par.bits);
                    156:                log_puts(": unsupported sample size\n");
                    157:                goto bad_close;
                    158:        }
                    159:        if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
                    160:                log_puts(d->path);
                    161:                log_puts(": ");
                    162:                log_putu(par.pchan);
                    163:                log_puts(": unsupported number of play channels\n");
                    164:                goto bad_close;
                    165:        }
                    166:        if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
                    167:                log_puts(d->path);
                    168:                log_puts(": ");
                    169:                log_putu(par.rchan);
                    170:                log_puts(": unsupported number of rec channels\n");
                    171:                goto bad_close;
                    172:        }
                    173:        if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
                    174:                log_puts(d->path);
                    175:                log_puts(": ");
                    176:                log_putu(par.bufsz);
                    177:                log_puts(": unsupported buffer size\n");
                    178:                goto bad_close;
                    179:        }
                    180:        if (par.round == 0 || par.round > par.bufsz ||
                    181:            par.bufsz % par.round != 0) {
                    182:                log_puts(d->path);
                    183:                log_puts(": ");
                    184:                log_putu(par.round);
                    185:                log_puts(": unsupported block size\n");
                    186:                goto bad_close;
                    187:        }
                    188:        if (par.rate == 0 || par.rate > RATE_MAX) {
                    189:                log_puts(d->path);
                    190:                log_puts(": ");
                    191:                log_putu(par.rate);
                    192:                log_puts(": unsupported rate\n");
                    193:                goto bad_close;
                    194:        }
                    195: #endif
                    196:
1.1       ratchov   197:        d->par.bits = par.bits;
                    198:        d->par.bps = par.bps;
                    199:        d->par.sig = par.sig;
                    200:        d->par.le = par.le;
                    201:        d->par.msb = par.msb;
                    202:        if (mode & SIO_PLAY)
                    203:                d->pchan = par.pchan;
                    204:        if (mode & SIO_REC)
                    205:                d->rchan = par.rchan;
                    206:        d->bufsz = par.bufsz;
                    207:        d->round = par.round;
                    208:        d->rate = par.rate;
                    209:        if (!(mode & MODE_PLAY))
                    210:                d->mode &= ~(MODE_PLAY | MODE_MON);
                    211:        if (!(mode & MODE_REC))
                    212:                d->mode &= ~MODE_REC;
                    213:        sio_onmove(d->sio.hdl, dev_sio_onmove, d);
                    214:        d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
1.2       ratchov   215:        timo_set(&d->sio.watchdog, dev_sio_timeout, d);
1.1       ratchov   216:        return 1;
                    217:  bad_close:
                    218:        sio_close(d->sio.hdl);
                    219:        return 0;
                    220: }
                    221:
                    222: void
                    223: dev_sio_close(struct dev *d)
                    224: {
                    225: #ifdef DEBUG
                    226:        if (log_level >= 3) {
                    227:                dev_log(d);
                    228:                log_puts(": closed\n");
                    229:        }
                    230: #endif
1.7       ratchov   231:        timo_del(&d->sio.watchdog);
1.1       ratchov   232:        file_del(d->sio.file);
1.6       ratchov   233:        sio_close(d->sio.hdl);
1.1       ratchov   234: }
                    235:
                    236: void
                    237: dev_sio_start(struct dev *d)
                    238: {
                    239:        if (!sio_start(d->sio.hdl)) {
                    240:                if (log_level >= 1) {
                    241:                        dev_log(d);
                    242:                        log_puts(": failed to start device\n");
                    243:                }
                    244:                return;
                    245:        }
                    246:        if (d->mode & MODE_PLAY) {
                    247:                d->sio.cstate = DEV_SIO_CYCLE;
                    248:                d->sio.todo = 0;
                    249:        } else {
                    250:                d->sio.cstate = DEV_SIO_READ;
                    251:                d->sio.todo = d->round * d->rchan * d->par.bps;
                    252:        }
                    253: #ifdef DEBUG
                    254:        d->sio.pused = 0;
                    255:        d->sio.rused = 0;
                    256:        d->sio.sum_utime = 0;
                    257:        d->sio.sum_wtime = 0;
                    258:        d->sio.wtime = file_wtime;
                    259:        d->sio.utime = file_utime;
                    260:        if (log_level >= 3) {
                    261:                dev_log(d);
                    262:                log_puts(": started\n");
                    263:        }
                    264: #endif
1.2       ratchov   265:        timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1       ratchov   266: }
                    267:
                    268: void
                    269: dev_sio_stop(struct dev *d)
                    270: {
                    271:        if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
                    272:                if (log_level >= 1) {
                    273:                        dev_log(d);
                    274:                        log_puts(": failed to stop device\n");
                    275:                }
                    276:                return;
                    277:        }
                    278: #ifdef DEBUG
                    279:        if (log_level >= 3) {
                    280:                dev_log(d);
                    281:                log_puts(": stopped, load avg = ");
                    282:                log_puti(d->sio.sum_utime / 1000);
                    283:                log_puts(" / ");
                    284:                log_puti(d->sio.sum_wtime / 1000);
                    285:                log_puts("\n");
                    286:        }
                    287: #endif
1.2       ratchov   288:        timo_del(&d->sio.watchdog);
1.1       ratchov   289: }
                    290:
                    291: int
                    292: dev_sio_pollfd(void *arg, struct pollfd *pfd)
                    293: {
                    294:        struct dev *d = arg;
                    295:        int events;
                    296:
                    297:        events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
                    298:        return sio_pollfd(d->sio.hdl, pfd, events);
                    299: }
                    300:
                    301: int
                    302: dev_sio_revents(void *arg, struct pollfd *pfd)
                    303: {
                    304:        struct dev *d = arg;
                    305:        int events;
                    306:
                    307:        events = sio_revents(d->sio.hdl, pfd);
                    308: #ifdef DEBUG
                    309:        d->sio.events = events;
                    310: #endif
                    311:        return events;
                    312: }
                    313:
                    314: void
                    315: dev_sio_run(void *arg)
                    316: {
                    317:        struct dev *d = arg;
                    318:        unsigned char *data, *base;
                    319:        unsigned int n;
                    320:
                    321:        /*
                    322:         * sio_read() and sio_write() would block at the end of the
                    323:         * cycle so we *must* return and restart poll()'ing. Otherwise
                    324:         * we may trigger dev_cycle() which would make all clients
                    325:         * underrun (ex, on a play-only device)
                    326:         */
                    327:        for (;;) {
                    328:                if (d->pstate != DEV_RUN)
                    329:                        return;
                    330:                switch (d->sio.cstate) {
                    331:                case DEV_SIO_READ:
                    332: #ifdef DEBUG
                    333:                        if (!(d->sio.events & POLLIN)) {
                    334:                                dev_log(d);
                    335:                                log_puts(": recording, but POLLIN not set\n");
                    336:                                panic();
                    337:                        }
                    338:                        if (d->sio.todo == 0) {
                    339:                                dev_log(d);
                    340:                                log_puts(": can't read data\n");
                    341:                                panic();
                    342:                        }
                    343:                        if (d->prime > 0) {
                    344:                                dev_log(d);
                    345:                                log_puts(": unexpected data\n");
                    346:                                panic();
                    347:                        }
                    348: #endif
                    349:                        base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
                    350:                        data = base +
                    351:                            d->rchan * d->round * d->par.bps -
                    352:                            d->sio.todo;
                    353:                        n = sio_read(d->sio.hdl, data, d->sio.todo);
                    354:                        d->sio.todo -= n;
                    355: #ifdef DEBUG
                    356:                        if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
                    357:                                dev_log(d);
                    358:                                log_puts(": read blocked at cycle start\n");
                    359:                        }
                    360:                        if (log_level >= 4) {
                    361:                                dev_log(d);
                    362:                                log_puts(": read ");
                    363:                                log_putu(n);
                    364:                                log_puts(": bytes, todo ");
                    365:                                log_putu(d->sio.todo);
                    366:                                log_puts("/");
                    367:                                log_putu(d->round * d->rchan * d->par.bps);
                    368:                                log_puts("\n");
                    369:                        }
                    370: #endif
                    371:                        if (d->sio.todo > 0)
                    372:                                return;
                    373: #ifdef DEBUG
                    374:                        d->sio.rused -= d->round;
                    375:                        if (log_level >= 2) {
                    376:                                if (d->sio.rused >= d->round) {
                    377:                                        dev_log(d);
                    378:                                        log_puts(": rec hw xrun, rused = ");
                    379:                                        log_puti(d->sio.rused);
                    380:                                        log_puts("/");
                    381:                                        log_puti(d->bufsz);
                    382:                                        log_puts("\n");
                    383:                                }
                    384:                                if (d->sio.rused < 0 ||
                    385:                                    d->sio.rused >= d->bufsz) {
                    386:                                        dev_log(d);
                    387:                                        log_puts(": out of bounds rused = ");
                    388:                                        log_puti(d->sio.rused);
                    389:                                        log_puts("/");
                    390:                                        log_puti(d->bufsz);
                    391:                                        log_puts("\n");
                    392:                                }
                    393:                        }
                    394: #endif
                    395:                        d->sio.cstate = DEV_SIO_CYCLE;
                    396:                        break;
                    397:                case DEV_SIO_CYCLE:
1.2       ratchov   398:                        timo_del(&d->sio.watchdog);
                    399:                        timo_add(&d->sio.watchdog, WATCHDOG_USEC);
                    400:
1.1       ratchov   401: #ifdef DEBUG
                    402:                        /*
                    403:                         * check that we're called at cycle boundary:
                    404:                         * either after a recorded block, or when POLLOUT is
                    405:                         * raised
                    406:                         */
                    407:                        if (!((d->mode & MODE_REC) && d->prime == 0) &&
                    408:                            !(d->sio.events & POLLOUT)) {
                    409:                                dev_log(d);
                    410:                                log_puts(": cycle not at block boundary\n");
                    411:                                panic();
                    412:                        }
                    413: #endif
                    414:                        dev_cycle(d);
                    415:                        if (d->mode & MODE_PLAY) {
                    416:                                d->sio.cstate = DEV_SIO_WRITE;
                    417:                                d->sio.todo = d->round * d->pchan * d->par.bps;
                    418:                                break;
                    419:                        } else {
                    420:                                d->sio.cstate = DEV_SIO_READ;
                    421:                                d->sio.todo = d->round * d->rchan * d->par.bps;
                    422:                                return;
                    423:                        }
                    424:                case DEV_SIO_WRITE:
                    425: #ifdef DEBUG
                    426:                        if (d->sio.todo == 0) {
                    427:                                dev_log(d);
                    428:                                log_puts(": can't write data\n");
                    429:                                panic();
                    430:                        }
                    431: #endif
                    432:                        base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
                    433:                        data = base +
                    434:                            d->pchan * d->round * d->par.bps -
                    435:                            d->sio.todo;
                    436:                        n = sio_write(d->sio.hdl, data, d->sio.todo);
                    437:                        d->sio.todo -= n;
                    438: #ifdef DEBUG
                    439:                        if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
                    440:                                dev_log(d);
                    441:                                log_puts(": write blocked at cycle start\n");
                    442:                        }
                    443:                        if (log_level >= 4) {
                    444:                                dev_log(d);
                    445:                                log_puts(": wrote ");
                    446:                                log_putu(n);
                    447:                                log_puts(" bytes, todo ");
                    448:                                log_putu(d->sio.todo);
                    449:                                log_puts("/");
                    450:                                log_putu(d->round * d->pchan * d->par.bps);
                    451:                                log_puts("\n");
                    452:                        }
                    453: #endif
                    454:                        if (d->sio.todo > 0)
                    455:                                return;
                    456: #ifdef DEBUG
                    457:                        d->sio.pused += d->round;
                    458:                        if (log_level >= 2) {
                    459:                                if (d->prime == 0 &&
                    460:                                    d->sio.pused <= d->bufsz - d->round) {
                    461:                                        dev_log(d);
                    462:                                        log_puts(": play hw xrun, pused = ");
                    463:                                        log_puti(d->sio.pused);
                    464:                                        log_puts("/");
                    465:                                        log_puti(d->bufsz);
                    466:                                        log_puts("\n");
                    467:                                }
                    468:                                if (d->sio.pused < 0 ||
                    469:                                    d->sio.pused > d->bufsz) {
                    470:                                        /* device driver or libsndio bug */
                    471:                                        dev_log(d);
                    472:                                        log_puts(": out of bounds pused = ");
                    473:                                        log_puti(d->sio.pused);
                    474:                                        log_puts("/");
                    475:                                        log_puti(d->bufsz);
                    476:                                        log_puts("\n");
                    477:                                }
                    478:                        }
                    479: #endif
                    480:                        d->poffs += d->round;
1.4       ratchov   481:                        if (d->poffs == d->psize)
1.1       ratchov   482:                                d->poffs = 0;
                    483:                        if ((d->mode & MODE_REC) && d->prime == 0) {
                    484:                                d->sio.cstate = DEV_SIO_READ;
                    485:                                d->sio.todo = d->round * d->rchan * d->par.bps;
                    486:                        } else
                    487:                                d->sio.cstate = DEV_SIO_CYCLE;
                    488:                        return;
                    489:                }
                    490:        }
                    491: }
                    492:
                    493: void
                    494: dev_sio_hup(void *arg)
                    495: {
                    496:        struct dev *d = arg;
                    497:
1.8     ! ratchov   498: #ifdef DEBUG
        !           499:        if (log_level >= 2) {
        !           500:                dev_log(d);
        !           501:                log_puts(": disconnected\n");
        !           502:        }
        !           503: #endif
1.1       ratchov   504:        dev_close(d);
                    505: }