Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 7 Aug 2008 13:41:28 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 146837 for review
Message-ID:  <200808071341.m77DfSI3051427@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=146837

Change 146837 by hselasky@hselasky_laptop001 on 2008/08/07 13:41:14

	
	Fixes and improvements to the Linux USB compat layer.
	Has been tested and found to work.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_compat_linux.c#8 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb2/core/usb2_compat_linux.c#8 (text+ko) ====

@@ -28,6 +28,9 @@
 #include <dev/usb2/include/usb2_mfunc.h>
 #include <dev/usb2/include/usb2_standard.h>
 #include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_ioctl.h>
+
+#define	USB_DEBUG_VAR usb2_debug
 
 #include <dev/usb2/core/usb2_core.h>
 #include <dev/usb2/core/usb2_compat_linux.h>
@@ -38,6 +41,8 @@
 #include <dev/usb2/core/usb2_transfer.h>
 #include <dev/usb2/core/usb2_parse.h>
 #include <dev/usb2/core/usb2_hub.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_debug.h>
 
 struct usb_linux_softc {
 	LIST_ENTRY(usb_linux_softc) sc_attached_list;
@@ -62,12 +67,13 @@
 static usb_complete_t usb_linux_wait_complete;
 
 static uint16_t usb_max_isoc_frames(struct usb_device *dev);
-static int usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint32_t *p_actlen);
+static int usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint16_t *p_actlen);
 static const struct usb_device_id *usb_linux_lookup_id(const struct usb_device_id *id, struct usb2_attach_arg *uaa);
 static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *sc);
 static struct usb_device *usb_linux_create_usb_device(struct usb2_device *udev, device_t dev);
 static void usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface);
 static void usb_linux_complete(struct usb2_xfer *xfer);
+static int usb_unlink_urb_sub(struct urb *urb, uint8_t drain);
 
 /*------------------------------------------------------------------------*
  * FreeBSD USB interface
@@ -437,12 +443,35 @@
  *
  * This function is used to stop an URB after that it is been
  * submitted, but before the "complete" callback has been called. On
- * FreeBSD this function is always non-blocking, and will always call
- * the "complete" callback with an error before it returns.
  *------------------------------------------------------------------------*/
 int
 usb_unlink_urb(struct urb *urb)
 {
+	return (usb_unlink_urb_sub(urb, 0));
+}
+
+static void
+usb_unlink_bsd(struct usb2_xfer *xfer,
+    struct urb *urb, uint8_t drain)
+{
+	if (xfer &&
+	    usb2_transfer_pending(xfer) &&
+	    (xfer->priv_fifo == (void *)urb)) {
+		if (drain) {
+			mtx_unlock(&Giant);
+			usb2_transfer_drain(xfer);
+			mtx_lock(&Giant);
+		} else {
+			usb2_transfer_stop(xfer);
+		}
+		usb2_transfer_start(xfer);
+	}
+	return;
+}
+
+static int
+usb_unlink_urb_sub(struct urb *urb, uint8_t drain)
+{
 	struct usb_host_endpoint *uhe;
 	uint16_t x;
 
@@ -479,17 +508,8 @@
 		 * If so, re-start that transfer, which will lead to the
 		 * termination of that URB:
 		 */
-
-		if (uhe->bsd_xfer[0] &&
-		    (uhe->bsd_xfer[0]->priv_fifo == (void *)urb)) {
-			usb2_transfer_stop(uhe->bsd_xfer[0]);
-			usb2_transfer_start(uhe->bsd_xfer[0]);
-		}
-		if (uhe->bsd_xfer[1] &&
-		    (uhe->bsd_xfer[1]->priv_fifo == (void *)urb)) {
-			usb2_transfer_stop(uhe->bsd_xfer[1]);
-			usb2_transfer_start(uhe->bsd_xfer[1]);
-		}
+		usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain);
+		usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain);
 	}
 	return (0);
 }
@@ -539,7 +559,7 @@
  * Linux USB transfers.
  *------------------------------------------------------------------------*/
 static int
-usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint32_t *p_actlen)
+usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint16_t *p_actlen)
 {
 	int err;
 
@@ -564,8 +584,6 @@
 		urb->transfer_flags |= URB_IS_SLEEPING;
 		usb2_cv_wait(&(urb->cv_wait), &Giant);
 		urb->transfer_flags &= ~URB_IS_SLEEPING;
-		if (err)
-			goto done;
 	}
 
 	err = urb->status;
@@ -596,48 +614,51 @@
 int
 usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *uhe,
     uint8_t request, uint8_t requesttype,
