Date: Sun, 20 Aug 2006 07:57:20 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 104584 for review Message-ID: <200608200757.k7K7vKvM078486@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=104584 Change 104584 by hselasky@hselasky_mini_itx on 2006/08/20 07:57:10 Added support for USB MIDI cables to uaudio. Affected files ... .. //depot/projects/usb/src/sys/dev/sound/usb/uaudio.c#3 edit Differences ... ==== //depot/projects/usb/src/sys/dev/sound/usb/uaudio.c#3 (text+ko) ==== @@ -164,11 +164,58 @@ u_int8_t iface_alt_index; }; +#define UMIDI_N_TRANSFER 4 /* units */ +#define UMIDI_CABLES_MAX 16 /* units */ +#define UMIDI_BULK_SIZE 1024 /* bytes */ + +struct umidi_sub_chan { + struct usb_cdev cdev; + u_int8_t * temp_cmd; + u_int8_t temp_0[4]; + u_int8_t temp_1[4]; + u_int8_t state; +#define UMIDI_ST_UNKNOWN 0 /* scan for command */ +#define UMIDI_ST_1PARAM 1 +#define UMIDI_ST_2PARAM_1 2 +#define UMIDI_ST_2PARAM_2 3 +#define UMIDI_ST_SYSEX_0 4 +#define UMIDI_ST_SYSEX_1 5 +#define UMIDI_ST_SYSEX_2 6 + + u_int8_t read_open : 1; + u_int8_t write_open : 1; + u_int8_t unused : 6; +}; + +struct umidi_chan { + + struct umidi_sub_chan sub[UMIDI_CABLES_MAX]; + struct usbd_memory_wait mem_wait; + struct mtx mtx; + + struct usbd_xfer *xfer[UMIDI_N_TRANSFER]; + + u_int8_t iface_index; + u_int8_t iface_alt_index; + + u_int8_t flags; +#define UMIDI_FLAG_READ_STALL 0x01 +#define UMIDI_FLAG_WRITE_STALL 0x02 + + u_int8_t read_open_refcount; + u_int8_t write_open_refcount; + + u_int8_t curr_cable; + u_int8_t max_cable; + u_int8_t valid; +}; + struct uaudio_softc { struct sbuf sc_sndstat; struct sndcard_func sc_sndcard_func; struct uaudio_chan sc_rec_chan; struct uaudio_chan sc_play_chan; + struct umidi_chan sc_midi_chan; struct usbd_memory_wait sc_mixer_mem; struct usbd_device * sc_udev; @@ -373,7 +420,55 @@ static void uaudio_mixer_init(struct uaudio_softc *sc); -static const struct usbd_config uaudio_cfg_record_full_speed[UAUDIO_NCHANBUFS] = { +static void +umidi_read_clear_stall_callback(struct usbd_xfer *xfer); + +static void +umidi_bulk_read_callback(struct usbd_xfer *xfer); + +static void +umidi_write_clear_stall_callback(struct usbd_xfer *xfer); + +static u_int8_t +umidi_convert_to_usb(struct umidi_sub_chan *sub, u_int8_t cn, u_int8_t b); + +static void +umidi_bulk_write_callback(struct usbd_xfer *xfer); + +static struct umidi_sub_chan * +umidi_sub_by_cdev(struct usb_cdev *cdev); + +static void +umidi_start_read(struct usb_cdev *cdev); + +static void +umidi_stop_read(struct usb_cdev *cdev); + +static void +umidi_start_write(struct usb_cdev *cdev); + +static void +umidi_stop_write(struct usb_cdev *cdev); + +static int32_t +umidi_open(struct usb_cdev *cdev, int32_t fflags, + int32_t devtype, struct thread *td); + +static int32_t +umidi_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t data, + int32_t fflags, struct thread *td); +static void +umidi_init(device_t dev); + +static int32_t +umidi_probe(device_t dev); + +static int32_t +umidi_detach(device_t dev); + + +static const struct usbd_config +uaudio_cfg_record_full_speed[UAUDIO_NCHANBUFS] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = -1, /* any */ @@ -395,7 +490,8 @@ }, }; -static const struct usbd_config uaudio_cfg_record_high_speed[UAUDIO_NCHANBUFS] = { +static const struct usbd_config +uaudio_cfg_record_high_speed[UAUDIO_NCHANBUFS] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = -1, /* any */ @@ -417,7 +513,8 @@ }, }; -static const struct usbd_config uaudio_cfg_play_full_speed[UAUDIO_NCHANBUFS] = { +static const struct usbd_config +uaudio_cfg_play_full_speed[UAUDIO_NCHANBUFS] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = -1, /* any */ @@ -439,7 +536,8 @@ }, }; -static const struct usbd_config uaudio_cfg_play_high_speed[UAUDIO_NCHANBUFS] = { +static const struct usbd_config +uaudio_cfg_play_high_speed[UAUDIO_NCHANBUFS] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = -1, /* any */ @@ -461,7 +559,8 @@ }, }; -static const struct usbd_config uaudio_mixer_config[1] = { +static const struct usbd_config +uaudio_mixer_config[1] = { [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ @@ -472,6 +571,66 @@ }, }; +static const +u_int8_t umidi_cmd_to_len[16] = { + [0x0] = 0, /* reserved */ + [0x1] = 0, /* reserved */ + [0x2] = 2, /* bytes */ + [0x3] = 3, /* bytes */ + [0x4] = 3, /* bytes */ + [0x5] = 1, /* bytes */ + [0x6] = 2, /* bytes */ + [0x7] = 3, /* bytes */ + [0x8] = 3, /* bytes */ + [0x9] = 3, /* bytes */ + [0xA] = 3, /* bytes */ + [0xB] = 3, /* bytes */ + [0xC] = 2, /* bytes */ + [0xD] = 2, /* bytes */ + [0xE] = 3, /* bytes */ + [0xF] = 1, /* bytes */ +}; + +static const struct usbd_config +umidi_config[UMIDI_N_TRANSFER] = { + [0] = { + .type = UE_BULK, + .endpoint = -1, /* any */ + .direction = UE_DIR_OUT, + .bufsize = UMIDI_BULK_SIZE, + .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), + .callback = &umidi_bulk_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = -1, /* any */ + .direction = UE_DIR_IN, + .bufsize = UMIDI_BULK_SIZE, + .flags = (USBD_USE_DMA|USBD_SHORT_XFER_OK), + .callback = &umidi_bulk_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = -1, + .bufsize = sizeof(usb_device_request_t), + .flags = USBD_USE_DMA, + .callback = &umidi_write_clear_stall_callback, + .timeout = 1000, /* 1 second */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = -1, + .bufsize = sizeof(usb_device_request_t), + .flags = USBD_USE_DMA, + .callback = &umidi_read_clear_stall_callback, + .timeout = 1000, /* 1 second */ + }, +}; static devclass_t uaudio_devclass; @@ -528,6 +687,8 @@ sc->sc_rec_chan.priv_sc = sc; sc->sc_udev = uaa->device; + umidi_init(dev); + usbd_set_desc(dev, uaa->device); id = usbd_get_interface_descriptor(uaa->iface); @@ -564,8 +725,22 @@ device_printf(dev, "No recording!\n"); } - device_printf(dev, "WARNING: Unplugging the device while " - "it is in use will cause a panic!\n"); + if (sc->sc_midi_chan.valid) { + + if (umidi_probe(dev)) { + goto detach; + } + + device_printf(dev, "MIDI sequencer\n"); + } else { + device_printf(dev, "No midi sequencer\n"); + } + + if (sc->sc_play_chan.valid || + sc->sc_rec_chan.valid) { + device_printf(dev, "WARNING: Unplugging the device while " + "it is in use will cause a panic!\n"); + } DPRINTF(0, "doing child attach\n"); @@ -689,6 +864,8 @@ sbuf_delete(&(sc->sc_sndstat)); sc->sc_sndstat_valid = 0; + umidi_detach(dev); + return 0; } @@ -765,6 +942,19 @@ audio_if = 0; } + if ((id->bInterfaceClass == UICLASS_AUDIO) && + (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) { + + /* XXX could allow multiple MIDI interfaces XXX */ + + if ((sc->sc_midi_chan.valid == 0) && + usbd_get_iface(udev, curidx)) { + sc->sc_midi_chan.iface_index = curidx; + sc->sc_midi_chan.iface_alt_index = alt_index; + sc->sc_midi_chan.valid = 1; + } + } + asid = NULL; asf1d = NULL; ed1 = NULL; @@ -3071,6 +3261,599 @@ return src; } +/*========================================================================* + * MIDI support routines + *========================================================================*/ + +static void +umidi_read_clear_stall_callback(struct usbd_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct usbd_xfer *xfer_other = chan->xfer[1]; + u_int32_t n; + + USBD_CHECK_STATUS(xfer); + + tr_setup: + /* start clear stall */ + usbd_clear_stall_tr_setup(xfer, xfer_other); + return; + + tr_transferred: + usbd_clear_stall_tr_transferred(xfer, xfer_other); + + chan->flags &= ~UMIDI_FLAG_READ_STALL; + usbd_transfer_start(xfer_other); + return; + + tr_error: + /* bomb out */ + chan->flags &= ~UMIDI_FLAG_READ_STALL; + + if (xfer->error != USBD_CANCELLED) { + for (n = 0; n < chan->max_cable; n++) { + if (chan->sub[n].read_open) { + usb_cdev_put_data_error(&(chan->sub[n].cdev)); + } + } + } + return; +} + +static void +umidi_bulk_read_callback(struct usbd_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct umidi_sub_chan *sub; + u_int8_t buf[4]; + u_int8_t cmd_len; + u_int8_t cn; + u_int16_t pos; + + USBD_CHECK_STATUS(xfer); + + tr_error: + + DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); + + if (xfer->error != USBD_CANCELLED) { + /* try to clear stall first */ + chan->flags |= UMIDI_FLAG_READ_STALL; + usbd_transfer_start(chan->xfer[3]); + } + return; + + tr_transferred: + + DPRINTF(0, "actlen=%d bytes\n", xfer->actlen); + + if (xfer->actlen == 0) { + /* should not happen */ + goto tr_error; + } + + pos = 0; + + while (xfer->actlen >= 4) { + + usbd_copy_out(&(xfer->buf_data), pos, buf, 4); + + cmd_len = umidi_cmd_to_len[buf[0] & 0xF]; /* command length */ + cn = buf[0] >> 4; /* cable number */ + sub = &(chan->sub[cn]); + + if (cmd_len && (cn < chan->max_cable) && sub->read_open) { + usb_cdev_put_data(&(sub->cdev), buf+1, cmd_len, 1); + } else { + /* ignore the command */ + } + + xfer->actlen -= 4; + pos += 4; + } + + tr_setup: + DPRINTF(0, "start\n"); + + if (chan->flags & UMIDI_FLAG_READ_STALL) { + usbd_transfer_start(chan->xfer[3]); + return; + } + usbd_start_hardware(xfer); + return; +} + +static void +umidi_write_clear_stall_callback(struct usbd_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct usbd_xfer *xfer_other = chan->xfer[0]; + u_int32_t n; + + USBD_CHECK_STATUS(xfer); + + tr_setup: + /* start clear stall */ + usbd_clear_stall_tr_setup(xfer, xfer_other); + return; + + tr_transferred: + usbd_clear_stall_tr_transferred(xfer, xfer_other); + chan->flags &= ~UMIDI_FLAG_WRITE_STALL; + usbd_transfer_start(xfer_other); + return; + + tr_error: + /* bomb out */ + chan->flags &= ~UMIDI_FLAG_WRITE_STALL; + if (xfer->error != USBD_CANCELLED) { + for (n = 0; n < chan->max_cable; n++) { + if (chan->sub[n].write_open) { + usb_cdev_get_data_error(&(chan->sub[n].cdev)); + } + } + } + return; +} + +/* + * the following statemachine, that converts MIDI commands to + * USB MIDI packets, derives from Linux's usbmidi.c, which + * was written by "Clemens Ladisch": + * + * return values: + * 0: No command + * Else: Command is complete + */ +static u_int8_t +umidi_convert_to_usb(struct umidi_sub_chan *sub, u_int8_t cn, u_int8_t b) +{ + u_int8_t p0 = (cn << 4); + + if (b >= 0xf8) { + sub->temp_0[0] = p0 | 0x0f; + sub->temp_0[1] = b; + sub->temp_0[2] = 0; + sub->temp_0[3] = 0; + sub->temp_cmd = sub->temp_0; + return 1; + + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: /* system exclusive begin */ + sub->temp_1[1] = b; + sub->state = UMIDI_ST_SYSEX_1; + break; + case 0xf1: /* MIDI time code */ + case 0xf3: /* song select */ + sub->temp_1[1] = b; + sub->state = UMIDI_ST_1PARAM; + break; + case 0xf2: /* song position pointer */ + sub->temp_1[1] = b; + sub->state = UMIDI_ST_2PARAM_1; + break; + case 0xf4: /* unknown */ + case 0xf5: /* unknown */ + sub->state = UMIDI_ST_UNKNOWN; + break; + case 0xf6: /* tune request */ + sub->temp_1[0] = p0 | 0x05; + sub->temp_1[1] = 0xf6; + sub->temp_1[2] = 0; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return 1; + + case 0xf7: /* system exclusive end */ + switch (sub->state) { + case UMIDI_ST_SYSEX_0: + sub->temp_1[0] = p0 | 0x05; + sub->temp_1[1] = 0xf7; + sub->temp_1[2] = 0; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return 1; + case UMIDI_ST_SYSEX_1: + sub->temp_1[0] = p0 | 0x06; + sub->temp_1[2] = 0xf7; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return 1; + case UMIDI_ST_SYSEX_2: + sub->temp_1[0] = p0 | 0x07; + sub->temp_1[3] = 0xf7; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_UNKNOWN; + return 1; + } + sub->state = UMIDI_ST_UNKNOWN; + break; + } + } else if (b >= 0x80) { + sub->temp_1[1] = b; + if ((b >= 0xc0) && (b <= 0xdf)) { + sub->state = UMIDI_ST_1PARAM; + } else { + sub->state = UMIDI_ST_2PARAM_1; + } + } else { /* b < 0x80 */ + switch (sub->state) { + case UMIDI_ST_1PARAM: + if (sub->temp_1[1] < 0xf0) { + p0 |= sub->temp_1[1] >> 4; + } else { + p0 |= 0x02; + sub->state = UMIDI_ST_UNKNOWN; + } + sub->temp_1[0] = p0; + sub->temp_1[2] = b; + sub->temp_1[3] = 0; + sub->temp_cmd = sub->temp_1; + return 1; + case UMIDI_ST_2PARAM_1: + sub->temp_1[2] = b; + sub->state = UMIDI_ST_2PARAM_2; + break; + case UMIDI_ST_2PARAM_2: + if (sub->temp_1[1] < 0xf0) { + p0 |= sub->temp_1[1] >> 4; + sub->state = UMIDI_ST_2PARAM_1; + } else { + p0 |= 0x03; + sub->state = UMIDI_ST_UNKNOWN; + } + sub->temp_1[0] = p0; + sub->temp_1[3] = b; + sub->temp_cmd = sub->temp_1; + return 1; + case UMIDI_ST_SYSEX_0: + sub->temp_1[1] = b; + sub->state = UMIDI_ST_SYSEX_1; + break; + case UMIDI_ST_SYSEX_1: + sub->temp_1[2] = b; + sub->state = UMIDI_ST_SYSEX_2; + break; + case UMIDI_ST_SYSEX_2: + sub->temp_1[0] = p0 | 0x04; + sub->temp_1[3] = b; + sub->temp_cmd = sub->temp_1; + sub->state = UMIDI_ST_SYSEX_0; + return 1; + } + } + return 0; +} + +static void +umidi_bulk_write_callback(struct usbd_xfer *xfer) +{ + struct umidi_chan *chan = xfer->priv_sc; + struct umidi_sub_chan *sub; + u_int32_t actlen; + u_int16_t total_length; + u_int8_t buf; + u_int8_t start_cable; + u_int8_t tr_any; + + USBD_CHECK_STATUS(xfer); + + tr_error: + + DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error)); + + if (xfer->error != USBD_CANCELLED) { + /* try to clear stall first */ + chan->flags |= UMIDI_FLAG_WRITE_STALL; + usbd_transfer_start(chan->xfer[2]); + } + return; + + tr_transferred: + DPRINTF(0, "actlen=%d bytes\n", xfer->actlen); + + tr_setup: + + DPRINTF(0, "start\n"); + + if (chan->flags & UMIDI_FLAG_WRITE_STALL) { + usbd_transfer_start(chan->xfer[2]); + return; + } + + total_length = 0; /* reset */ + + start_cable = chan->curr_cable; + + tr_any = 0; + + while (1) { + + /* round robin de-queueing */ + + sub = &(chan->sub[chan->curr_cable]); + + if (sub->write_open) { + usb_cdev_get_data(&(sub->cdev), &buf, 1, &actlen, 0); + } else { + actlen = 0; + } + + if (actlen) { + tr_any = 1; + + DPRINTF(0, "byte=0x%02x\n", buf); + + if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) { + + DPRINTF(0, "sub= %02x %02x %02x %02x\n", + sub->temp_cmd[0], sub->temp_cmd[1], + sub->temp_cmd[2], sub->temp_cmd[3]); + + usbd_copy_in(&(xfer->buf_data), total_length, + sub->temp_cmd, 4); + + total_length += 4; + + if (total_length >= UMIDI_BULK_SIZE) { + break; + } + + } else { + continue; + } + } + + chan->curr_cable ++; + if (chan->curr_cable >= chan->max_cable) { + chan->curr_cable = 0; + } + if (chan->curr_cable == start_cable) { + if (tr_any == 0) { + break; + } + tr_any = 0; + } + } + + if (total_length) { + xfer->length = total_length; + usbd_start_hardware(xfer); + } + return; +} + +static struct umidi_sub_chan * +umidi_sub_by_cdev(struct usb_cdev *cdev) +{ + struct umidi_chan *chan = cdev->sc_priv_ptr; + struct umidi_sub_chan *sub; + u_int32_t n; + + for (n = 0; n < UMIDI_CABLES_MAX; n++) { + sub = &(chan->sub[n]); + if ((&(sub->cdev)) == cdev) { + return sub; + } + } + + panic("%s:%d cannot find usb_cdev!\n", + __FILE__, __LINE__); + + return NULL; +} + +static void +umidi_start_read(struct usb_cdev *cdev) +{ + struct umidi_chan *chan = cdev->sc_priv_ptr; + usbd_transfer_start(chan->xfer[1]); + return; +} + +static void +umidi_stop_read(struct usb_cdev *cdev) +{ + struct umidi_chan *chan = cdev->sc_priv_ptr; + struct umidi_sub_chan *sub = umidi_sub_by_cdev(cdev); + + DPRINTF(0, "\n"); + + sub->read_open = 0; + + if (--(chan->read_open_refcount) == 0) { + /* XXX don't stop the read transfer here, + * hence that causes problems with some + * MIDI adapters + */ + DPRINTF(0, "(stopping read transfer)\n"); + } + return; +} + +static void +umidi_start_write(struct usb_cdev *cdev) +{ + struct umidi_chan *chan = cdev->sc_priv_ptr; + usbd_transfer_start(chan->xfer[0]); + return; +} + +static void +umidi_stop_write(struct usb_cdev *cdev) +{ + struct umidi_chan *chan = cdev->sc_priv_ptr; + struct umidi_sub_chan *sub = umidi_sub_by_cdev(cdev); + + DPRINTF(0, "\n"); + + sub->write_open = 0; + + if (--(chan->write_open_refcount) == 0) { + DPRINTF(0, "(stopping write transfer)\n"); + usbd_transfer_stop(chan->xfer[2]); + usbd_transfer_stop(chan->xfer[0]); + } + return; +} + +static int32_t +umidi_open(struct usb_cdev *cdev, int32_t fflags, + int32_t devtype, struct thread *td) +{ + struct umidi_chan *chan = cdev->sc_priv_ptr; + struct umidi_sub_chan *sub = umidi_sub_by_cdev(cdev); + + if (fflags & FREAD) { + chan->read_open_refcount++; + sub->read_open = 1; + } + + if (fflags & FWRITE) { + /* clear stall first */ + chan->flags |= UMIDI_FLAG_WRITE_STALL; + chan->write_open_refcount++; + sub->write_open = 1; + + /* reset */ + sub->state = UMIDI_ST_UNKNOWN; + } + return 0; /* success */ +} + +static int32_t +umidi_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t data, + int32_t fflags, struct thread *td) +{ + return ENODEV; +} + +static void +umidi_init(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + struct umidi_chan *chan = &(sc->sc_midi_chan); + + mtx_init(&(chan->mtx), "umidi lock", NULL, MTX_DEF|MTX_RECURSE); + return; +} + +static int32_t +umidi_probe(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct umidi_chan *chan = &(sc->sc_midi_chan); + struct umidi_sub_chan *sub; + const char * p_buf[3]; + int32_t unit = device_get_unit(dev); + int32_t error; + u_int32_t n; + u_int8_t buf[32]; + + if (usbreq_set_interface(sc->sc_udev, chan->iface_index, + chan->iface_alt_index)) { + DPRINTF(0, "setting of alternate index failed!\n"); + goto detach; + } + + USBD_SET_IFACE_NO_PROBE(sc->sc_udev, chan->iface_index); + + error = usbd_transfer_setup(uaa->device, chan->iface_index, + chan->xfer, umidi_config, UMIDI_N_TRANSFER, + chan, &(chan->mtx), &(chan->mem_wait)); + if (error) { + DPRINTF(0, "error=%s\n", usbd_errstr(error)) ; + goto detach; + } + + if ((chan->max_cable > UMIDI_CABLES_MAX) || + (chan->max_cable == 0)) { + chan->max_cable = UMIDI_CABLES_MAX; + } + + for (n = 0; n < chan->max_cable; n++) { + + sub = &(chan->sub[n]); + + snprintf(buf, sizeof(buf), "umidi" + "%d.%x", unit, n); + + p_buf[0] = buf; + p_buf[1] = NULL; + + sub->cdev.sc_start_read = &umidi_start_read; + sub->cdev.sc_start_write = &umidi_start_write; + sub->cdev.sc_stop_read = &umidi_stop_read; + sub->cdev.sc_stop_write = &umidi_stop_write; + sub->cdev.sc_open = &umidi_open; + sub->cdev.sc_ioctl = &umidi_ioctl; + sub->cdev.sc_flags |= (USB_CDEV_FLAG_WAKEUP_RD_IMMED| + USB_CDEV_FLAG_WAKEUP_WR_IMMED); + + error = usb_cdev_attach(&(sub->cdev), chan, &(chan->mtx), p_buf, + UID_ROOT, GID_OPERATOR, 0644, + 4, (1024/4), + 32, (1024/32)); + if (error) { + goto detach; + } + } + + mtx_lock(&(chan->mtx)); + + /* clear stall first */ + chan->flags |= UMIDI_FLAG_READ_STALL; + + /* + * NOTE: at least one device will not work properly unless + * the BULK pipe is open all the time. + */ + usbd_transfer_start(chan->xfer[1]); + + mtx_unlock(&(chan->mtx)); + + return 0; /* success */ + + detach: + return ENXIO; /* failure */ +} + +static int32_t +umidi_detach(device_t dev) +{ + struct uaudio_softc *sc = device_get_softc(dev); + struct umidi_chan *chan = &(sc->sc_midi_chan); + u_int32_t n; + + for (n = 0; n < UMIDI_CABLES_MAX; n++) { + usb_cdev_detach(&(chan->sub[n].cdev)); + } + + mtx_lock(&(chan->mtx)); + + if (chan->xfer[3]) { + usbd_transfer_stop(chan->xfer[3]); + } + if (chan->xfer[1]) { + usbd_transfer_stop(chan->xfer[1]); + } + + mtx_unlock(&(chan->mtx)); + + usbd_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER); + + usbd_transfer_drain(&(chan->mem_wait), &(chan->mtx)); + + mtx_destroy(&(chan->mtx)); + + return 0; +} + DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, usbd_driver_load, 0); MODULE_DEPEND(uaudio, usb, 1, 1, 1); MODULE_VERSION(uaudio, 1);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200608200757.k7K7vKvM078486>