summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2010-05-12 23:40:44 +0000
committerthompsa <thompsa@FreeBSD.org>2010-05-12 23:40:44 +0000
commitd57aea636907a33a4d849915b2c88264122fbdcf (patch)
tree48f680963767d15be0a7cfda99dcf6a1df7d9643
parent5893edc5a1f5c676082888983ab9b1d7a1155ca8 (diff)
downloadFreeBSD-src-d57aea636907a33a4d849915b2c88264122fbdcf.zip
FreeBSD-src-d57aea636907a33a4d849915b2c88264122fbdcf.tar.gz
Reduce diffs to p4.
Add test code for delaying or failing usb control requests, disabled by default under ifdef USB_REQ_DEBUG. Submitted by: Hans Petter Selasky
-rw-r--r--sys/dev/usb/usb_request.c187
1 files changed, 163 insertions, 24 deletions
diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c
index 19806c0..a3c685d 100644
--- a/sys/dev/usb/usb_request.c
+++ b/sys/dev/usb/usb_request.c
@@ -71,15 +71,122 @@
#ifdef USB_DEBUG
static int usb_pr_poll_delay = USB_PORT_RESET_DELAY;
static int usb_pr_recovery_delay = USB_PORT_RESET_RECOVERY;
-static int usb_ss_delay = 0;
SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW,
&usb_pr_poll_delay, 0, "USB port reset poll delay in ms");
SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW,
&usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms");
-SYSCTL_INT(_hw_usb, OID_AUTO, ss_delay, CTLFLAG_RW,
- &usb_ss_delay, 0, "USB status stage delay in ms");
-#endif
+
+#ifdef USB_REQ_DEBUG
+/* The following structures are used in connection to fault injection. */
+struct usb_ctrl_debug {
+ int bus_index; /* target bus */
+ int dev_index; /* target address */
+ int ds_fail; /* fail data stage */
+ int ss_fail; /* fail data stage */
+ int ds_delay; /* data stage delay in ms */
+ int ss_delay; /* status stage delay in ms */
+ int bmRequestType_value;
+ int bRequest_value;
+};
+
+struct usb_ctrl_debug_bits {
+ uint16_t ds_delay;
+ uint16_t ss_delay;
+ uint8_t ds_fail:1;
+ uint8_t ss_fail:1;
+ uint8_t enabled:1;
+};
+
+/* The default is to disable fault injection. */
+
+static struct usb_ctrl_debug usb_ctrl_debug = {
+ .bus_index = -1,
+ .dev_index = -1,
+ .bmRequestType_value = -1,
+ .bRequest_value = -1,
+};
+
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RW,
+ &usb_ctrl_debug.bus_index, 0, "USB controller index to fail");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RW,
+ &usb_ctrl_debug.dev_index, 0, "USB device address to fail");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RW,
+ &usb_ctrl_debug.ds_fail, 0, "USB fail data stage");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RW,
+ &usb_ctrl_debug.ss_fail, 0, "USB fail status stage");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RW,
+ &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RW,
+ &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RW,
+ &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail");
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RW,
+ &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail");
+
+/*------------------------------------------------------------------------*
+ * usbd_get_debug_bits
+ *
+ * This function is only useful in USB host mode.
+ *------------------------------------------------------------------------*/
+static void
+usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req,
+ struct usb_ctrl_debug_bits *dbg)
+{
+ int temp;
+
+ memset(dbg, 0, sizeof(*dbg));
+
+ /* Compute data stage delay */
+
+ temp = usb_ctrl_debug.ds_delay;
+ if (temp < 0)
+ temp = 0;
+ else if (temp > (16*1024))
+ temp = (16*1024);
+
+ dbg->ds_delay = temp;
+
+ /* Compute status stage delay */
+
+ temp = usb_ctrl_debug.ss_delay;
+ if (temp < 0)
+ temp = 0;
+ else if (temp > (16*1024))
+ temp = (16*1024);
+
+ dbg->ss_delay = temp;
+
+ /* Check if this control request should be failed */
+
+ if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index)
+ return;
+
+ if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index)
+ return;
+
+ temp = usb_ctrl_debug.bmRequestType_value;
+
+ if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255))
+ return;
+
+ temp = usb_ctrl_debug.bRequest_value;
+
+ if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255))
+ return;
+
+ temp = usb_ctrl_debug.ds_fail;
+ if (temp)
+ dbg->ds_fail = 1;
+
+ temp = usb_ctrl_debug.ss_fail;
+ if (temp)
+ dbg->ss_fail = 1;
+
+ dbg->enabled = 1;
+}
+#endif /* USB_REQ_DEBUG */
+#endif /* USB_DEBUG */
/*------------------------------------------------------------------------*
* usbd_do_request_callback
@@ -264,6 +371,9 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
struct usb_device_request *req, void *data, uint16_t flags,
uint16_t *actlen, usb_timeout_t timeout)
{
+#ifdef USB_REQ_DEBUG
+ struct usb_ctrl_debug_bits dbg;
+#endif
usb_handle_req_t *hr_func;
struct usb_xfer *xfer;
const void *desc;
@@ -273,6 +383,7 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
usb_ticks_t max_ticks;
uint16_t length;
uint16_t temp;
+ uint16_t acttemp;
uint8_t enum_locked;
if (timeout < 50) {
@@ -327,7 +438,6 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
* Grab the default sx-lock so that serialisation
* is achieved when multiple threads are involved:
*/
-
sx_xlock(&udev->ctrl_sx);
hr_func = usbd_get_hr_func(udev);
@@ -391,6 +501,15 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
err = USB_ERR_NOMEM;
goto done;
}
+
+#ifdef USB_REQ_DEBUG
+ /* Get debug bits */
+ usbd_get_debug_bits(udev, req, &dbg);
+
+ /* Check for fault injection */
+ if (dbg.enabled)
+ flags |= USB_DELAY_STATUS_STAGE;
+#endif
USB_XFER_LOCK(xfer);
if (flags & USB_DELAY_STATUS_STAGE)
@@ -412,13 +531,32 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req));
usbd_xfer_set_frame_len(xfer, 0, sizeof(*req));
- xfer->nframes = 2;
while (1) {
temp = length;
- if (temp > xfer->max_data_length) {
+ if (temp > usbd_xfer_max_len(xfer)) {
temp = usbd_xfer_max_len(xfer);
}
+#ifdef USB_REQ_DEBUG
+ if (xfer->flags.manual_status) {
+ if (usbd_xfer_frame_len(xfer, 0) != 0) {
+ /* Execute data stage separately */
+ temp = 0;
+ } else if (temp > 0) {
+ if (dbg.ds_fail) {
+ err = USB_ERR_INVAL;
+ break;
+ }
+ if (dbg.ds_delay > 0) {
+ usb_pause_mtx(
+ xfer->xroot->xfer_mtx,
+ USB_MS_TO_TICKS(dbg.ds_delay));
+ /* make sure we don't time out */
+ start_ticks = ticks;
+ }
+ }
+ }
+#endif
usbd_xfer_set_frame_len(xfer, 1, temp);
if (temp > 0) {
@@ -438,21 +576,21 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
usbd_copy_in(xfer->frbuffers + 1,
0, data, temp);
}
- xfer->nframes = 2;
+ usbd_xfer_set_frames(xfer, 2);
} else {
- if (xfer->frlengths[0] == 0) {
+ if (usbd_xfer_frame_len(xfer, 0) == 0) {
if (xfer->flags.manual_status) {
-#ifdef USB_DEBUG
- int temp;
-
- temp = usb_ss_delay;
- if (temp > 5000) {
- temp = 5000;
+#ifdef USB_REQ_DEBUG
+ if (dbg.ss_fail) {
+ err = USB_ERR_INVAL;
+ break;
}
- if (temp > 0) {
+ if (dbg.ss_delay > 0) {
usb_pause_mtx(
xfer->xroot->xfer_mtx,
- USB_MS_TO_TICKS(temp));
+ USB_MS_TO_TICKS(dbg.ss_delay));
+ /* make sure we don't time out */
+ start_ticks = ticks;
}
#endif
xfer->flags.manual_status = 0;
@@ -460,7 +598,7 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
break;
}
}
- xfer->nframes = 1;
+ usbd_xfer_set_frames(xfer, 1);
}
usbd_transfer_start(xfer);
@@ -475,18 +613,19 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
if (err) {
break;
}
- /* subtract length of SETUP packet, if any */
- if (xfer->aframes > 0) {
- xfer->actlen -= xfer->frlengths[0];
+ /* get actual length of DATA stage */
+
+ if (xfer->aframes < 2) {
+ acttemp = 0;
} else {
- xfer->actlen = 0;
+ acttemp = usbd_xfer_frame_len(xfer, 1);
}
/* check for short packet */
- if (temp > xfer->actlen) {
- temp = xfer->actlen;
+ if (temp > acttemp) {
+ temp = acttemp;
length = temp;
}
if (temp > 0) {
OpenPOWER on IntegriCloud