-    uint16_t value, uint16_t index, void *data, uint16_t size, uint32_t timeout)
+    uint16_t value, uint16_t index, void *data,
+    uint16_t size, uint32_t timeout)
 {
+	struct usb2_device_request req;
 	struct urb *urb;
-	struct usb2_device_request *req;
-	struct usb_host_endpoint *uhe_write;
-	struct usb_host_endpoint *uhe_read;
-	uint32_t actlen;
 	int err;
+	uint16_t actlen;
 	uint8_t type;
 	uint8_t addr;
 
+	req.bmRequestType = requesttype;
+	req.bRequest = request;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, index);
+	USETW(req.wLength, size);
+
 	if (uhe == NULL) {
 		return (-EINVAL);
 	}
 	type = (uhe->desc.bmAttributes & UE_XFERTYPE);
 	addr = (uhe->desc.bEndpointAddress & UE_ADDR);
 
-	/*
-	 * The FreeBSD USB stack supports standard control transfers on
-	 * control endpoints only. For the other two endpoint types we need
-	 * special handling. Check the endpoint type:
-	 */
-	if (type == UE_CONTROL) {
-		uhe_write = NULL;
-		uhe_read = NULL;
-	} else {
-		if (type == UE_ISOCHRONOUS) {
-			return (-EINVAL);
-		}
-		uhe_write = usb_find_host_endpoint(dev, type, addr | UE_DIR_OUT);
-		if (uhe_write == NULL) {
-			return (-EINVAL);
-		}
-		if (requesttype & UT_READ) {
-			uhe_read = usb_find_host_endpoint(dev, type, addr | UE_DIR_IN);
-			if (uhe_read == NULL) {
-				return (-EINVAL);
-			}
+	if (type != UE_CONTROL) {
+		return (-EINVAL);
+	}
+	if (addr == 0) {
+		/*
+		 * The FreeBSD USB stack supports standard control
+		 * transfers on control endpoint zero:
+		 */
+		err = usb2_do_request_flags(dev->bsd_udev,
+		    &Giant, &req, data, USB_SHORT_XFER_OK,
+		    &actlen, timeout);
+		if (err) {
+			err = -EPIPE;
 		} else {
-			uhe_read = NULL;
+			err = actlen;
 		}
+		return (err);
 	}
+	if (dev->bsd_udev->flags.usb2_mode != USB_MODE_HOST) {
+		/* not supported */
+		return (-EINVAL);
+	}
+	err = usb_setup_endpoint(dev, uhe, 1 /* dummy */ );
 
 	/*
 	 * NOTE: we need to allocate real memory here so that we don't
@@ -650,58 +671,21 @@
 		return (-ENOMEM);
 
 	urb->dev = dev;
+	urb->pipe = uhe;
 
-	req = (void *)(urb->setup_packet);
+	bcopy(&req, urb->setup_packet, sizeof(req));
 
-	req->bmRequestType = requesttype;
-	req->bRequest = request;
-	USETW(req->wValue, value);
-	USETW(req->wIndex, index);
-	USETW(req->wLength, size);
-
-	if (size && (req->bmRequestType & UT_WRITE)) {
+	if (size && (!(req.bmRequestType & UT_READ))) {
 		/* move the data to a real buffer */
-		bcopy(data, req + 1, size);
+		bcopy(data, USB_ADD_BYTES(urb->setup_packet,
+		    sizeof(req)), size);
 	}
-	if (type == UE_CONTROL) {
-		urb->pipe = uhe;
-		err = usb_start_wait_urb(urb, timeout, &actlen);
-	} else {
-		urb->pipe = uhe_write;
-		urb->transfer_buffer = urb->setup_packet;
-		urb->transfer_buffer_length = sizeof(*req);
+	err = usb_start_wait_urb(urb, timeout, &actlen);
 
-		err = usb_start_wait_urb(urb, 1000, &actlen);
-		if (err) {
-			goto done;
-		}
-		if (actlen < sizeof(*req)) {
-			err = -EPIPE;
-			actlen = 0;
-			goto done;
-		}
-		if (size) {
-			if (req->bmRequestType & UT_READ) {
-				urb->pipe = uhe_read;
-			}
-			urb->transfer_buffer = req + 1;
-			urb->transfer_buffer_length = size;
-
-			err = usb_start_wait_urb(urb, timeout, &actlen);
-			if (err) {
-				goto done;
-			}
-		}
-	}
-
-done:
-	if (req->bmRequestType & UT_READ) {
-		if (actlen < size) {
-			/* we don't like returning random data */
-			bzero(((uint8_t *)data) + actlen, size - actlen);
-		}
+	if (req.bmRequestType & UT_READ) {
 		if (actlen) {
-			bcopy(req + 1, data, actlen);
+			bcopy(USB_ADD_BYTES(urb->setup_packet,
+			    sizeof(req)), data, actlen);
 		}
 	}
 	usb_free_urb(urb);
@@ -751,7 +735,8 @@
  * size on "wMaxPacketSize".
  *------------------------------------------------------------------------*/
 int
-usb_setup_endpoint(struct usb_device *dev, struct usb_host_endpoint *uhe, uint32_t bufsize)
+usb_setup_endpoint(struct usb_device *dev,
+    struct usb_host_endpoint *uhe, uint32_t bufsize)
 {
 	struct usb2_config cfg[2];
 	uint8_t type = uhe->desc.bmAttributes & UE_XFERTYPE;
@@ -962,12 +947,6 @@
 			    sizeof(p_ud->descriptor));
 			bcopy(udev->default_pipe.edesc, &(p_ud->ep0.desc),
 			    sizeof(p_ud->ep0.desc));
-
-			if (usb_setup_endpoint(p_ud, &(p_ud->ep0), 1024 /* bytes */ )) {
-				usb_linux_free_device(p_ud);
-				p_ud = NULL;
-				goto done;
-			}
 		}
 	}
 done:
