summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2014-03-14 08:42:30 +0000
committerhselasky <hselasky@FreeBSD.org>2014-03-14 08:42:30 +0000
commit6bbd5680a4df771c980fdd36dcf78dc9dae915d7 (patch)
tree8a3b75d06927aee1ad6354b789d7bfa23e6faf3d
parent7961add611c0111412dc670fa7b7386b3ca135d2 (diff)
downloadFreeBSD-src-6bbd5680a4df771c980fdd36dcf78dc9dae915d7.zip
FreeBSD-src-6bbd5680a4df771c980fdd36dcf78dc9dae915d7.tar.gz
Workaround for USB MIDI adapters which use non-supported values of
wMaxPacketSize for BULK endpoints. MFC after: 1 week
-rw-r--r--sys/dev/sound/usb/uaudio.c19
-rw-r--r--sys/dev/usb/usb_core.h2
-rw-r--r--sys/dev/usb/usb_transfer.c23
-rw-r--r--sys/dev/usb/usbdi.h1
4 files changed, 45 insertions, 0 deletions
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index 3299f22..33ff274 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -5674,6 +5674,25 @@ umidi_probe(device_t dev)
DPRINTF("error=%s\n", usbd_errstr(error));
goto detach;
}
+
+ /*
+ * Some USB MIDI device makers couldn't resist using
+ * wMaxPacketSize = 4 for RX and TX BULK endpoints, although
+ * that size is an unsupported value for FULL speed BULK
+ * endpoints. The same applies to some HIGH speed MIDI devices
+ * which are using a wMaxPacketSize different from 512 bytes.
+ *
+ * Refer to section 5.8.3 in USB 2.0 PDF: Cite: "All Host
+ * Controllers are required to have support for 8-, 16-, 32-,
+ * and 64-byte maximum packet sizes for full-speed bulk
+ * endpoints and 512 bytes for high-speed bulk endpoints."
+ */
+ if (usbd_xfer_maxp_was_clamped(chan->xfer[UMIDI_TX_TRANSFER]))
+ chan->single_command = 1;
+
+ if (chan->single_command != 0)
+ device_printf(dev, "Single command MIDI quirk enabled\n");
+
if ((chan->max_cable > UMIDI_CABLES_MAX) ||
(chan->max_cable == 0)) {
chan->max_cable = UMIDI_CABLES_MAX;
diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h
index afd74ed..bea7c2c 100644
--- a/sys/dev/usb/usb_core.h
+++ b/sys/dev/usb/usb_core.h
@@ -114,6 +114,8 @@ struct usb_xfer_flags_int {
uint8_t can_cancel_immed:1; /* set if USB transfer can be
* cancelled immediately */
uint8_t doing_callback:1; /* set if executing the callback */
+ uint8_t maxp_was_clamped:1; /* set if the max packet size
+ * was outside its allowed range */
};
/*
diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
index dccac40..db912bc 100644
--- a/sys/dev/usb/usb_transfer.c
+++ b/sys/dev/usb/usb_transfer.c
@@ -346,6 +346,7 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
usb_frcount_t n_frlengths;
usb_frcount_t n_frbuffers;
usb_frcount_t x;
+ uint16_t maxp_old;
uint8_t type;
uint8_t zmps;
@@ -433,6 +434,11 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
if (xfer->max_packet_count > parm->hc_max_packet_count) {
xfer->max_packet_count = parm->hc_max_packet_count;
}
+
+ /* store max packet size value before filtering */
+
+ maxp_old = xfer->max_packet_size;
+
/* filter "wMaxPacketSize" according to HC capabilities */
if ((xfer->max_packet_size > parm->hc_max_packet_size) ||
@@ -465,6 +471,13 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
}
}
+ /*
+ * Check if the max packet size was outside its allowed range
+ * and clamped to a valid value:
+ */
+ if (maxp_old != xfer->max_packet_size)
+ xfer->flags_int.maxp_was_clamped = 1;
+
/* compute "max_frame_size" */
usbd_update_max_frame_size(xfer);
@@ -3432,3 +3445,13 @@ usbd_xfer_get_timestamp(struct usb_xfer *xfer)
{
return (xfer->isoc_time_complete);
}
+
+/*
+ * The following function returns non-zero if the max packet size
+ * field was clamped to a valid value. Else it returns zero.
+ */
+uint8_t
+usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer)
+{
+ return (xfer->flags_int.maxp_was_clamped);
+}
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
index 099b390..f4c6cf9 100644
--- a/sys/dev/usb/usbdi.h
+++ b/sys/dev/usb/usbdi.h
@@ -569,6 +569,7 @@ int usbd_xfer_is_stalled(struct usb_xfer *xfer);
void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag);
void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag);
uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer);
+uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer);
void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset,
const void *ptr, usb_frlength_t len);
OpenPOWER on IntegriCloud