summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2009-07-30 00:15:50 +0000
committeralfred <alfred@FreeBSD.org>2009-07-30 00:15:50 +0000
commitec456e3a3b32ea50f5c8e3b3a05352e989c10967 (patch)
treead76cd69ab61646de0aee618acfa94818090e52d
parentfee3a289891db0743737072e9c451512f989ceae (diff)
downloadFreeBSD-src-ec456e3a3b32ea50f5c8e3b3a05352e989c10967.zip
FreeBSD-src-ec456e3a3b32ea50f5c8e3b3a05352e989c10967.tar.gz
USB core:
- add support for defragging of written device data. - improve handling of alternate settings in device side mode. - correct return value from usbd_get_no_alts() function. - reported by: HPS - P4 ID: 166156, 166168 - report USB device release information to devd and pnpinfo. - reported by: MIHIRA Sanpei Yoshiro - P4 ID: 166221 Submitted by: hps Approved by: re
-rw-r--r--sys/dev/usb/usb_dev.c83
-rw-r--r--sys/dev/usb/usb_dev.h4
-rw-r--r--sys/dev/usb/usb_device.c35
-rw-r--r--sys/dev/usb/usb_handle_request.c66
-rw-r--r--sys/dev/usb/usb_hub.c2
-rw-r--r--sys/dev/usb/usb_parse.c18
-rw-r--r--sys/dev/usb/usb_request.c8
7 files changed, 145 insertions, 71 deletions
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c
index de2e156..cba8919 100644
--- a/sys/dev/usb/usb_dev.c
+++ b/sys/dev/usb/usb_dev.c
@@ -740,6 +740,8 @@ usb_fifo_reset(struct usb_fifo *f)
break;
}
}
+ /* reset have fragment flag */
+ f->flag_have_fragment = 0;
}
/*------------------------------------------------------------------------*
@@ -783,6 +785,16 @@ usb_fifo_close(struct usb_fifo *f, int fflags)
/* set flushing flag */
f->flag_flushing = 1;
+ /* get the last packet in */
+ if (f->flag_have_fragment) {
+ struct usb_mbuf *m;
+ f->flag_have_fragment = 0;
+ USB_IF_DEQUEUE(&f->free_q, m);
+ if (m) {
+ USB_IF_ENQUEUE(&f->used_q, m);
+ }
+ }
+
/* start write transfer, if not already started */
(f->methods->f_start_write) (f);
@@ -1303,6 +1315,7 @@ usb_write(struct cdev *dev, struct uio *uio, int ioflag)
struct usb_cdev_privdata* cpd;
struct usb_fifo *f;
struct usb_mbuf *m;
+ uint8_t *pdata;
int fflags;
int resid;
int io_len;
@@ -1373,33 +1386,59 @@ usb_write(struct cdev *dev, struct uio *uio, int ioflag)
}
tr_data = 1;
- USB_MBUF_RESET(m);
-
- io_len = MIN(m->cur_data_len, uio->uio_resid);
-
- m->cur_data_len = io_len;
+ if (f->flag_have_fragment == 0) {
+ USB_MBUF_RESET(m);
+ io_len = m->cur_data_len;
+ pdata = m->cur_data_ptr;
+ if (io_len > uio->uio_resid)
+ io_len = uio->uio_resid;
+ m->cur_data_len = io_len;
+ } else {
+ io_len = m->max_data_len - m->cur_data_len;
+ pdata = m->cur_data_ptr + m->cur_data_len;
+ if (io_len > uio->uio_resid)
+ io_len = uio->uio_resid;
+ m->cur_data_len += io_len;
+ }
DPRINTFN(2, "transfer %d bytes to %p\n",
- io_len, m->cur_data_ptr);
+ io_len, pdata);
- err = usb_fifo_uiomove(f,
- m->cur_data_ptr, io_len, uio);
+ err = usb_fifo_uiomove(f, pdata, io_len, uio);
if (err) {
+ f->flag_have_fragment = 0;
USB_IF_ENQUEUE(&f->free_q, m);
break;
}
- if (f->methods->f_filter_write) {
+
+ /* check if the buffer is ready to be transmitted */
+
+ if ((f->flag_write_defrag == 0) ||
+ (m->cur_data_len == m->max_data_len)) {
+ f->flag_have_fragment = 0;
+
/*
- * Sometimes it is convenient to process data at the
- * expense of a userland process instead of a kernel
- * process.
+ * Check for write filter:
+ *
+ * Sometimes it is convenient to process data
+ * at the expense of a userland process
+ * instead of a kernel process.
*/
- (f->methods->f_filter_write) (f, m);
- }
- USB_IF_ENQUEUE(&f->used_q, m);
+ if (f->methods->f_filter_write) {
+ (f->methods->f_filter_write) (f, m);
+ }
- (f->methods->f_start_write) (f);
+ /* Put USB mbuf in the used queue */
+ USB_IF_ENQUEUE(&f->used_q, m);
+
+ /* Start writing data, if not already started */
+ (f->methods->f_start_write) (f);
+ } else {
+ /* Wait for more data or close */
+ f->flag_have_fragment = 1;
+ USB_IF_PREPEND(&f->free_q, m);
+ }
} while (uio->uio_resid > 0);
done:
@@ -2220,6 +2259,18 @@ usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
f->flag_short = onoff;
}
+void
+usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff)
+{
+ if (f == NULL)
+ return;
+
+ /* defrag written data */
+ f->flag_write_defrag = onoff;
+ /* reset defrag state */
+ f->flag_have_fragment = 0;
+}
+
void *
usb_fifo_softc(struct usb_fifo *f)
{
diff --git a/sys/dev/usb/usb_dev.h b/sys/dev/usb/usb_dev.h
index 040916b..aa9197f 100644
--- a/sys/dev/usb/usb_dev.h
+++ b/sys/dev/usb/usb_dev.h
@@ -130,6 +130,8 @@ struct usb_fifo {
uint8_t flag_short; /* set if short_ok or force_short
* transfer flags should be set */
uint8_t flag_stall; /* set if clear stall should be run */
+ uint8_t flag_write_defrag; /* set to defrag written data */
+ uint8_t flag_have_fragment; /* set if defragging */
uint8_t iface_index; /* set to the interface we belong to */
uint8_t fifo_index; /* set to the FIFO index in "struct
* usb_device" */
@@ -144,11 +146,9 @@ extern struct cdevsw usb_devsw;
int usb_fifo_wait(struct usb_fifo *fifo);
void usb_fifo_signal(struct usb_fifo *fifo);
uint8_t usb_fifo_opened(struct usb_fifo *fifo);
-void usb_fifo_free(struct usb_fifo *f);
struct usb_symlink *usb_alloc_symlink(const char *target);
void usb_free_symlink(struct usb_symlink *ps);
int usb_read_symlink(uint8_t *user_ptr, uint32_t startentry,
uint32_t user_len);
-void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t);
#endif /* _USB_DEV_H_ */
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index 6617297..2d34017 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -833,18 +833,13 @@ usbd_set_alt_interface_index(struct usb_device *udev,
err = USB_ERR_INVAL;
goto done;
}
- if (udev->flags.usb_mode == USB_MODE_DEVICE) {
- usb_detach_device(udev, iface_index,
- USB_UNCFG_FLAG_FREE_SUBDEV);
- } else {
- if (iface->alt_index == alt_index) {
- /*
- * Optimise away duplicate setting of
- * alternate setting in USB Host Mode!
- */
- err = 0;
- goto done;
- }
+ if (iface->alt_index == alt_index) {
+ /*
+ * Optimise away duplicate setting of
+ * alternate setting in USB Host Mode!
+ */
+ err = 0;
+ goto done;
}
#if USB_HAVE_UGEN
/*
@@ -858,6 +853,12 @@ usbd_set_alt_interface_index(struct usb_device *udev,
if (err) {
goto done;
}
+ if (iface->alt_index != alt_index) {
+ /* the alternate setting does not exist */
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+
err = usbd_req_set_alt_interface_no(udev, NULL, iface_index,
iface->idesc->bAlternateSetting);
@@ -959,7 +960,6 @@ usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index)
{
struct usb_endpoint *ep;
struct usb_endpoint *ep_end;
- usb_error_t err;
ep = udev->endpoints;
ep_end = udev->endpoints + udev->endpoints_max;
@@ -971,10 +971,7 @@ usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index)
continue;
}
/* simulate a clear stall from the peer */
- err = usbd_set_endpoint_stall(udev, ep, 0);
- if (err) {
- /* just ignore */
- }
+ usbd_set_endpoint_stall(udev, ep, 0);
}
return (0);
}
@@ -1286,6 +1283,7 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index)
uaa.info.bIfaceNum =
iface->idesc->bInterfaceNumber;
uaa.use_generic = 0;
+ uaa.driver_info = 0; /* reset driver_info */
DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n",
uaa.info.bInterfaceClass,
@@ -1302,6 +1300,7 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index)
/* try generic interface drivers last */
uaa.use_generic = 1;
+ uaa.driver_info = 0; /* reset driver_info */
if (usb_probe_and_attach_sub(udev, &uaa)) {
/* ignore */
@@ -2334,6 +2333,7 @@ usb_notify_addq(const char *type, struct usb_device *udev)
"devclass=0x%02x "
"devsubclass=0x%02x "
"sernum=\"%s\" "
+ "release=0x%04x "
"at "
"port=%u "
"on "
@@ -2345,6 +2345,7 @@ usb_notify_addq(const char *type, struct usb_device *udev)
udev->ddesc.bDeviceClass,
udev->ddesc.bDeviceSubClass,
udev->serial,
+ UGETW(udev->ddesc.bcdDevice),
udev->port_no,
udev->parent_hub != NULL ?
udev->parent_hub->ugen_name :
diff --git a/sys/dev/usb/usb_handle_request.c b/sys/dev/usb/usb_handle_request.c
index 23486ad..a720919 100644
--- a/sys/dev/usb/usb_handle_request.c
+++ b/sys/dev/usb/usb_handle_request.c
@@ -46,6 +46,7 @@
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
#include "usb_if.h"
#define USB_DEBUG_VAR usb_debug
@@ -181,6 +182,30 @@ done:
return (err);
}
+static usb_error_t
+usb_check_alt_setting(struct usb_device *udev,
+ struct usb_interface *iface, uint8_t alt_index)
+{
+ uint8_t do_unlock;
+ usb_error_t err = 0;
+
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+
+ if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
+ err = USB_ERR_INVAL;
+
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (err);
+}
+
/*------------------------------------------------------------------------*
* usb_handle_iface_request
*
@@ -285,42 +310,29 @@ tr_repeat:
switch (req.bRequest) {
case UR_SET_INTERFACE:
/*
- * Handle special case. If we have parent interface
- * we just reset the endpoints, because this is a
- * multi interface device and re-attaching only a
- * part of the device is not possible. Also if the
- * alternate setting is the same like before we just
- * reset the interface endoints.
- */
- if ((iface_parent != NULL) ||
- (iface->alt_index == req.wValue[0])) {
- error = usb_reset_iface_endpoints(udev,
- iface_index);
- if (error) {
- DPRINTF("alt setting failed %s\n",
- usbd_errstr(error));
- goto tr_stalled;
- }
- break;
- }
- /*
- * Doing the alternate setting will detach the
- * interface aswell:
+ * We assume that the endpoints are the same
+ * accross the alternate settings.
+ *
+ * Reset the endpoints, because re-attaching
+ * only a part of the device is not possible.
*/
- error = usbd_set_alt_interface_index(udev,
- iface_index, req.wValue[0]);
+ error = usb_check_alt_setting(udev,
+ iface, req.wValue[0]);
if (error) {
- DPRINTF("alt setting failed %s\n",
+ DPRINTF("alt setting does not exist %s\n",
usbd_errstr(error));
goto tr_stalled;
}
- error = usb_probe_and_attach(udev,
- iface_index);
+ error = usb_reset_iface_endpoints(udev, iface_index);
if (error) {
- DPRINTF("alt setting probe failed\n");
+ DPRINTF("alt setting failed %s\n",
+ usbd_errstr(error));
goto tr_stalled;
}
+ /* update the current alternate setting */
+ iface->alt_index = req.wValue[0];
break;
+
default:
goto tr_stalled;
}
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index 8fbd573..0defc97 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -1020,12 +1020,14 @@ uhub_child_pnpinfo_string(device_t parent, device_t child,
snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
"devclass=0x%02x devsubclass=0x%02x "
"sernum=\"%s\" "
+ "release=0x%04x "
"intclass=0x%02x intsubclass=0x%02x",
UGETW(res.udev->ddesc.idVendor),
UGETW(res.udev->ddesc.idProduct),
res.udev->ddesc.bDeviceClass,
res.udev->ddesc.bDeviceSubClass,
res.udev->serial,
+ UGETW(res.udev->ddesc.bcdDevice),
iface->idesc->bInterfaceClass,
iface->idesc->bInterfaceSubClass);
} else {
diff --git a/sys/dev/usb/usb_parse.c b/sys/dev/usb/usb_parse.c
index 884bd05..deb52ea 100644
--- a/sys/dev/usb/usb_parse.c
+++ b/sys/dev/usb/usb_parse.c
@@ -215,20 +215,29 @@ usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
* usbd_get_no_alts
*
* Return value:
- * Number of alternate settings for the given interface descriptor pointer.
+ * Number of alternate settings for the given interface descriptor
+ * pointer. If the USB descriptor is corrupt, the returned value can
+ * be greater than the actual number of alternate settings.
*------------------------------------------------------------------------*/
uint8_t
usbd_get_no_alts(struct usb_config_descriptor *cd,
struct usb_interface_descriptor *id)
{
struct usb_descriptor *desc;
- uint8_t n = 0;
+ uint8_t n;
uint8_t ifaceno;
+ /* Reset interface count */
+
+ n = 0;
+
+ /* Get the interface number */
+
ifaceno = id->bInterfaceNumber;
- desc = (struct usb_descriptor *)id;
+ /* Iterate all the USB descriptors */
+ desc = NULL;
while ((desc = usb_desc_foreach(cd, desc))) {
if ((desc->bDescriptorType == UDESC_INTERFACE) &&
(desc->bLength >= sizeof(*id))) {
@@ -237,8 +246,7 @@ usbd_get_no_alts(struct usb_config_descriptor *cd,
n++;
if (n == 0xFF)
break; /* crazy */
- } else
- break; /* end */
+ }
}
}
return (n);
diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c
index de7101a..8ccaa14 100644
--- a/sys/dev/usb/usb_request.c
+++ b/sys/dev/usb/usb_request.c
@@ -1059,9 +1059,9 @@ usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
struct usb_interface *iface = usbd_get_iface(udev, iface_index);
struct usb_device_request req;
- if ((iface == NULL) || (iface->idesc == NULL)) {
+ if ((iface == NULL) || (iface->idesc == NULL))
return (USB_ERR_INVAL);
- }
+
req.bmRequestType = UT_READ_INTERFACE;
req.bRequest = UR_GET_INTERFACE;
USETW(req.wValue, 0);
@@ -1085,9 +1085,9 @@ usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
struct usb_interface *iface = usbd_get_iface(udev, iface_index);
struct usb_device_request req;
- if ((iface == NULL) || (iface->idesc == NULL)) {
+ if ((iface == NULL) || (iface->idesc == NULL))
return (USB_ERR_INVAL);
- }
+
req.bmRequestType = UT_WRITE_INTERFACE;
req.bRequest = UR_SET_INTERFACE;
req.wValue[0] = alt_no;
OpenPOWER on IntegriCloud