Merge branch 'master' of ssh://moule/~alex/git/sndio

This commit is contained in:
Alexandre Ratchov 2019-09-05 09:05:37 +02:00
commit e453b782d1
10 changed files with 364 additions and 66 deletions

View File

@ -58,7 +58,10 @@ int dev_getpos(struct dev *);
struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int, struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int, unsigned int); unsigned int, unsigned int, unsigned int, unsigned int);
void dev_adjpar(struct dev *, int, int, int); void dev_adjpar(struct dev *, int, int, int);
int dev_open_do(struct dev *);
int dev_open(struct dev *); int dev_open(struct dev *);
void dev_exitall(struct dev *);
void dev_close_do(struct dev *);
void dev_close(struct dev *); void dev_close(struct dev *);
int dev_ref(struct dev *); int dev_ref(struct dev *);
void dev_unref(struct dev *); void dev_unref(struct dev *);
@ -966,7 +969,8 @@ dev_new(char *path, struct aparams *par,
return NULL; return NULL;
} }
d = xmalloc(sizeof(struct dev)); d = xmalloc(sizeof(struct dev));
d->path = xstrdup(path); d->path_list = NULL;
namelist_add(&d->path_list, path);
d->num = dev_sndnum++; d->num = dev_sndnum++;
d->opt_list = NULL; d->opt_list = NULL;
@ -1029,24 +1033,11 @@ dev_adjpar(struct dev *d, int mode,
* monitor, midi control, and any necessary conversions. * monitor, midi control, and any necessary conversions.
*/ */
int int
dev_open(struct dev *d) dev_open_do(struct dev *d)
{ {
d->mode = d->reqmode;
d->round = d->reqround;
d->bufsz = d->reqbufsz;
d->rate = d->reqrate;
d->pchan = d->reqpchan;
d->rchan = d->reqrchan;
d->par = d->reqpar;
if (d->pchan == 0)
d->pchan = 2;
if (d->rchan == 0)
d->rchan = 2;
if (!dev_sio_open(d)) { if (!dev_sio_open(d)) {
if (log_level >= 1) { if (log_level >= 1) {
dev_log(d); dev_log(d);
log_puts(": ");
log_puts(d->path);
log_puts(": failed to open audio device\n"); log_puts(": failed to open audio device\n");
} }
return 0; return 0;
@ -1109,15 +1100,51 @@ dev_open(struct dev *d)
} }
/* /*
* force the device to go in DEV_CFG state, the caller is supposed to * Reset parameters and open the device.
* ensure buffers are drained */
int
dev_open(struct dev *d)
{
d->mode = d->reqmode;
d->round = d->reqround;
d->bufsz = d->reqbufsz;
d->rate = d->reqrate;
d->pchan = d->reqpchan;
d->rchan = d->reqrchan;
d->par = d->reqpar;
if (d->pchan == 0)
d->pchan = 2;
if (d->rchan == 0)
d->rchan = 2;
if (!dev_open_do(d))
return 0;
return 1;
}
/*
* Force all slots to exit
*/ */
void void
dev_close(struct dev *d) dev_exitall(struct dev *d)
{ {
int i; int i;
struct slot *s; struct slot *s;
for (s = d->slot, i = DEV_NSLOT; i > 0; i--, s++) {
if (s->ops)
s->ops->exit(s->arg);
s->ops = NULL;
}
d->slot_list = NULL;
}
/*
* force the device to go in DEV_CFG state, the caller is supposed to
* ensure buffers are drained
*/
void
dev_close_do(struct dev *d)
{
#ifdef DEBUG #ifdef DEBUG
if (log_level >= 3) { if (log_level >= 3) {
dev_log(d); dev_log(d);
@ -1125,12 +1152,6 @@ dev_close(struct dev *d)
} }
#endif #endif
d->pstate = DEV_CFG; d->pstate = DEV_CFG;
for (s = d->slot, i = DEV_NSLOT; i > 0; i--, s++) {
if (s->ops)
s->ops->exit(s->arg);
s->ops = NULL;
}
d->slot_list = NULL;
dev_sio_close(d); dev_sio_close(d);
if (d->mode & MODE_PLAY) { if (d->mode & MODE_PLAY) {
if (d->encbuf != NULL) if (d->encbuf != NULL)
@ -1144,6 +1165,104 @@ dev_close(struct dev *d)
} }
} }
/*
* Close the device and exit all slots
*/
void
dev_close(struct dev *d)
{
dev_exitall(d);
dev_close_do(d);
}
/*
* Close the device, but attempt to migrate everything to a new sndio
* device.
*/
void
dev_reopen(struct dev *d)
{
struct slot *s;
long long pos;
unsigned int mode, round, bufsz, rate, pstate;
int delta;
/* not opened */
if (d->pstate == DEV_CFG)
return;
if (log_level >= 1) {
dev_log(d);
log_puts(": reopening device\n");
}
/* save state */
mode = d->mode;
round = d->round;
bufsz = d->bufsz;
rate = d->rate;
delta = d->delta;
pstate = d->pstate;
/* close device */
dev_close_do(d);
/* open device */
if (!dev_open_do(d)) {
if (log_level >= 1) {
dev_log(d);
log_puts(": found no working alternate device\n");
}
dev_exitall(d);
return;
}
/* check if new parameters are compatible with old ones */
if (d->mode != mode ||
d->round != round ||
d->bufsz != bufsz ||
d->rate != rate) {
if (log_level >= 1) {
dev_log(d);
log_puts(": alternate device not compatible\n");
}
dev_close(d);
return;
}
/*
* adjust time positions, make anything go back delta ticks, so
* that the new device can start at zero
*/
for (s = d->slot_list; s != NULL; s = s->next) {
pos = (long long)(d->round - delta) * s->round + s->delta_rem;
s->delta_rem = pos % d->round;
s->delta += pos / (int)d->round;
s->delta -= s->round;
if (log_level >= 2) {
slot_log(s);
log_puts(": adjusted: delta -> ");
log_puti(s->delta);
log_puts(", delta_rem -> ");
log_puti(s->delta_rem);
log_puts("\n");
}
}
if (d->tstate == MMC_RUN) {
d->mtc.delta -= delta * MTC_SEC;
if (log_level >= 2) {
dev_log(d);
log_puts(": adjusted mtc: delta ->");
log_puti(d->mtc.delta);
log_puts("\n");
}
}
/* start the device if needed */
if (pstate == DEV_RUN)
dev_wakeup(d);
}
int int
dev_ref(struct dev *d) dev_ref(struct dev *d)
{ {
@ -1250,7 +1369,7 @@ dev_del(struct dev *d)
} }
midi_del(d->midi); midi_del(d->midi);
*p = d->next; *p = d->next;
xfree(d->path); namelist_clear(&d->path_list);
xfree(d); xfree(d);
} }

View File

@ -159,7 +159,7 @@ struct dev {
#define DEV_INIT 1 /* stopped */ #define DEV_INIT 1 /* stopped */
#define DEV_RUN 2 /* playin & recording */ #define DEV_RUN 2 /* playin & recording */
unsigned int pstate; /* one of above */ unsigned int pstate; /* one of above */
char *path; /* sio path */ struct name *path_list;
/* /*
* actual parameters and runtime state (i.e. once opened) * actual parameters and runtime state (i.e. once opened)
@ -201,6 +201,7 @@ extern struct dev *dev_list;
void dev_log(struct dev *); void dev_log(struct dev *);
void dev_close(struct dev *); void dev_close(struct dev *);
void dev_reopen(struct dev *);
struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int, struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int, unsigned int); unsigned int, unsigned int, unsigned int, unsigned int);
struct dev *dev_bynum(int); struct dev *dev_bynum(int);

View File

@ -33,6 +33,7 @@ void port_imsg(void *, unsigned char *, int);
void port_omsg(void *, unsigned char *, int); void port_omsg(void *, unsigned char *, int);
void port_fill(void *, int); void port_fill(void *, int);
void port_exit(void *); void port_exit(void *);
void port_exitall(struct port *);
struct midiops port_midiops = { struct midiops port_midiops = {
port_imsg, port_imsg,
@ -438,7 +439,8 @@ port_new(char *path, unsigned int mode, int hold)
struct port *c; struct port *c;
c = xmalloc(sizeof(struct port)); c = xmalloc(sizeof(struct port));
c->path = xstrdup(path); c->path_list = NULL;
namelist_add(&c->path_list, path);
c->state = PORT_CFG; c->state = PORT_CFG;
c->hold = hold; c->hold = hold;
c->midi = midi_new(&port_midiops, c, mode); c->midi = midi_new(&port_midiops, c, mode);
@ -468,7 +470,7 @@ port_del(struct port *c)
#endif #endif
} }
*p = c->next; *p = c->next;
xfree(c->path); namelist_clear(&c->path_list);
xfree(c); xfree(c);
} }
@ -521,7 +523,7 @@ port_open(struct port *c)
{ {
if (!port_mio_open(c)) { if (!port_mio_open(c)) {
if (log_level >= 1) { if (log_level >= 1) {
log_puts(c->path); port_log(c);
log_puts(": failed to open midi port\n"); log_puts(": failed to open midi port\n");
} }
return 0; return 0;
@ -530,11 +532,23 @@ port_open(struct port *c)
return 1; return 1;
} }
int void
port_close(struct port *c) port_exitall(struct port *c)
{ {
int i; int i;
struct midi *ep; struct midi *ep;
for (i = 0; i < MIDI_NEP; i++) {
ep = midi_ep + i;
if ((ep->txmask & c->midi->self) ||
(c->midi->txmask & ep->self))
ep->ops->exit(ep->arg);
}
}
int
port_close(struct port *c)
{
#ifdef DEBUG #ifdef DEBUG
if (c->state == PORT_CFG) { if (c->state == PORT_CFG) {
port_log(c); port_log(c);
@ -545,12 +559,7 @@ port_close(struct port *c)
c->state = PORT_CFG; c->state = PORT_CFG;
port_mio_close(c); port_mio_close(c);
for (i = 0; i < MIDI_NEP; i++) { port_exitall(c);
ep = midi_ep + i;
if ((ep->txmask & c->midi->self) ||
(c->midi->txmask & ep->self))
ep->ops->exit(ep->arg);
}
return 1; return 1;
} }
@ -586,3 +595,26 @@ port_done(struct port *c)
if (c->state == PORT_INIT) if (c->state == PORT_INIT)
port_drain(c); port_drain(c);
} }
void
port_reopen(struct port *p)
{
if (p->state == PORT_CFG)
return;
if (log_level >= 1) {
port_log(p);
log_puts(": reopening port\n");
}
port_mio_close(p);
if (!port_mio_open(p)) {
if (log_level >= 1) {
port_log(p);
log_puts(": found no working alternate port\n");
}
p->state = PORT_CFG;
port_exitall(p);
}
}

View File

@ -88,8 +88,8 @@ struct port {
#define PORT_DRAIN 2 #define PORT_DRAIN 2
unsigned int state; unsigned int state;
unsigned int num; /* port serial number */ unsigned int num; /* port serial number */
char *path; /* hold the port open ? */ struct name *path_list;
int hold; int hold; /* hold the port open ? */
struct midi *midi; struct midi *midi;
}; };
@ -121,5 +121,6 @@ int port_init(struct port *);
void port_done(struct port *); void port_done(struct port *);
void port_drain(struct port *); void port_drain(struct port *);
int port_close(struct port *); int port_close(struct port *);
void port_reopen(struct port *);
#endif /* !defined(MIDI_H) */ #endif /* !defined(MIDI_H) */

