[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.
This commit is contained in:
Ariff Abdullah 2007-03-16 17:19:03 +00:00
parent 1042342ad8
commit 7ab4fa8ee2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=167649
3 changed files with 160 additions and 14 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}