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

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