View File

@ -44,13 +44,35 @@ struct fileops port_mio_ops = {
port_mio_hup port_mio_hup
}; };
/*
* open the port using one of the provided paths
*/
static char *
port_mio_openlist(struct port *c, unsigned int mode)
{
struct name *n;
n = c->path_list;
while (1) {
if (n == NULL)
break;
c->mio.hdl = mio_open(n->str, mode, 1);
if (c->mio.hdl != NULL)
return n->str;
n = n->next;
}
return NULL;
}
int int
port_mio_open(struct port *p) port_mio_open(struct port *p)
{ {
p->mio.hdl = mio_open(p->path, p->midi->mode, 1); char *path;
path = port_mio_openlist(p, p->midi->mode);
if (p->mio.hdl == NULL) if (p->mio.hdl == NULL)
return 0; return 0;
p->mio.file = file_new(&port_mio_ops, p, p->path, mio_nfds(p->mio.hdl)); p->mio.file = file_new(&port_mio_ops, p, path, mio_nfds(p->mio.hdl));
return 1; return 1;
} }
@ -128,5 +150,5 @@ port_mio_hup(void *arg)
{ {
struct port *p = arg; struct port *p = arg;
port_close(p); port_reopen(p);
} }

View File

@ -83,6 +83,26 @@ dev_sio_timeout(void *arg)
dev_close(d); dev_close(d);
} }
/*
* open the device using one of the provided paths
*/
static char *
dev_sio_openlist(struct dev *d, unsigned int mode)
{
struct name *n;
n = d->path_list;
while (1) {
if (n == NULL)
break;
d->sio.hdl = sio_open(n->str, mode, 1);
if (d->sio.hdl != NULL)
return n->str;
n = n->next;
}
return NULL;
}
/* /*
* open the device. * open the device.
*/ */
@ -91,17 +111,18 @@ dev_sio_open(struct dev *d)
{ {
struct sio_par par; struct sio_par par;
unsigned int mode = d->mode & (MODE_PLAY | MODE_REC); unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
char *path;
d->sio.hdl = sio_open(d->path, mode, 1); path = dev_sio_openlist(d, mode);
if (d->sio.hdl == NULL) { if (path == NULL) {
if (mode != (SIO_PLAY | SIO_REC)) if (mode != (SIO_PLAY | SIO_REC))
return 0; return 0;
d->sio.hdl = sio_open(d->path, SIO_PLAY, 1); path = dev_sio_openlist(d, SIO_PLAY);
if (d->sio.hdl != NULL) if (path != NULL)
mode = SIO_PLAY; mode = SIO_PLAY;
else { else {
d->sio.hdl = sio_open(d->path, SIO_REC, 1); path = dev_sio_openlist(d, SIO_REC);
if (d->sio.hdl != NULL) if (path != NULL)
mode = SIO_REC; mode = SIO_REC;
else else
return 0; return 0;
@ -143,35 +164,35 @@ dev_sio_open(struct dev *d)
*/ */
if (par.bits > BITS_MAX) { if (par.bits > BITS_MAX) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.bits); log_putu(par.bits);
log_puts(": unsupported number of bits\n"); log_puts(": unsupported number of bits\n");
goto bad_close; goto bad_close;
} }
if (par.bps > SIO_BPS(BITS_MAX)) { if (par.bps > SIO_BPS(BITS_MAX)) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.bps); log_putu(par.bps);
log_puts(": unsupported sample size\n"); log_puts(": unsupported sample size\n");
goto bad_close; goto bad_close;
} }
if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) { if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.pchan); log_putu(par.pchan);
log_puts(": unsupported number of play channels\n"); log_puts(": unsupported number of play channels\n");
goto bad_close; goto bad_close;
} }
if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) { if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.rchan); log_putu(par.rchan);
log_puts(": unsupported number of rec channels\n"); log_puts(": unsupported number of rec channels\n");
goto bad_close; goto bad_close;
} }
if (par.bufsz == 0 || par.bufsz > RATE_MAX) { if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.bufsz); log_putu(par.bufsz);
log_puts(": unsupported buffer size\n"); log_puts(": unsupported buffer size\n");
@ -179,14 +200,14 @@ dev_sio_open(struct dev *d)
} }
if (par.round == 0 || par.round > par.bufsz || if (par.round == 0 || par.round > par.bufsz ||
par.bufsz % par.round != 0) { par.bufsz % par.round != 0) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.round); log_putu(par.round);
log_puts(": unsupported block size\n"); log_puts(": unsupported block size\n");
goto bad_close; goto bad_close;
} }
if (par.rate == 0 || par.rate > RATE_MAX) { if (par.rate == 0 || par.rate > RATE_MAX) {
log_puts(d->path); dev_log(d);
log_puts(": "); log_puts(": ");
log_putu(par.rate); log_putu(par.rate);
log_puts(": unsupported rate\n"); log_puts(": unsupported rate\n");
@ -211,8 +232,14 @@ dev_sio_open(struct dev *d)
if (!(mode & MODE_REC)) if (!(mode & MODE_REC))
d->mode &= ~MODE_REC; d->mode &= ~MODE_REC;
sio_onmove(d->sio.hdl, dev_sio_onmove, d); sio_onmove(d->sio.hdl, dev_sio_onmove, d);
d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl)); d->sio.file = file_new(&dev_sio_ops, d, path, sio_nfds(d->sio.hdl));
timo_set(&d->sio.watchdog, dev_sio_timeout, d); timo_set(&d->sio.watchdog, dev_sio_timeout, d);
if (log_level >= 1) {
dev_log(d);
log_puts(": using ");
log_puts(path);
log_puts("\n");
}
return 1; return 1;
bad_close: bad_close:
sio_close(d->sio.hdl); sio_close(d->sio.hdl);
@ -493,5 +520,5 @@ dev_sio_hup(void *arg)
log_puts(": disconnected\n"); log_puts(": disconnected\n");
} }
#endif #endif
dev_close(d); dev_reopen(d);
} }