@@ -1007,7 +986,6 @@
 			urb->setup_packet = (void *)(urb + 1);
 			urb->transfer_buffer = (void *)(urb->setup_packet +
 			    sizeof(struct usb2_device_request));
-			urb->transfer_buffer_length = mem_flags;
 		} else {
 			urb->number_of_packets = iso_packets;
 		}
@@ -1058,7 +1036,6 @@
 			for (uhe = uhi->endpoint;
 			    uhe != uhe_end;
 			    uhe++) {
-
 				ea = uhe->desc.bEndpointAddress;
 				at = uhe->desc.bmAttributes;
 
@@ -1264,9 +1241,9 @@
 void
 usb_kill_urb(struct urb *urb)
 {
-	int err;
-
-	err = usb_unlink_urb(urb);
+	if (usb_unlink_urb_sub(urb, 1)) {
+		/* ignore */
+	}
 	return;
 }
 
@@ -1362,6 +1339,8 @@
 	struct usb_host_endpoint *uhe = xfer->priv_sc;
 	struct usb_iso_packet_descriptor *uipd;
 
+	DPRINTF("\n");
+
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
 
@@ -1423,12 +1402,13 @@
 			TAILQ_REMOVE(&(uhe->bsd_urb_list), urb, bsd_urb_list);
 			urb->bsd_urb_list.tqe_prev = NULL;
 
-			x = usb_max_isoc_frames(urb->dev);
+			x = xfer->max_frame_count;
 			if (urb->number_of_packets > x) {
 				/* XXX simply truncate the transfer */
 				urb->number_of_packets = x;
 			}
 		} else {
+			DPRINTF("Already got a transfer\n");
 
 			/* already got a transfer (should not happen) */
 			urb = xfer->priv_fifo;
@@ -1525,19 +1505,17 @@
 	};
 	struct urb *urb = xfer->priv_fifo;
 	struct usb_host_endpoint *uhe = xfer->priv_sc;
+	uint8_t *ptr;
 	uint32_t max_bulk = xfer->max_data_length;
 	uint8_t data_frame = xfer->flags_int.control_xfr ? 1 : 0;
 
+	DPRINTF("\n");
+
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
 
 		if (xfer->flags_int.control_xfr) {
 
-			/* sanity check - should not happen */
-
-			if (xfer->aframes < xfer->nframes) {
-				goto tr_error;
-			}
 			/* don't transfer the setup packet again: */
 
 			xfer->frlengths[0] = 0;
@@ -1562,7 +1540,6 @@
 				urb->status = 0;
 			}
 		} else {
-
 			/* check remainder */
 			if (urb->bsd_length_rem > 0) {
 				goto setup_bulk;
@@ -1576,7 +1553,6 @@
 
 	case USB_ST_SETUP:
 tr_setup:
-
 		/* get next transfer */
 		urb = TAILQ_FIRST(&(uhe->bsd_urb_list));
 		if (urb == NULL) {
@@ -1607,18 +1583,21 @@
 
 			xfer->frlengths[0] = REQ_SIZE;
 
-			/* setup data transfer direction */
+			ptr = urb->setup_packet;
 
-			urb->bsd_isread = (((uint8_t *)(urb->setup_packet))[0] & UT_READ) ? 1 : 0;
+			/* setup data transfer direction and length */
+			urb->bsd_isread = (ptr[0] & UT_READ) ? 1 : 0;
+			urb->bsd_length_rem = ptr[6] | (ptr[7] << 8);
 
 		} else {
 
 			/* setup data transfer direction */
 
-			urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0;
+			urb->bsd_length_rem = urb->transfer_buffer_length;
+			urb->bsd_isread = (uhe->desc.bEndpointAddress &
+			    UE_DIR_IN) ? 1 : 0;
 		}
 
-		urb->bsd_length_rem = urb->transfer_buffer_length;
 		urb->bsd_data_ptr = urb->transfer_buffer;
 		urb->actual_length = 0;
 
@@ -1658,7 +1637,6 @@
 		return;
 
 	default:
-tr_error:
 		if (xfer->error == USB_ERR_CANCELLED) {
 			urb->status = -ECONNRESET;
 		} else {
@@ -1676,6 +1654,5 @@
 			return;
 		}
 		goto tr_setup;
-
 	}
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200808071341.m77DfSI3051427>