summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2009-07-30 00:16:50 +0000
committeralfred <alfred@FreeBSD.org>2009-07-30 00:16:50 +0000
commit0f210d639a8b58ebb9aa016f3ec10e676cc16f04 (patch)
treef04b065a14af3d17aa8e906309d28fb90689f410
parent1e6e123c8472f28c2482a5c77515f24343fbce36 (diff)
downloadFreeBSD-src-0f210d639a8b58ebb9aa016f3ec10e676cc16f04.zip
FreeBSD-src-0f210d639a8b58ebb9aa016f3ec10e676cc16f04.tar.gz
USB CORE - compat Linux:
- Patch request from Tim Borgeaud: - add automatic locking - add refcount for killing URB's Submitted by: hps Approved by: re
-rw-r--r--sys/dev/usb/usb_compat_linux.c76
-rw-r--r--sys/dev/usb/usb_compat_linux.h1
2 files changed, 59 insertions, 18 deletions
diff --git a/sys/dev/usb/usb_compat_linux.c b/sys/dev/usb/usb_compat_linux.c
index 7385fa2..604ac4d 100644
--- a/sys/dev/usb/usb_compat_linux.c
+++ b/sys/dev/usb/usb_compat_linux.c
@@ -398,15 +398,32 @@ int
usb_submit_urb(struct urb *urb, uint16_t mem_flags)
{
struct usb_host_endpoint *uhe;
+ uint8_t do_unlock;
+ int err;
- if (urb == NULL) {
+ if (urb == NULL)
return (-EINVAL);
- }
- mtx_assert(&Giant, MA_OWNED);
+
+ do_unlock = mtx_owned(&Giant) ? 0 : 1;
+ if (do_unlock)
+ mtx_lock(&Giant);
if (urb->endpoint == NULL) {
- return (-EINVAL);
+ err = -EINVAL;
+ goto done;
}
+
+ /*
+ * Check to see if the urb is in the process of being killed
+ * and stop a urb that is in the process of being killed from
+ * being re-submitted (e.g. from its completion callback
+ * function).
+ */
+ if (urb->kill_count != 0) {
+ err = -EPERM;
+ goto done;
+ }
+
uhe = urb->endpoint;
/*
@@ -424,12 +441,16 @@ usb_submit_urb(struct urb *urb, uint16_t mem_flags)
usbd_transfer_start(uhe->bsd_xfer[0]);
usbd_transfer_start(uhe->bsd_xfer[1]);
+ err = 0;
} else {
/* no pipes have been setup yet! */
urb->status = -EINVAL;
- return (-EINVAL);
+ err = -EINVAL;
}
- return (0);
+done:
+ if (do_unlock)
+ mtx_unlock(&Giant);
+ return (err);
}
/*------------------------------------------------------------------------*
@@ -448,9 +469,11 @@ static void
usb_unlink_bsd(struct usb_xfer *xfer,
struct urb *urb, uint8_t drain)
{
- if (xfer &&
- usbd_transfer_pending(xfer) &&
- (xfer->priv_fifo == (void *)urb)) {
+ if (xfer == NULL)
+ return;
+ if (!usbd_transfer_pending(xfer))
+ return;
+ if (xfer->priv_fifo == (void *)urb) {
if (drain) {
mtx_unlock(&Giant);
usbd_transfer_drain(xfer);
@@ -467,14 +490,21 @@ usb_unlink_urb_sub(struct urb *urb, uint8_t drain)
{
struct usb_host_endpoint *uhe;
uint16_t x;
+ uint8_t do_unlock;
+ int err;
- if (urb == NULL) {
+ if (urb == NULL)
return (-EINVAL);
- }
- mtx_assert(&Giant, MA_OWNED);
+
+ do_unlock = mtx_owned(&Giant) ? 0 : 1;
+ if (do_unlock)
+ mtx_lock(&Giant);
+ if (drain)
+ urb->kill_count++;
if (urb->endpoint == NULL) {
- return (-EINVAL);
+ err = -EINVAL;
+ goto done;
}
uhe = urb->endpoint;
@@ -504,7 +534,13 @@ usb_unlink_urb_sub(struct urb *urb, uint8_t drain)
usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain);
usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain);
}
- return (0);
+ err = 0;
+done:
+ if (drain)
+ urb->kill_count--;
+ if (do_unlock)
+ mtx_unlock(&Giant);
+ return (err);
}
/*------------------------------------------------------------------------*
@@ -555,6 +591,7 @@ static int
usb_start_wait_urb(struct urb *urb, usb_timeout_t timeout, uint16_t *p_actlen)
{
int err;
+ uint8_t do_unlock;
/* you must have a timeout! */
if (timeout == 0) {
@@ -565,6 +602,9 @@ usb_start_wait_urb(struct urb *urb, usb_timeout_t timeout, uint16_t *p_actlen)
urb->transfer_flags |= URB_WAIT_WAKEUP;
urb->transfer_flags &= ~URB_IS_SLEEPING;
+ do_unlock = mtx_owned(&Giant) ? 0 : 1;
+ if (do_unlock)
+ mtx_lock(&Giant);
err = usb_submit_urb(urb, 0);
if (err)
goto done;
@@ -582,6 +622,8 @@ usb_start_wait_urb(struct urb *urb, usb_timeout_t timeout, uint16_t *p_actlen)
err = urb->status;
done:
+ if (do_unlock)
+ mtx_unlock(&Giant);
if (err) {
*p_actlen = 0;
} else {
@@ -638,7 +680,7 @@ usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *uhe,
* transfers on control endpoint zero:
*/
err = usbd_do_request_flags(dev,
- &Giant, &req, data, USB_SHORT_XFER_OK,
+ NULL, &req, data, USB_SHORT_XFER_OK,
&actlen, timeout);
if (err) {
err = -EPIPE;
@@ -1216,9 +1258,7 @@ usb_init_urb(struct urb *urb)
void
usb_kill_urb(struct urb *urb)
{
- if (usb_unlink_urb_sub(urb, 1)) {
- /* ignore */
- }
+ usb_unlink_urb_sub(urb, 1);
}
/*------------------------------------------------------------------------*
diff --git a/sys/dev/usb/usb_compat_linux.h b/sys/dev/usb/usb_compat_linux.h
index 38545d8..25a5bfb 100644
--- a/sys/dev/usb/usb_compat_linux.h
+++ b/sys/dev/usb/usb_compat_linux.h
@@ -262,6 +262,7 @@ struct urb {
uint8_t setup_dma; /* (in) not used on FreeBSD */
uint8_t transfer_dma; /* (in) not used on FreeBSD */
uint8_t bsd_isread;
+ uint8_t kill_count; /* FreeBSD specific */
struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */
};
OpenPOWER on IntegriCloud