From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: * X-Spam-Status: No, score=1.0 required=5.0 tests=DATE_IN_PAST_12_24, DKIM_INVALID,DKIM_SIGNED autolearn=no autolearn_force=no version=3.4.4 Received: (qmail 7814 invoked from network); 19 Nov 2022 03:49:23 -0000 Received: from 9front.inri.net (168.235.81.73) by inbox.vuxu.org with ESMTPUTF8; 19 Nov 2022 03:49:23 -0000 MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit Received: from mail-pj1-f41.google.com ([209.85.216.41]) by 9front; Fri Nov 18 22:39:07 -0500 2022 Received: by mail-pj1-f41.google.com with SMTP id u6-20020a17090a5e4600b0021881a8d264so4430968pji.4 for <9front@9front.org>; Fri, 18 Nov 2022 19:39:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mforney.org; s=google; h=to:subject:date:from:references:in-reply-to:message-id:from:to:cc :subject:date:message-id:reply-to; bh=s+LE3VzoGAnzl+i14htIosiCfZtwjaN3PWbbipw0BHc=; b=HiVR7A1fmzfZngP9Ej7oE1lDcaAVbHd1jZiMaI+0aLv0RWbAA+Zfjr/p4UEsHBq2eY 2w4mhcA6KEdiVuwNq6v6LObinBtqpVjOMZCh4ebI5sMWUIRyN3WKgMdtBvlf+qUHq0Np eLRSuIX1q88QH/6nJU6s3FMskzlis3Q4tbtR8vt4OKVy+c++Q0+FD0i4bmYkOUgQFXbx 2BtOcUm+m0PrqEP35Ig2dSw0pGPDgBEiAZnVLBsNln6Pp3xRXwpvyeQxsAackilqcTxU qHDw+3NVX5XJ7QufUVocVd9m53NK4jae1NV2DlsXqhr2WTvRnBj1lGUOo8fSJlV/6jsO fYFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:date:from:references:in-reply-to:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=s+LE3VzoGAnzl+i14htIosiCfZtwjaN3PWbbipw0BHc=; b=3bV4eIgt13Jw1xArsi9haT1fNC1xuQmvrmZ7d0QCwPSMcivHxwHLkHo9o+pTXyycb7 G7CW8Lz5BnccO8R+lXh3C+r1ZlVuczXcp+ARCR2AHQuELpqDY19XvdUHeEaj8QKxEqjr DnZoNqsyvXO3ytwIUbdeL6B6ld4JkVsExandz+2DAf1D67EmJ+/IFcoVBsyf3ARDcsNA xMQ52BRyrIHDa4qvH87q9rfg3hdqteewEZ8adciwslMbTfzNJk73feMpmTNKbuZcWnVA RIOGe3k9NWGjirLkUnkZv2rs6z38/bbx7zXad4lCq4yxomvwEiq9VLW/qM+Vf0M4srEO EN9A== X-Gm-Message-State: ANoB5pm/qMtavsb1Rmw6a4fVvHiJwIJ1hjw8ptBoBbokfJbUvpA3kQh0 V9d2gZg92ig5lhwOmj7DqlhuRYnD07bFieB5OxI= X-Google-Smtp-Source: AA0mqf7oUdalKIQCJY89GtlPVfJFGKHVzyJ10VIcaXiYaq6hzaB22MNHZxyFfwqLei5P+z0pt8MjUg== X-Received: by 2002:a17:902:aa0c:b0:180:4030:1c7d with SMTP id be12-20020a170902aa0c00b0018040301c7dmr2365034plb.99.1668829143650; Fri, 18 Nov 2022 19:39:03 -0800 (PST) Return-Path: Received: from localhost ([2601:647:6400:20b0:cab2:9bff:fe88:d09c]) by smtp.gmail.com with ESMTPSA id c19-20020a631c53000000b00476e77b0f07sm3503215pgm.38.2022.11.18.19.39.02 for <9front@9front.org> (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Fri, 18 Nov 2022 19:39:02 -0800 (PST) Message-Id: In-Reply-To: References: From: Michael Forney Date: Fri, 18 Nov 2022 09:21:12 +0000 To: 9front@9front.org List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: scripting-scale blockchain CSS-oriented factory interface Subject: [9front] [PATCH 4/5] nusb/audio: enumerate streams through control interface Reply-To: 9front@9front.org Precedence: bulk USB audio 2.0 splits the sample rate control from the AudioStreaming alternate settings to a clock entity connected to input/output terminals associated with the AudioStreaming interfaces. So, in order to pair endpoints with their corresponding clock, we must have a better picture of the audio function topology. The AudioControl interface tells us which streams are available, so we can go through each one, determining the possible stream parameters and locating the associated endpoint. This will give us the necessary context to locate the corresponding terminal (and soon, clock). When we go to setup input/output endpoints, we now only have to consider those that we have selected earlier. --- sys/src/cmd/nusb/audio/audio.c | 193 ++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 54 deletions(-) diff --git a/sys/src/cmd/nusb/audio/audio.c b/sys/src/cmd/nusb/audio/audio.c index 09b84b8c48..2cd75f6361 100644 --- a/sys/src/cmd/nusb/audio/audio.c +++ b/sys/src/cmd/nusb/audio/audio.c @@ -19,16 +19,21 @@ enum { typedef struct Range Range; struct Range { - Range *next; - int min; - int max; + uint min; + uint max; }; typedef struct Aconf Aconf; struct Aconf { + Ep *ep; + int bits; + int bps; /* subslot size (bytes per sample) */ + int format; + int channels; + int controls; Range *freq; - int caps; + int nfreq; }; int audiodelay = 1764; /* 40 ms */ @@ -39,62 +44,131 @@ int audiores = 16; char user[] = "audio"; Dev *audiodev = nil; - +Iface *audiocontrol = nil; Ep *audioepin = nil; Ep *audioepout = nil; -void -parsedescr(Desc *dd) +Iface* +findiface(Conf *conf, int class, int subclass, int id) { - Aconf *c; - Range *f; - uchar *b; int i; + Iface *iface; - if(dd == nil || dd->iface == nil) - return; - if(Subclass(dd->iface->csp) != 2) - return; + for(i = 0; i < nelem(conf->iface); i++){ + iface = conf->iface[i]; + if(iface == nil || Class(iface->csp) != class || Subclass(iface->csp) != subclass) + continue; + if(id == -1 || iface->id == id) + return iface; + } + return nil; +} + +Desc* +findacheader(Usbdev *u, Iface *ac) +{ + Desc *dd; + uchar *b; + int i; - c = dd->iface->aux; - if(c == nil){ - c = mallocz(sizeof(*c), 1); - dd->iface->aux = c; + for(i = 0; i < nelem(u->ddesc); i++){ + dd = u->ddesc[i]; + if(dd == nil || dd->iface != ac || dd->data.bDescriptorType != 0x24) + continue; + if(dd->data.bLength < 8 || dd->data.bbytes[0] != 1) + continue; + b = dd->data.bbytes; + if(dd->data.bLength == 8+b[5]) + return dd; } + return nil; +} + +void +parseasdesc(Desc *dd, Aconf *c) +{ + uchar *b; + Range *f; - b = (uchar*)&dd->data; - switch(b[1]<<8 | b[2]){ + b = dd->data.bbytes; + switch(dd->data.bDescriptorType<<8 | b[0]){ case 0x2501: /* CS_ENDPOINT, EP_GENERAL */ - c->caps |= b[3]; + if(dd->data.bLength != 7) + return; + c->controls = b[1]; break; - case 0x2402: /* CS_INTERFACE, FORMAT_TYPE */ - if(b[4] != audiochan) - break; - if(b[6] != audiores) - break; + case 0x2401: /* CS_INTERFACE, AS_GENERAL */ + if(dd->data.bLength != 7) + return; + c->format = GET2(&b[3]); + break; - if(b[7] == 0){ - f = mallocz(sizeof(*f), 1); - f->min = b[8] | b[9]<<8 | b[10]<<16; - f->max = b[11] | b[12]<<8 | b[13]<<16; - - f->next = c->freq; - c->freq = f; - } else { - for(i=0; imin = b[8+3*i] | b[9+3*i]<<8 | b[10+3*i]<<16; + case 0x2402: /* CS_INTERFACE, FORMAT_TYPE */ + if(dd->data.bLength < 8 || b[1] != 1) + return; + c->channels = b[2]; + c->bps = b[3]; + c->bits = b[4]; + if(b[5] == 0){ /* continuous frequency range */ + c->nfreq = 1; + c->freq = emallocz(sizeof(*f), 1); + c->freq->min = b[6] | b[7]<<8 | b[8]<<16; + c->freq->max = b[9] | b[10]<<8 | b[11]<<16; + }else{ /* discrete sampling frequencies */ + c->nfreq = b[5]; + c->freq = emallocz(c->nfreq * sizeof(*f), 1); + b += 6; + for(f = c->freq; f < c->freq+c->nfreq; f++, b += 3){ + f->min = b[0] | b[1]<<8 | b[2]<<16; f->max = f->min; - - f->next = c->freq; - c->freq = f; } } break; } } +void +parsestream(Dev *d, int id) +{ + Iface *as; + Desc *dd; + Ep *e; + Aconf *c; + int i; + + /* find AS interface */ + as = findiface(d->usb->conf[0], Claudio, 2, id); + + /* enumerate through alt. settings */ + for(; as != nil; as = as->next){ + c = emallocz(sizeof(*c), 1); + as->aux = c; + + /* find AS endpoint */ + for(i = 0; i < nelem(as->ep); i++){ + e = as->ep[i]; + if(e != nil && e->type == Eiso && (e->attrib>>4 & 3) == Edata){ + c->ep = e; + break; + } + } + if(c->ep == nil){ + free(c); + as->aux = nil; + continue; + } + + /* parse AS descriptors */ + for(i = 0; i < nelem(d->usb->ddesc); i++){ + dd = d->usb->ddesc[i]; + if(dd == nil || dd->iface != as) + continue; + parseasdesc(dd, c); + } + } +} + Dev* setupep(Dev *d, Ep *e, int speed) { @@ -103,12 +177,12 @@ setupep(Dev *d, Ep *e, int speed) Range *f; for(;e != nil; e = e->next){ - if(e->dir!=dir) - continue; c = e->iface->aux; - if(c == nil) + if(c == nil || e != c->ep || e->dir != dir) continue; - for(f = c->freq; f != nil; f = f->next) + if(c->format != 1 || c->bits != audiores || 8*c->bps != audiores || c->channels != audiochan) + continue; + for(f = c->freq; f != c->freq+c->nfreq; f++) if(speed >= f->min && speed <= f->max) goto Foundaltc; } @@ -119,7 +193,7 @@ Foundaltc: if(setalt(d, e->iface) < 0) return nil; - if(c->caps & 1){ + if(c->controls & 1){ uchar b[4]; b[0] = speed; @@ -207,6 +281,10 @@ main(int argc, char *argv[]) { char buf[32]; Dev *d, *ed; + Desc *dd; + Conf *conf; + Iface *ac; + Aconf *c; Ep *e; int i; @@ -226,15 +304,22 @@ main(int argc, char *argv[]) sysfatal("getdev: %r"); audiodev = d; - /* parse descriptors, mark valid altc */ - for(i = 0; i < nelem(d->usb->ddesc); i++) - parsedescr(d->usb->ddesc[i]); + conf = d->usb->conf[0]; + ac = findiface(conf, Claudio, 1, -1); + if(ac == nil) + sysfatal("no audio control interface"); + audiocontrol = ac; + + dd = findacheader(d->usb, ac); + if(dd == nil) + sysfatal("no audio control header"); + for(i = 6; i < dd->data.bLength-2; i++) + parsestream(d, dd->data.bbytes[i]); + for(i = 0; i < nelem(d->usb->ep); i++){ - e = d->usb->ep[i]; - if(e == nil || e->type != Eiso || e->iface->csp != CSP(Claudio, 2, 0)) - continue; - for(; e != nil; e = e->next){ - if((e->attrib>>4 & 3) == Edata) + for(e = d->usb->ep[i]; e != nil; e = e->next){ + c = e->iface->aux; + if(c != nil && c->ep == e) break; } if(e == nil) @@ -263,7 +348,7 @@ main(int argc, char *argv[]) closedev(ed); } if(audioepout == nil) - sysfatal("no endpoints found"); + sysfatal("no output stream found"); fs.tree = alloctree(user, "usb", DMDIR|0555, nil); createfile(fs.tree->root, "volume", user, 0666, nil); -- 2.37.3