[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.5

1.5     ! ratchov     1: /*     $OpenBSD: siofile.c,v 1.4 2014/03/17 17:16:06 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
                    231:        file_del(d->sio.file);
                    232:        sio_close(d->sio.hdl);
                    233: }
                    234:
                    235: void
                    236: dev_sio_start(struct dev *d)
                    237: {
                    238:        if (!sio_start(d->sio.hdl)) {
                    239:                if (log_level >= 1) {
                    240:                        dev_log(d);
                    241:                        log_puts(": failed to start device\n");
                    242:                }
                    243:                return;
                    244:        }
                    245:        if (d->mode & MODE_PLAY) {
                    246:                d->sio.cstate = DEV_SIO_CYCLE;
                    247:                d->sio.todo = 0;
                    248:        } else {
                    249:                d->sio.cstate = DEV_SIO_READ;
                    250:                d->sio.todo = d->round * d->rchan * d->par.bps;
                    251:        }
                    252: #ifdef DEBUG
                    253:        d->sio.pused = 0;
                    254:        d->sio.rused = 0;
                    255:        d->sio.sum_utime = 0;
                    256:        d->sio.sum_wtime = 0;
                    257:        d->sio.wtime = file_wtime;
                    258:        d->sio.utime = file_utime;
                    259:        if (log_level >= 3) {
                    260:                dev_log(d);
                    261:                log_puts(": started\n");
                    262:        }
                    263: #endif
1.2       ratchov   264:        timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1       ratchov   265: }
                    266:
                    267: void
                    268: dev_sio_stop(struct dev *d)
                    269: {
                    270:        if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
                    271:                if (log_level >= 1) {
                    272:                        dev_log(d);
                    273:                        log_puts(": failed to stop device\n");
                    274:                }
                    275:                return;
                    276:        }
                    277: #ifdef DEBUG
                    278:        if (log_level >= 3) {
                    279:                dev_log(d);
                    280:                log_puts(": stopped, load avg = ");
                    281:                log_puti(d->sio.sum_utime / 1000);
                    282:                log_puts(" / ");
                    283:                log_puti(d->sio.sum_wtime / 1000);
                    284:                log_puts("\n");
                    285:        }
                    286: #endif
1.2       ratchov   287:        timo_del(&d->sio.watchdog);
1.1       ratchov   288: }
                    289:
                    290: int
                    291: dev_sio_pollfd(void *arg, struct pollfd *pfd)
                    292: {
                    293:        struct dev *d = arg;
                    294:        int events;
                    295:
                    296:        events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
                    297:        return sio_pollfd(d->sio.hdl, pfd, events);
                    298: }
                    299:
                    300: int
                    301: dev_sio_revents(void *arg, struct pollfd *pfd)
                    302: {
                    303:        struct dev *d = arg;
                    304:        int events;
                    305:
                    306:        events = sio_revents(d->sio.hdl, pfd);
                    307: #ifdef DEBUG
                    308:        d->sio.events = events;
                    309: #endif
                    310:        return events;
                    311: }
                    312:
                    313: void
                    314: dev_sio_run(void *arg)
                    315: {
                    316:        struct dev *d = arg;
                    317:        unsigned char *data, *base;
                    318:        unsigned int n;
                    319:
                    320:        /*
                    321:         * sio_read() and sio_write() would block at the end of the
                    322:         * cycle so we *must* return and restart poll()'ing. Otherwise
                    323:         * we may trigger dev_cycle() which would make all clients
                    324:         * underrun (ex, on a play-only device)
                    325:         */
                    326:        for (;;) {
                    327:                if (d->pstate != DEV_RUN)
                    328:                        return;
                    329:                switch (d->sio.cstate) {
                    330:                case DEV_SIO_READ:
                    331: #ifdef DEBUG
                    332:                        if (!(d->sio.events & POLLIN)) {
                    333:                                dev_log(d);
                    334:                                log_puts(": recording, but POLLIN not set\n");
                    335:                                panic();
                    336:                        }
                    337:                        if (d->sio.todo == 0) {
                    338:                                dev_log(d);
                    339:                                log_puts(": can't read data\n");
                    340:                                panic();
                    341:                        }
                    342:                        if (d->prime > 0) {
                    343:                                dev_log(d);
                    344:                                log_puts(": unexpected data\n");
                    345:                                panic();
                    346:                        }
                    347: #endif
                    348:                        base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
                    349:                        data = base +
                    350:                            d->rchan * d->round * d->par.bps -
                    351:                            d->sio.todo;
                    352:                        n = sio_read(d->sio.hdl, data, d->sio.todo);
                    353:                        d->sio.todo -= n;
                    354: #ifdef DEBUG
                    355:                        if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
                    356:                                dev_log(d);
                    357:                                log_puts(": read blocked at cycle start\n");
                    358:                        }
                    359:                        if (log_level >= 4) {
                    360:                                dev_log(d);
                    361:                                log_puts(": read ");
                    362:                                log_putu(n);
                    363:                                log_puts(": bytes, todo ");
                    364:                                log_putu(d->sio.todo);
                    365:                                log_puts("/");
                    366:                                log_putu(d->round * d->rchan * d->par.bps);
                    367:                                log_puts("\n");
                    368:                        }
                    369: #endif
                    370:                        if (d->sio.todo > 0)
                    371:                                return;
                    372: #ifdef DEBUG
                    373:                        d->sio.rused -= d->round;
                    374:                        if (log_level >= 2) {
                    375:                                if (d->sio.rused >= d->round) {
                    376:                                        dev_log(d);
                    377:                                        log_puts(": rec hw xrun, rused = ");
                    378:                                        log_puti(d->sio.rused);
                    379:                                        log_puts("/");
                    380:                                        log_puti(d->bufsz);
                    381:                                        log_puts("\n");
                    382:                                }
                    383:                                if (d->sio.rused < 0 ||
                    384:                                    d->sio.rused >= d->bufsz) {
                    385:                                        dev_log(d);
                    386:                                        log_puts(": out of bounds rused = ");
                    387:                                        log_puti(d->sio.rused);
                    388:                                        log_puts("/");
                    389:                                        log_puti(d->bufsz);
                    390:                                        log_puts("\n");
                    391:                                }
                    392:                        }
                    393: #endif
                    394:                        d->sio.cstate = DEV_SIO_CYCLE;
                    395:                        break;
                    396:                case DEV_SIO_CYCLE:
1.2       ratchov   397:                        timo_del(&d->sio.watchdog);
                    398:                        timo_add(&d->sio.watchdog, WATCHDOG_USEC);
                    399:
1.1       ratchov   400: #ifdef DEBUG
                    401:                        /*
                    402:                         * check that we're called at cycle boundary:
                    403:                         * either after a recorded block, or when POLLOUT is
                    404:                         * raised
                    405:                         */
                    406:                        if (!((d->mode & MODE_REC) && d->prime == 0) &&
                    407:                            !(d->sio.events & POLLOUT)) {
                    408:                                dev_log(d);
                    409:                                log_puts(": cycle not at block boundary\n");
                    410:                                panic();
                    411:                        }
                    412: #endif
                    413:                        dev_cycle(d);
                    414:                        if (d->mode & MODE_PLAY) {
                    415:                                d->sio.cstate = DEV_SIO_WRITE;
                    416:                                d->sio.todo = d->round * d->pchan * d->par.bps;
                    417:                                break;
                    418:                        } else {
                    419:                                d->sio.cstate = DEV_SIO_READ;
                    420:                                d->sio.todo = d->round * d->rchan * d->par.bps;
                    421:                                return;
                    422:                        }
                    423:                case DEV_SIO_WRITE:
                    424: #ifdef DEBUG
                    425:                        if (d->sio.todo == 0) {
                    426:                                dev_log(d);
                    427:                                log_puts(": can't write data\n");
                    428:                                panic();
                    429:                        }
                    430: #endif
                    431:                        base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
                    432:                        data = base +
                    433:                            d->pchan * d->round * d->par.bps -
                    434:                            d->sio.todo;
                    435:                        n = sio_write(d->sio.hdl, data, d->sio.todo);
                    436:                        d->sio.todo -= n;
                    437: #ifdef DEBUG
                    438:                        if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
                    439:                                dev_log(d);
                    440:                                log_puts(": write blocked at cycle start\n");
                    441:                        }
                    442:                        if (log_level >= 4) {
                    443:                                dev_log(d);
                    444:                                log_puts(": wrote ");
                    445:                                log_putu(n);
                    446:                                log_puts(" bytes, todo ");
                    447:                                log_putu(d->sio.todo);
                    448:                                log_puts("/");
                    449:                                log_putu(d->round * d->pchan * d->par.bps);
                    450:                                log_puts("\n");
                    451:                        }
                    452: #endif
                    453:                        if (d->sio.todo > 0)
                    454:                                return;
                    455: #ifdef DEBUG
                    456:                        d->sio.pused += d->round;
                    457:                        if (log_level >= 2) {
                    458:                                if (d->prime == 0 &&
                    459:                                    d->sio.pused <= d->bufsz - d->round) {
                    460:                                        dev_log(d);
                    461:                                        log_puts(": play hw xrun, pused = ");
                    462:                                        log_puti(d->sio.pused);
                    463:                                        log_puts("/");
                    464:                                        log_puti(d->bufsz);
                    465:                                        log_puts("\n");
                    466:                                }
                    467:                                if (d->sio.pused < 0 ||
                    468:                                    d->sio.pused > d->bufsz) {
                    469:                                        /* device driver or libsndio bug */
                    470:                                        dev_log(d);
                    471:                                        log_puts(": out of bounds pused = ");
                    472:                                        log_puti(d->sio.pused);
                    473:                                        log_puts("/");
                    474:                                        log_puti(d->bufsz);
                    475:                                        log_puts("\n");
                    476:                                }
                    477:                        }
                    478: #endif
                    479:                        d->poffs += d->round;
1.4       ratchov   480:                        if (d->poffs == d->psize)
1.1       ratchov   481:                                d->poffs = 0;
                    482:                        if ((d->mode & MODE_REC) && d->prime == 0) {
                    483:                                d->sio.cstate = DEV_SIO_READ;
                    484:                                d->sio.todo = d->round * d->rchan * d->par.bps;
                    485:                        } else
                    486:                                d->sio.cstate = DEV_SIO_CYCLE;
                    487:                        return;
                    488:                }
                    489:        }
                    490: }
                    491:
                    492: void
                    493: dev_sio_hup(void *arg)
                    494: {
                    495:        struct dev *d = arg;
                    496:
                    497:        dev_close(d);
                    498: }