View File

@ -29,10 +29,12 @@
.Op Fl C Ar min : Ns Ar max .Op Fl C Ar min : Ns Ar max
.Op Fl c Ar min : Ns Ar max .Op Fl c Ar min : Ns Ar max
.Op Fl e Ar enc .Op Fl e Ar enc
.Op Fl F Ar device
.Op Fl f Ar device .Op Fl f Ar device
.Op Fl j Ar flag .Op Fl j Ar flag
.Op Fl L Ar addr .Op Fl L Ar addr
.Op Fl m Ar mode .Op Fl m Ar mode
.Op Fl Q Ar port
.Op Fl q Ar port .Op Fl q Ar port
.Op Fl r Ar rate .Op Fl r Ar rate
.Op Fl s Ar name .Op Fl s Ar name
@ -182,6 +184,18 @@ or
Only the signedness and the precision are mandatory. Only the signedness and the precision are mandatory.
Examples: Examples:
.Va u8 , s16le , s24le3 , s24le4lsb . .Va u8 , s16le , s24le3 , s24le4lsb .
.It Fl F Ar device
Specify an alternate device to use.
If doesn't work, the one given with the last
.Fl f
or
.Fl F
options will be used.
For instance, specifying a USB device following a
PCI device allows
.Nm
to use the USB one preferably when it's connected
and to fall back to the PCI one when it's disconnected.
.It Fl f Ar device .It Fl f Ar device
Add this Add this
.Xr sndio 7 .Xr sndio 7
@ -245,6 +259,15 @@ but the same sub-device cannot be used for both recording and monitoring.
The default is The default is
.Ar play , Ns Ar rec .Ar play , Ns Ar rec
(i.e. full-duplex). (i.e. full-duplex).
.It Fl Q Ar port
Specify an alternate MIDI port to use.
If doesn't work, the one given with the last
.Fl Q
or
.Fl q
options will be used.
For instance, this allows to replace a USB MIDI controller without
the need to restart programs using it.
.It Fl q Ar port .It Fl q Ar port
Expose the given MIDI port. Expose the given MIDI port.
This allows multiple programs to share the port. This allows multiple programs to share the port.
@ -376,11 +399,15 @@ is
If If
.Nm .Nm
is sent is sent
.Dv SIGHUP ,
.Dv SIGINT .Dv SIGINT
or or
.Dv SIGTERM , .Dv SIGTERM ,
it terminates. it terminates.
If
.Nm
is sent
.Dv SIGHUP ,
it reopens all audio devices and MIDI ports.
.Pp .Pp
By default, when the program cannot accept By default, when the program cannot accept
recorded data fast enough or cannot provide data to play fast enough, recorded data fast enough or cannot provide data to play fast enough,

