xref: /openbsd-src/usr.bin/sndiod/opt.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
1*7b639200Sratchov /*	$OpenBSD: opt.c,v 1.13 2024/12/20 07:35:56 ratchov Exp $	*/
287bc9f6aSratchov /*
387bc9f6aSratchov  * Copyright (c) 2008-2011 Alexandre Ratchov <alex@caoua.org>
487bc9f6aSratchov  *
587bc9f6aSratchov  * Permission to use, copy, modify, and distribute this software for any
687bc9f6aSratchov  * purpose with or without fee is hereby granted, provided that the above
787bc9f6aSratchov  * copyright notice and this permission notice appear in all copies.
887bc9f6aSratchov  *
987bc9f6aSratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1087bc9f6aSratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1187bc9f6aSratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1287bc9f6aSratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1387bc9f6aSratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1487bc9f6aSratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1587bc9f6aSratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1687bc9f6aSratchov  */
1787bc9f6aSratchov #include <string.h>
1887bc9f6aSratchov 
1987bc9f6aSratchov #include "dev.h"
205fb8f3c9Sratchov #include "midi.h"
2187bc9f6aSratchov #include "opt.h"
225fb8f3c9Sratchov #include "sysex.h"
2387bc9f6aSratchov #include "utils.h"
2487bc9f6aSratchov 
255684d550Sratchov struct opt *opt_list;
265684d550Sratchov 
275fb8f3c9Sratchov void opt_midi_imsg(void *, unsigned char *, int);
285fb8f3c9Sratchov void opt_midi_omsg(void *, unsigned char *, int);
295fb8f3c9Sratchov void opt_midi_fill(void *, int);
305fb8f3c9Sratchov void opt_midi_exit(void *);
315fb8f3c9Sratchov 
325fb8f3c9Sratchov struct midiops opt_midiops = {
335fb8f3c9Sratchov 	opt_midi_imsg,
345fb8f3c9Sratchov 	opt_midi_omsg,
355fb8f3c9Sratchov 	opt_midi_fill,
365fb8f3c9Sratchov 	opt_midi_exit
375fb8f3c9Sratchov };
385fb8f3c9Sratchov 
395fb8f3c9Sratchov void
405fb8f3c9Sratchov opt_midi_imsg(void *arg, unsigned char *msg, int len)
415fb8f3c9Sratchov {
425fb8f3c9Sratchov #ifdef DEBUG
435fb8f3c9Sratchov 	struct opt *o = arg;
445fb8f3c9Sratchov 
45*7b639200Sratchov 	logx(0, "%s: can't receive midi messages", o->name);
465fb8f3c9Sratchov 	panic();
475fb8f3c9Sratchov #endif
485fb8f3c9Sratchov }
495fb8f3c9Sratchov 
505fb8f3c9Sratchov void
515fb8f3c9Sratchov opt_midi_omsg(void *arg, unsigned char *msg, int len)
525fb8f3c9Sratchov {
535fb8f3c9Sratchov 	struct opt *o = arg;
545fb8f3c9Sratchov 	struct sysex *x;
555fb8f3c9Sratchov 	unsigned int fps, chan;
565fb8f3c9Sratchov 
575fb8f3c9Sratchov 	if ((msg[0] & MIDI_CMDMASK) == MIDI_CTL && msg[1] == MIDI_CTL_VOL) {
585fb8f3c9Sratchov 		chan = msg[0] & MIDI_CHANMASK;
595fb8f3c9Sratchov 		if (chan >= DEV_NSLOT)
605fb8f3c9Sratchov 			return;
6136355b88Sratchov 		if (slot_array[chan].opt != o)
625fb8f3c9Sratchov 			return;
635fb8f3c9Sratchov 		slot_setvol(slot_array + chan, msg[2]);
645fb8f3c9Sratchov 		ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
655fb8f3c9Sratchov 		return;
665fb8f3c9Sratchov 	}
675fb8f3c9Sratchov 	x = (struct sysex *)msg;
685fb8f3c9Sratchov 	if (x->start != SYSEX_START)
695fb8f3c9Sratchov 		return;
705fb8f3c9Sratchov 	if (len < SYSEX_SIZE(empty))
715fb8f3c9Sratchov 		return;
725fb8f3c9Sratchov 	switch (x->type) {
735fb8f3c9Sratchov 	case SYSEX_TYPE_RT:
745fb8f3c9Sratchov 		if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
755fb8f3c9Sratchov 			if (len == SYSEX_SIZE(master)) {
765fb8f3c9Sratchov 				dev_master(o->dev, x->u.master.coarse);
775fb8f3c9Sratchov 				if (o->dev->master_enabled) {
785fb8f3c9Sratchov 					ctl_onval(CTL_DEV_MASTER, o->dev, NULL,
795fb8f3c9Sratchov 					   x->u.master.coarse);
805fb8f3c9Sratchov 				}
815fb8f3c9Sratchov 			}
825fb8f3c9Sratchov 			return;
835fb8f3c9Sratchov 		}
845fb8f3c9Sratchov 		if (x->id0 != SYSEX_MMC)
855fb8f3c9Sratchov 			return;
865fb8f3c9Sratchov 		switch (x->id1) {
875fb8f3c9Sratchov 		case SYSEX_MMC_STOP:
885fb8f3c9Sratchov 			if (len != SYSEX_SIZE(stop))
895fb8f3c9Sratchov 				return;
900e6be583Sratchov 			if (o->mtc == NULL)
915fb8f3c9Sratchov 				return;
9236355b88Sratchov 			mtc_setdev(o->mtc, o->dev);
93*7b639200Sratchov 			logx(2, "%s: mmc stop", o->name);
940e6be583Sratchov 			mtc_stop(o->mtc);
955fb8f3c9Sratchov 			break;
965fb8f3c9Sratchov 		case SYSEX_MMC_START:
975fb8f3c9Sratchov 			if (len != SYSEX_SIZE(start))
985fb8f3c9Sratchov 				return;
990e6be583Sratchov 			if (o->mtc == NULL)
1005fb8f3c9Sratchov 				return;
10136355b88Sratchov 			mtc_setdev(o->mtc, o->dev);
102*7b639200Sratchov 			logx(2, "%s: mmc start", o->name);
1030e6be583Sratchov 			mtc_start(o->mtc);
1045fb8f3c9Sratchov 			break;
1055fb8f3c9Sratchov 		case SYSEX_MMC_LOC:
1065fb8f3c9Sratchov 			if (len != SYSEX_SIZE(loc) ||
1075fb8f3c9Sratchov 			    x->u.loc.len != SYSEX_MMC_LOC_LEN ||
1085fb8f3c9Sratchov 			    x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
1095fb8f3c9Sratchov 				return;
1100e6be583Sratchov 			if (o->mtc == NULL)
1115fb8f3c9Sratchov 				return;
11236355b88Sratchov 			mtc_setdev(o->mtc, o->dev);
1135fb8f3c9Sratchov 			switch (x->u.loc.hr >> 5) {
1145fb8f3c9Sratchov 			case MTC_FPS_24:
1155fb8f3c9Sratchov 				fps = 24;
1165fb8f3c9Sratchov 				break;
1175fb8f3c9Sratchov 			case MTC_FPS_25:
1185fb8f3c9Sratchov 				fps = 25;
1195fb8f3c9Sratchov 				break;
1205fb8f3c9Sratchov 			case MTC_FPS_30:
1215fb8f3c9Sratchov 				fps = 30;
1225fb8f3c9Sratchov 				break;
1235fb8f3c9Sratchov 			default:
1240e6be583Sratchov 				mtc_stop(o->mtc);
1255fb8f3c9Sratchov 				return;
1265fb8f3c9Sratchov 			}
1270e6be583Sratchov 			mtc_loc(o->mtc,
1285fb8f3c9Sratchov 			    (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
1295fb8f3c9Sratchov 			     x->u.loc.min * 60 * MTC_SEC +
1305fb8f3c9Sratchov 			     x->u.loc.sec * MTC_SEC +
1315fb8f3c9Sratchov 			     x->u.loc.fr * (MTC_SEC / fps));
1325fb8f3c9Sratchov 			break;
1335fb8f3c9Sratchov 		}
1345fb8f3c9Sratchov 		break;
1355fb8f3c9Sratchov 	case SYSEX_TYPE_EDU:
1365fb8f3c9Sratchov 		if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
1375fb8f3c9Sratchov 			return;
1385fb8f3c9Sratchov 		if (len != SYSEX_SIZE(dumpreq))
1395fb8f3c9Sratchov 			return;
1405fb8f3c9Sratchov 		dev_midi_dump(o->dev);
1415fb8f3c9Sratchov 		break;
1425fb8f3c9Sratchov 	}
1435fb8f3c9Sratchov }
1445fb8f3c9Sratchov 
1455fb8f3c9Sratchov void
1465fb8f3c9Sratchov opt_midi_fill(void *arg, int count)
1475fb8f3c9Sratchov {
1485fb8f3c9Sratchov 	/* nothing to do */
1495fb8f3c9Sratchov }
1505fb8f3c9Sratchov 
1515fb8f3c9Sratchov void
1525fb8f3c9Sratchov opt_midi_exit(void *arg)
1535fb8f3c9Sratchov {
1545fb8f3c9Sratchov 	struct opt *o = arg;
1555fb8f3c9Sratchov 
156*7b639200Sratchov 	logx(1, "%s: midi end point died", o->name);
1575fb8f3c9Sratchov }
1585fb8f3c9Sratchov 
15987bc9f6aSratchov /*
16087bc9f6aSratchov  * create a new audio sub-device "configuration"
16187bc9f6aSratchov  */
16287bc9f6aSratchov struct opt *
163db7ff504Sratchov opt_new(struct dev *d, char *name,
16487bc9f6aSratchov     int pmin, int pmax, int rmin, int rmax,
16587bc9f6aSratchov     int maxweight, int mmc, int dup, unsigned int mode)
16687bc9f6aSratchov {
16736355b88Sratchov 	struct dev *a;
168869641a9Sratchov 	struct opt *o, **po;
169*7b639200Sratchov 	char str[64];
170869641a9Sratchov 	unsigned int len, num;
17187bc9f6aSratchov 	char c;
17287bc9f6aSratchov 
17336355b88Sratchov 	if (name == NULL) {
17436355b88Sratchov 		name = d->name;
17536355b88Sratchov 		len = strlen(name);
17636355b88Sratchov 	} else {
17787bc9f6aSratchov 		for (len = 0; name[len] != '\0'; len++) {
17887bc9f6aSratchov 			if (len == OPT_NAMEMAX) {
179*7b639200Sratchov 				logx(0, "%s: too long", name);
180cadb4237Sratchov 				return NULL;
18187bc9f6aSratchov 			}
18287bc9f6aSratchov 			c = name[len];
18387bc9f6aSratchov 			if ((c < 'a' || c > 'z') &&
18487bc9f6aSratchov 			    (c < 'A' || c > 'Z')) {
185*7b639200Sratchov 				logx(0, "%s: only alphabetic chars allowed", name);
186cadb4237Sratchov 				return NULL;
18787bc9f6aSratchov 			}
18887bc9f6aSratchov 		}
18936355b88Sratchov 	}
190869641a9Sratchov 	num = 0;
191869641a9Sratchov 	for (po = &opt_list; *po != NULL; po = &(*po)->next)
192869641a9Sratchov 		num++;
193869641a9Sratchov 	if (num >= OPT_NMAX) {
194*7b639200Sratchov 		logx(0, "%s: too many opts", name);
195869641a9Sratchov 		return NULL;
196869641a9Sratchov 	}
19736355b88Sratchov 
19836355b88Sratchov 	if (opt_byname(name)) {
199*7b639200Sratchov 		logx(1, "%s: already defined", name);
200869641a9Sratchov 		return NULL;
201869641a9Sratchov 	}
2020e6be583Sratchov 
2030e6be583Sratchov 	if (mmc) {
2040e6be583Sratchov 		if (mtc_array[0].dev != NULL && mtc_array[0].dev != d) {
205*7b639200Sratchov 			logx(0, "%s: MTC already setup for another device", name);
2060e6be583Sratchov 			return NULL;
2070e6be583Sratchov 		}
2080e6be583Sratchov 		mtc_array[0].dev = d;
209*7b639200Sratchov 		logx(2, "%s: initial MTC source, controlled by MMC", d->path);
2100e6be583Sratchov 	}
2110e6be583Sratchov 
21236355b88Sratchov 	if (strcmp(d->name, name) == 0)
21336355b88Sratchov 		a = d;
21436355b88Sratchov 	else {
21536355b88Sratchov 		/* circulate to the first "alternate" device (greatest num) */
21636355b88Sratchov 		for (a = d; a->alt_next->num > a->num; a = a->alt_next)
21736355b88Sratchov 			;
21836355b88Sratchov 	}
21936355b88Sratchov 
22087bc9f6aSratchov 	o = xmalloc(sizeof(struct opt));
221869641a9Sratchov 	o->num = num;
22236355b88Sratchov 	o->alt_first = o->dev = a;
22336355b88Sratchov 	o->refcnt = 0;
2245fb8f3c9Sratchov 
2255fb8f3c9Sratchov 	/*
2265fb8f3c9Sratchov 	 * XXX: below, we allocate a midi input buffer, since we don't
2275fb8f3c9Sratchov 	 *	receive raw midi data, so no need to allocate a input
2285fb8f3c9Sratchov 	 *	ibuf.  Possibly set imsg & fill callbacks to NULL and
2295fb8f3c9Sratchov 	 *	use this to in midi_new() to check if buffers need to be
2305fb8f3c9Sratchov 	 *	allocated
2315fb8f3c9Sratchov 	 */
2325fb8f3c9Sratchov 	o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
2335fb8f3c9Sratchov 	midi_tag(o->midi, o->num);
2345fb8f3c9Sratchov 
23587bc9f6aSratchov 	if (mode & MODE_PLAY) {
23687bc9f6aSratchov 		o->pmin = pmin;
23787bc9f6aSratchov 		o->pmax = pmax;
23887bc9f6aSratchov 	}
23987bc9f6aSratchov 	if (mode & MODE_RECMASK) {
24087bc9f6aSratchov 		o->rmin = rmin;
24187bc9f6aSratchov 		o->rmax = rmax;
24287bc9f6aSratchov 	}
24387bc9f6aSratchov 	o->maxweight = maxweight;
2440e6be583Sratchov 	o->mtc = mmc ? &mtc_array[0] : NULL;
24587bc9f6aSratchov 	o->dup = dup;
24687bc9f6aSratchov 	o->mode = mode;
24787bc9f6aSratchov 	memcpy(o->name, name, len + 1);
248869641a9Sratchov 	o->next = *po;
249869641a9Sratchov 	*po = o;
250*7b639200Sratchov 
251*7b639200Sratchov 	logx(2, "%s: %s%s, vol = %d", o->name, (chans_fmt(str, sizeof(str),
252*7b639200Sratchov 	    o->mode, o->pmin, o->pmax, o->rmin, o->rmax), str),
253*7b639200Sratchov 	    (o->dup) ? ", dup" : "", o->maxweight);
254*7b639200Sratchov 
25587bc9f6aSratchov 	return o;
25687bc9f6aSratchov }
25787bc9f6aSratchov 
25887bc9f6aSratchov struct opt *
25936355b88Sratchov opt_byname(char *name)
26087bc9f6aSratchov {
26187bc9f6aSratchov 	struct opt *o;
26287bc9f6aSratchov 
2635684d550Sratchov 	for (o = opt_list; o != NULL; o = o->next) {
26487bc9f6aSratchov 		if (strcmp(name, o->name) == 0)
26587bc9f6aSratchov 			return o;
26687bc9f6aSratchov 	}
26787bc9f6aSratchov 	return NULL;
26887bc9f6aSratchov }
26987bc9f6aSratchov 
27036355b88Sratchov struct opt *
27136355b88Sratchov opt_bynum(int num)
27236355b88Sratchov {
27336355b88Sratchov 	struct opt *o;
27436355b88Sratchov 
27536355b88Sratchov 	for (o = opt_list; o != NULL; o = o->next) {
27636355b88Sratchov 		if (o->num == num)
27736355b88Sratchov 			return o;
27836355b88Sratchov 	}
27936355b88Sratchov 	return NULL;
28036355b88Sratchov }
28136355b88Sratchov 
28287bc9f6aSratchov void
2835684d550Sratchov opt_del(struct opt *o)
28487bc9f6aSratchov {
28587bc9f6aSratchov 	struct opt **po;
28687bc9f6aSratchov 
2875684d550Sratchov 	for (po = &opt_list; *po != o; po = &(*po)->next) {
28887bc9f6aSratchov #ifdef DEBUG
28987bc9f6aSratchov 		if (*po == NULL) {
290*7b639200Sratchov 			logx(0, "%s: not on list", __func__);
29187bc9f6aSratchov 			panic();
29287bc9f6aSratchov 		}
29387bc9f6aSratchov #endif
29487bc9f6aSratchov 	}
2955fb8f3c9Sratchov 	midi_del(o->midi);
29687bc9f6aSratchov 	*po = o->next;
29787bc9f6aSratchov 	xfree(o);
29887bc9f6aSratchov }
29936355b88Sratchov 
30036355b88Sratchov void
30136355b88Sratchov opt_init(struct opt *o)
30236355b88Sratchov {
30336355b88Sratchov }
30436355b88Sratchov 
30536355b88Sratchov void
30636355b88Sratchov opt_done(struct opt *o)
30736355b88Sratchov {
30836355b88Sratchov 	struct dev *d;
30936355b88Sratchov 
31036355b88Sratchov 	if (o->refcnt != 0) {
31136355b88Sratchov 		// XXX: all clients are already kicked, so this never happens
312*7b639200Sratchov 		logx(0, "%s: still has refs", o->name);
31336355b88Sratchov 	}
31436355b88Sratchov 	for (d = dev_list; d != NULL; d = d->next)
31536355b88Sratchov 		ctl_del(CTL_OPT_DEV, o, d);
31636355b88Sratchov }
31736355b88Sratchov 
31836355b88Sratchov /*
31936355b88Sratchov  * Set opt's device, and (if necessary) move clients to
32036355b88Sratchov  * to the new device
32136355b88Sratchov  */
322786dc82eSratchov int
32336355b88Sratchov opt_setdev(struct opt *o, struct dev *ndev)
32436355b88Sratchov {
32536355b88Sratchov 	struct dev *odev;
32636355b88Sratchov 	struct ctl *c;
32736355b88Sratchov 	struct ctlslot *p;
32836355b88Sratchov 	struct slot *s;
32936355b88Sratchov 	int i;
33036355b88Sratchov 
33136355b88Sratchov 	if (!dev_ref(ndev))
332786dc82eSratchov 		return 0;
33336355b88Sratchov 
33436355b88Sratchov 	odev = o->dev;
33536355b88Sratchov 	if (odev == ndev) {
33636355b88Sratchov 		dev_unref(ndev);
337786dc82eSratchov 		return 1;
33836355b88Sratchov 	}
33936355b88Sratchov 
34036355b88Sratchov 	/* check if clients can use new device */
34136355b88Sratchov 	for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
34236355b88Sratchov 		if (s->opt != o)
34336355b88Sratchov 			continue;
34436355b88Sratchov 		if (s->ops != NULL && !dev_iscompat(odev, ndev)) {
34536355b88Sratchov 			dev_unref(ndev);
346786dc82eSratchov 			return 0;
34736355b88Sratchov 		}
34836355b88Sratchov 	}
34936355b88Sratchov 
35036355b88Sratchov 	/*
35136355b88Sratchov 	 * if we're using MMC, move all opts to the new device, mtc_setdev()
35236355b88Sratchov 	 * will call us back
353786dc82eSratchov 	 *
354786dc82eSratchov 	 * XXX: move this to the end to avoid the recursion
35536355b88Sratchov 	 */
35636355b88Sratchov 	if (o->mtc != NULL && o->mtc->dev != ndev) {
35736355b88Sratchov 		mtc_setdev(o->mtc, ndev);
35836355b88Sratchov 		dev_unref(ndev);
359786dc82eSratchov 		return 1;
36036355b88Sratchov 	}
36136355b88Sratchov 
36236355b88Sratchov 	c = ctl_find(CTL_OPT_DEV, o, o->dev);
36336355b88Sratchov 	if (c != NULL)
36436355b88Sratchov 		c->curval = 0;
36536355b88Sratchov 
36636355b88Sratchov 	/* detach clients from old device */
36736355b88Sratchov 	for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
36836355b88Sratchov 		if (s->opt != o)
36936355b88Sratchov 			continue;
37036355b88Sratchov 
37136355b88Sratchov 		if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
37236355b88Sratchov 			slot_detach(s);
37336355b88Sratchov 	}
37436355b88Sratchov 
37536355b88Sratchov 	o->dev = ndev;
37636355b88Sratchov 
37736355b88Sratchov 	if (o->refcnt > 0) {
37836355b88Sratchov 		dev_unref(odev);
37936355b88Sratchov 		dev_ref(o->dev);
38036355b88Sratchov 	}
38136355b88Sratchov 
38236355b88Sratchov 	c = ctl_find(CTL_OPT_DEV, o, o->dev);
38336355b88Sratchov 	if (c != NULL) {
38436355b88Sratchov 		c->curval = 1;
38536355b88Sratchov 		c->val_mask = ~0;
38636355b88Sratchov 	}
38736355b88Sratchov 
38836355b88Sratchov 	/* attach clients to new device */
38936355b88Sratchov 	for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
39036355b88Sratchov 		if (s->opt != o)
39136355b88Sratchov 			continue;
39236355b88Sratchov 
39336355b88Sratchov 		if (ndev != odev) {
39436355b88Sratchov 			dev_midi_slotdesc(odev, s);
39536355b88Sratchov 			dev_midi_slotdesc(ndev, s);
39636355b88Sratchov 			dev_midi_vol(ndev, s);
39736355b88Sratchov 		}
39836355b88Sratchov 
39936355b88Sratchov 		c = ctl_find(CTL_SLOT_LEVEL, s, NULL);
40036355b88Sratchov 		ctl_update(c);
40136355b88Sratchov 
40236355b88Sratchov 		if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) {
40336355b88Sratchov 			slot_initconv(s);
40436355b88Sratchov 			slot_attach(s);
40536355b88Sratchov 		}
40636355b88Sratchov 	}
40736355b88Sratchov 
40836355b88Sratchov 	/* move controlling clients to new device */
40936355b88Sratchov 	for (p = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, p++) {
41036355b88Sratchov 		if (p->ops == NULL)
41136355b88Sratchov 			continue;
41236355b88Sratchov 		if (p->opt == o)
41336355b88Sratchov 			ctlslot_update(p);
41436355b88Sratchov 	}
41536355b88Sratchov 
41636355b88Sratchov 	dev_unref(ndev);
417786dc82eSratchov 	return 1;
41836355b88Sratchov }
41936355b88Sratchov 
42036355b88Sratchov /*
42136355b88Sratchov  * Get a reference to opt's device
42236355b88Sratchov  */
42336355b88Sratchov struct dev *
42436355b88Sratchov opt_ref(struct opt *o)
42536355b88Sratchov {
42636355b88Sratchov 	struct dev *d;
42736355b88Sratchov 
42836355b88Sratchov 	if (o->refcnt == 0) {
42936355b88Sratchov 		if (strcmp(o->name, o->dev->name) == 0) {
43036355b88Sratchov 			if (!dev_ref(o->dev))
43136355b88Sratchov 				return NULL;
43236355b88Sratchov 		} else {
43336355b88Sratchov 			/* find first working one */
43436355b88Sratchov 			d = o->alt_first;
43536355b88Sratchov 			while (1) {
43636355b88Sratchov 				if (dev_ref(d))
43736355b88Sratchov 					break;
43836355b88Sratchov 				d = d->alt_next;
43936355b88Sratchov 				if (d == o->alt_first)
44036355b88Sratchov 					return NULL;
44136355b88Sratchov 			}
44236355b88Sratchov 
44336355b88Sratchov 			/* if device changed, move everything to the new one */
44436355b88Sratchov 			if (d != o->dev)
44536355b88Sratchov 				opt_setdev(o, d);
446ad73c565Sratchov 
447ad73c565Sratchov 			/* create server.device control */
448ad73c565Sratchov 			for (d = dev_list; d != NULL; d = d->next) {
449ad73c565Sratchov 				d->refcnt++;
450ad73c565Sratchov 				if (d->pstate == DEV_CFG)
451ad73c565Sratchov 					dev_open(d);
452ad73c565Sratchov 				ctl_new(CTL_OPT_DEV, o, d,
453ad73c565Sratchov 				    CTL_SEL, dev_getdisplay(d),
454ad73c565Sratchov 				    o->name, "server", -1, "device",
455ad73c565Sratchov 				    d->name, -1, 1, o->dev == d);
456ad73c565Sratchov 			}
45736355b88Sratchov 		}
45836355b88Sratchov 	}
45936355b88Sratchov 
46036355b88Sratchov 	o->refcnt++;
46136355b88Sratchov 	return o->dev;
46236355b88Sratchov }
46336355b88Sratchov 
46436355b88Sratchov /*
46536355b88Sratchov  * Release opt's device
46636355b88Sratchov  */
46736355b88Sratchov void
46836355b88Sratchov opt_unref(struct opt *o)
46936355b88Sratchov {
470ad73c565Sratchov 	struct dev *d;
471ad73c565Sratchov 
47236355b88Sratchov 	o->refcnt--;
473ad73c565Sratchov 	if (o->refcnt == 0) {
474ad73c565Sratchov 		/* delete server.device control */
475ad73c565Sratchov 		for (d = dev_list; d != NULL; d = d->next) {
476ad73c565Sratchov 			if (ctl_del(CTL_OPT_DEV, o, d))
477ad73c565Sratchov 				dev_unref(d);
478ad73c565Sratchov 		}
47936355b88Sratchov 		dev_unref(o->dev);
48036355b88Sratchov 	}
481ad73c565Sratchov }
482