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