From 7ab4fa8ee22aa3fbed16fa1190c2063b5bc426f1 Mon Sep 17 00:00:00 2001 From: Ariff Abdullah Date: Fri, 16 Mar 2007 17:19:03 +0000 Subject: [PATCH] [stage: 9/9] - SWAPLR quirk for (unknown, luckily it is mine) broken uaudio stick. Fixing by rewiring is impossible without damaging it. Luckily, we can fix it using "other" methods :) . - Add uaudio_get_vendor(), _product() and _release() in uaudio.c (currently used by uaudio_pcm quirk). - Implement CHANNEL_SETFRAGMENTS(). - Drop channel locking in few places where it is about to sleep somewhere. This should help eliminating illegal locking acquisition where the current thread is about to sleep, and also few deadlock cases. Dropping it right here is quite safe since it is already protected by CHN_F_BUSY flag and other threads won't bother to touch it. Solving other illegal locking issues are quite tricky without converting most usbd_do_request() calls to its equivalent _async() calls, which I intend to do it later after getting full test report from other people with different uaudio hardwares. - Fix memory leak issues during detach. This seems common to any drivers (notably emu10kx, csapcm?) with bridge functions. --- sys/dev/sound/usb/uaudio.c | 92 +++++++++++++++++++++++++++++++--- sys/dev/sound/usb/uaudio.h | 3 ++ sys/dev/sound/usb/uaudio_pcm.c | 79 ++++++++++++++++++++++++++--- 3 files changed, 160 insertions(+), 14 deletions(-) diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c index 4f7819f316c2..7e4bb52bb21f 100644 --- a/sys/dev/sound/usb/uaudio.c +++ b/sys/dev/sound/usb/uaudio.c @@ -251,6 +251,9 @@ struct uaudio_softc { #if defined(__FreeBSD__) struct sbuf uaudio_sndstat; int uaudio_sndstat_flag; + int sc_vendor; + int sc_product; + int sc_release; #endif }; @@ -532,6 +535,9 @@ USB_ATTACH(uaudio) #endif sc->sc_udev = uaa->device; + sc->sc_vendor = uaa->vendor; + sc->sc_product = uaa->product; + sc->sc_release = uaa->release; cdesc = usbd_get_config_descriptor(sc->sc_udev); if (cdesc == NULL) { @@ -659,6 +665,10 @@ uaudio_detach(device_t self, int flags) USB_DETACH(uaudio) { + struct sndcard_func *func; + device_t *devlist = NULL; + int err, i, devcount; + USB_DETACH_START(uaudio, sc); sbuf_delete(&(sc->uaudio_sndstat)); @@ -671,8 +681,24 @@ USB_DETACH(uaudio) usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); #endif - /* do nothing ? */ - return bus_generic_detach(sc->sc_dev); + err = bus_generic_detach(sc->sc_dev); + + if (err == 0) { + device_get_children(sc->sc_dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + func = device_get_ivars(devlist[i]); + if (func != NULL && func->func == SCF_PCM && + func->varinfo == NULL) { + device_set_ivars(devlist[i], NULL); + free(func, M_DEVBUF); + device_delete_child(sc->sc_dev, devlist[i]); + } + } + if (devlist != NULL) + free(devlist, M_TEMP); + } + + return err; } #endif @@ -3011,6 +3037,9 @@ uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch) { struct as_info *as; int endpt; +#if defined(__FreeBSD__) + int locked; +#endif usbd_status err; #if defined(__FreeBSD__) @@ -3023,8 +3052,17 @@ uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch) DPRINTF(("uaudio_chan_open: endpt=0x%02x, speed=%d, alt=%d\n", endpt, ch->sample_rate, as->alt)); +#if defined(__FreeBSD__) + locked = (ch->pcm_ch != NULL && mtx_owned(ch->pcm_ch->lock)) ? 1 : 0; + if (locked) + CHN_UNLOCK(ch->pcm_ch); +#endif /* Set alternate interface corresponding to the mode. */ err = usbd_set_interface(as->ifaceh, as->alt); +#if defined(__FreeBSD__) + if (locked) + CHN_LOCK(ch->pcm_ch); +#endif if (err) return err; @@ -3059,14 +3097,20 @@ static void uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch) { struct as_info *as; - #if defined(__FreeBSD__) + int locked; + if (sc->sc_dying) return ; #endif as = &sc->sc_alts[ch->altidx]; as->sc_busy = 0; +#if defined(__FreeBSD__) + locked = (ch->pcm_ch != NULL && mtx_owned(ch->pcm_ch->lock)) ? 1 : 0; + if (locked) + CHN_UNLOCK(ch->pcm_ch); +#endif if (sc->sc_nullalt >= 0) { DPRINTF(("uaudio_chan_close: set null alt=%d\n", sc->sc_nullalt)); @@ -3080,6 +3124,10 @@ uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch) usbd_abort_pipe(ch->sync_pipe); usbd_close_pipe(ch->sync_pipe); } +#if defined(__FreeBSD__) + if (locked) + CHN_LOCK(ch->pcm_ch); +#endif } static usbd_status @@ -4534,7 +4582,40 @@ uaudio_sndstat_register(device_t dev) struct snddev_info *d = device_get_softc(dev); sndstat_register(dev, d->status, uaudio_sndstat_prepare_pcm); } - + +int +uaudio_get_vendor(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + + if (sc == NULL) + return 0; + + return sc->sc_vendor; +} + +int +uaudio_get_product(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + + if (sc == NULL) + return 0; + + return sc->sc_product; +} + +int +uaudio_get_release(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + + if (sc == NULL) + return 0; + + return sc->sc_release; +} + static int audio_attach_mi(device_t dev) { @@ -4543,10 +4624,9 @@ audio_attach_mi(device_t dev) /* Attach the children. */ /* PCM Audio */ - func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) return (ENOMEM); - bzero(func, sizeof(*func)); func->func = SCF_PCM; child = device_add_child(dev, "pcm", -1); device_set_ivars(child, func); diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h index 5ce74ab2c8d1..b73f7a559e25 100644 --- a/sys/dev/sound/usb/uaudio.h +++ b/sys/dev/sound/usb/uaudio.h @@ -51,3 +51,6 @@ u_int32_t uaudio_query_mix_info(device_t dev); u_int32_t uaudio_query_recsrc_info(device_t dev); unsigned uaudio_query_formats(device_t dev, int dir, unsigned maxfmt, struct pcmchan_caps *fmt); void uaudio_sndstat_register(device_t dev); +int uaudio_get_vendor(device_t dev); +int uaudio_get_product(device_t dev); +int uaudio_get_release(device_t dev); diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c index b81d94ec7589..19588a14a17d 100644 --- a/sys/dev/sound/usb/uaudio_pcm.c +++ b/sys/dev/sound/usb/uaudio_pcm.c @@ -54,10 +54,20 @@ struct ua_info { u_int32_t ua_recfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */ struct pcmchan_caps ua_playcaps; struct pcmchan_caps ua_reccaps; + int vendor, product, release; }; #define UAUDIO_DEFAULT_BUFSZ 16*1024 +static const struct { + int vendor; + int product; + int release; + uint32_t dflags; +} ua_quirks[] = { + { 0x1130, 0xf211, 0x0101, SD_F_PSWAPLR }, +}; + /************************************************************/ static void * ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) @@ -144,27 +154,48 @@ ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) } static int -ua_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +ua_chan_setfragments(kobj_t obj, void *data, u_int32_t blksz, u_int32_t blkcnt) { device_t pa_dev; struct ua_chinfo *ch = data; struct ua_info *ua = ch->parent; - u_int32_t blkcnt; - RANGE(blocksize, 128, ua->bufsz / 2); - blkcnt = ua->bufsz / blocksize; + RANGE(blksz, 128, sndbuf_getmaxsize(ch->buffer) / 2); + RANGE(blkcnt, 2, 512); - if ((sndbuf_getblksz(ch->buffer) != blocksize || + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) { + if ((blkcnt >> 1) >= 2) + blkcnt >>= 1; + else if ((blksz >> 1) >= 128) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->buffer) != blksz || sndbuf_getblkcnt(ch->buffer) != blkcnt) && - sndbuf_resize(ch->buffer, blkcnt, blocksize) != 0) + sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) device_printf(ua->sc_dev, "%s: failed blksz=%u blkcnt=%u\n", - __func__, blocksize, blkcnt); + __func__, blksz, blkcnt); ch->blksz = sndbuf_getblksz(ch->buffer); pa_dev = device_get_parent(ua->sc_dev); + uaudio_chan_set_param_pcm_dma_buff(pa_dev, ch->buf, + ch->buf + sndbuf_getsize(ch->buffer), ch->channel, ch->dir); uaudio_chan_set_param_blocksize(pa_dev, ch->blksz, ch->dir); + return 1; +} + +static int +ua_chan_setblocksize(kobj_t obj, void *data, u_int32_t blksz) +{ + struct ua_chinfo *ch = data; + + ua_chan_setfragments(obj, data, blksz, + sndbuf_getmaxsize(ch->buffer) / blksz); + return ch->blksz; } @@ -228,6 +259,7 @@ static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_setformat, ua_chan_setformat), KOBJMETHOD(channel_setspeed, ua_chan_setspeed), KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize), + KOBJMETHOD(channel_setfragments, ua_chan_setfragments), KOBJMETHOD(channel_trigger, ua_chan_trigger), KOBJMETHOD(channel_getptr, ua_chan_getptr), KOBJMETHOD(channel_getcaps, ua_chan_getcaps), @@ -327,6 +359,8 @@ static int ua_attach(device_t dev) { struct ua_info *ua; + struct sndcard_func *func; + struct snddev_info *d; char status[SND_STATUSLEN]; device_t pa_dev; u_int32_t nplay, nrec; @@ -338,7 +372,21 @@ ua_attach(device_t dev) ua->sc_dev = dev; + /* Mark for existence */ + func = device_get_ivars(dev); + if (func != NULL) + func->varinfo = (void *)ua; + pa_dev = device_get_parent(dev); + ua->vendor = uaudio_get_vendor(pa_dev); + ua->product = uaudio_get_product(pa_dev); + ua->release = uaudio_get_release(pa_dev); + + if (bootverbose) + device_printf(dev, + "USB Audio: " + "vendor=0x%04x, product=0x%04x, release=0x%04x\n", + ua->vendor, ua->product, ua->release); ua->bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_DEFAULT_BUFSZ, 65536); if (bootverbose) @@ -360,6 +408,15 @@ ua_attach(device_t dev) if (nrec > 1) nrec = 1; + d = device_get_softc(dev); + for (i = 0; d != NULL && + i < (sizeof(ua_quirks) / sizeof(ua_quirks[0])); i++) { + if (ua->vendor == ua_quirks[i].vendor && + ua->product == ua_quirks[i].product && + ua->release == ua_quirks[i].release) + d->flags |= ua_quirks[i].dflags; + } + #ifndef NO_RECORDING if (pcm_register(dev, ua, nplay, nrec)) { #else @@ -390,8 +447,9 @@ bad: free(ua, M_DEVBUF); static int ua_detach(device_t dev) { - int r; struct ua_info *sc; + struct sndcard_func *func; + int r; r = pcm_unregister(dev); if (r) @@ -400,6 +458,11 @@ ua_detach(device_t dev) sc = pcm_getdevinfo(dev); free(sc, M_DEVBUF); + /* Mark for deletion */ + func = device_get_ivars(dev); + if (func != NULL) + func->varinfo = NULL; + return 0; }