View File

@ -68,7 +68,7 @@
* block size if neither ``-z'' nor ``-b'' is used * block size if neither ``-z'' nor ``-b'' is used
*/ */
#ifndef DEFAULT_ROUND #ifndef DEFAULT_ROUND
#define DEFAULT_ROUND 960 #define DEFAULT_ROUND 480
#endif #endif
/* /*
@ -86,6 +86,7 @@
#endif #endif
void sigint(int); void sigint(int);
void sighup(int);
void opt_ch(int *, int *); void opt_ch(int *, int *);
void opt_enc(struct aparams *); void opt_enc(struct aparams *);
int opt_mmc(void); int opt_mmc(void);
@ -102,12 +103,13 @@ struct opt *mkopt(char *, struct dev *,
int, int, int, int, int, int, int, int); int, int, int, int, int, int, int, int);
unsigned int log_level = 0; unsigned int log_level = 0;
volatile sig_atomic_t quit_flag = 0; volatile sig_atomic_t quit_flag = 0, reopen_flag = 0;
char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] " char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
"[-C min:max] [-c min:max] [-e enc]\n\t" "[-C min:max] [-c min:max]\n\t"
"[-f device] [-j flag] [-L addr] [-m mode] [-q port] [-r rate]\n\t" "[-e enc] [-F device] [-f device] [-j flag] [-L addr] [-m mode]\n\t"
"[-s name] [-t mode] [-U unit] [-v volume] [-w flag] [-z nframes]\n"; "[-Q port] [-q port] [-r rate] [-s name] [-t mode] [-U unit]\n\t"
"[-v volume] [-w flag] [-z nframes]\n";
/* /*
* SIGINT handler, it raises the quit flag. If the flag is already set, * SIGINT handler, it raises the quit flag. If the flag is already set,
@ -122,6 +124,16 @@ sigint(int s)
quit_flag = 1; quit_flag = 1;
} }
/*
* SIGHUP handler, it raises the reopen flag, which requests devices
* to be reopened.
*/
void
sighup(int s)
{
reopen_flag = 1;
}
void void
opt_ch(int *rcmin, int *rcmax) opt_ch(int *rcmin, int *rcmax)
{ {
@ -224,6 +236,7 @@ setsig(void)
struct sigaction sa; struct sigaction sa;
quit_flag = 0; quit_flag = 0;
reopen_flag = 0;
sigfillset(&sa.sa_mask); sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = sigint; sa.sa_handler = sigint;
@ -231,6 +244,7 @@ setsig(void)
err(1, "sigaction(int) failed"); err(1, "sigaction(int) failed");
if (sigaction(SIGTERM, &sa, NULL) == -1) if (sigaction(SIGTERM, &sa, NULL) == -1)
err(1, "sigaction(term) failed"); err(1, "sigaction(term) failed");
sa.sa_handler = sighup;
if (sigaction(SIGHUP, &sa, NULL) == -1) if (sigaction(SIGHUP, &sa, NULL) == -1)
err(1, "sigaction(hup) failed"); err(1, "sigaction(hup) failed");
} }
@ -287,7 +301,8 @@ mkdev(char *path, struct aparams *par,
struct dev *d; struct dev *d;
for (d = dev_list; d != NULL; d = d->next) { for (d = dev_list; d != NULL; d = d->next) {
if (strcmp(d->path, path) == 0) if (d->path_list->next == NULL &&
strcmp(d->path_list->str, path) == 0)
return d; return d;
} }
if (!bufsz && !round) { if (!bufsz && !round) {
@ -309,7 +324,8 @@ mkport(char *path, int hold)
struct port *c; struct port *c;
for (c = port_list; c != NULL; c = c->next) { for (c = port_list; c != NULL; c = c->next) {
if (strcmp(c->path, path) == 0) if (c->path_list->next == NULL &&
strcmp(c->path_list->str, path) == 0)
return c; return c;
} }
c = port_new(path, MODE_MIDIMASK, hold); c = port_new(path, MODE_MIDIMASK, hold);
@ -375,7 +391,8 @@ main(int argc, char **argv)
mode = MODE_PLAY | MODE_REC; mode = MODE_PLAY | MODE_REC;
tcpaddr_list = NULL; tcpaddr_list = NULL;
while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) { while ((c = getopt(argc, argv,
"a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) {
switch (c) { switch (c) {
case 'd': case 'd':
log_level++; log_level++;
@ -432,6 +449,11 @@ main(int argc, char **argv)
case 'q': case 'q':
mkport(optarg, hold); mkport(optarg, hold);
break; break;
case 'Q':
if (port_list == NULL)
errx(1, "-Q %s: no ports defined", optarg);
namelist_add(&port_list->path_list, optarg);
break;
case 'a': case 'a':
hold = opt_onoff(); hold = opt_onoff();
break; break;
@ -452,6 +474,11 @@ main(int argc, char **argv)
mkdev(optarg, &par, 0, bufsz, round, mkdev(optarg, &par, 0, bufsz, round,
rate, hold, autovol); rate, hold, autovol);
break; break;
case 'F':
if (dev_list == NULL)
errx(1, "-F %s: no devices defined", optarg);
namelist_add(&dev_list->path_list, optarg);
break;
default: default:
fputs(usagestr, stderr); fputs(usagestr, stderr);
return 1; return 1;
@ -519,6 +546,13 @@ main(int argc, char **argv)
for (;;) { for (;;) {
if (quit_flag) if (quit_flag)
break; break;
if (reopen_flag) {
reopen_flag = 0;
for (d = dev_list; d != NULL; d = d->next)
dev_reopen(d);
for (p = port_list; p != NULL; p = p->next)
port_reopen(p);
}
if (!file_poll()) if (!file_poll())
break; break;
} }

View File

@ -188,3 +188,30 @@ xstrdup(char *s)
memcpy(p, s, size); memcpy(p, s, size);
return p; return p;
} }
/*
* copy and append the given string to the name list
*/
void
namelist_add(struct name **list, char *str)
{
struct name *n;
size_t size;
size = strlen(str) + 1;
n = xmalloc(sizeof(struct name) + size);
memcpy(n->str, str, size);
n->next = *list;
*list = n;
}
void
namelist_clear(struct name **list)
{
struct name *n;
while ((n = *list) != NULL) {
*list = n->next;
xfree(n);
}
}

View File

@ -20,6 +20,11 @@
#include <stddef.h> #include <stddef.h>
struct name {
struct name *next;
char str[];
};
void log_puts(char *); void log_puts(char *);
void log_putx(unsigned long); void log_putx(unsigned long);
void log_putu(unsigned long); void log_putu(unsigned long);
@ -31,6 +36,9 @@ void *xmalloc(size_t);
char *xstrdup(char *); char *xstrdup(char *);
void xfree(void *); void xfree(void *);
void namelist_add(struct name **, char *);
void namelist_clear(struct name **);
/* /*
* Log levels: * Log levels:
* *