Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Jul 2009 00:15:50 +0000 (UTC)
From:      Alfred Perlstein <alfred@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r195963 - head/sys/dev/usb
Message-ID:  <200907300015.n6U0FoxE086498@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: alfred
Date: Thu Jul 30 00:15:50 2009
New Revision: 195963
URL: http://svn.freebsd.org/changeset/base/195963

Log:
  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

Modified:
  head/sys/dev/usb/usb_dev.c
  head/sys/dev/usb/usb_dev.h
  head/sys/dev/usb/usb_device.c
  head/sys/dev/usb/usb_handle_request.c
  head/sys/dev/usb/usb_hub.c
  head/sys/dev/usb/usb_parse.c
  head/sys/dev/usb/usb_request.c

Modified: head/sys/dev/usb/usb_dev.c
==============================================================================
--- head/sys/dev/usb/usb_dev.c	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_dev.c	Thu Jul 30 00:15:50 2009	(r195963)
@@ -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 f
 			/* 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 *
 	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 *
 		}
 		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->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)
 {

Modified: head/sys/dev/usb/usb_dev.h
==============================================================================
--- head/sys/dev/usb/usb_dev.h	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_dev.h	Thu Jul 30 00:15:50 2009	(r195963)
@@ -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_ */

Modified: head/sys/dev/usb/usb_device.c
==============================================================================
--- head/sys/dev/usb/usb_device.c	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_device.c	Thu Jul 30 00:15:50 2009	(r195963)
@@ -833,18 +833,13 @@ usbd_set_alt_interface_index(struct usb_
 		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_
 	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_dev
 {
 	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_dev
 			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 *
 		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 *
 		/* 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
 	    "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
 	    udev->ddesc.bDeviceClass,
 	    udev->ddesc.bDeviceSubClass,
 	    udev->serial,
+	    UGETW(udev->ddesc.bcdDevice),
 	    udev->port_no,
 	    udev->parent_hub != NULL ?
 		udev->parent_hub->ugen_name :

Modified: head/sys/dev/usb/usb_handle_request.c
==============================================================================
--- head/sys/dev/usb/usb_handle_request.c	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_handle_request.c	Thu Jul 30 00:15:50 2009	(r195963)
@@ -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;
 		}

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_hub.c	Thu Jul 30 00:15:50 2009	(r195963)
@@ -1020,12 +1020,14 @@ uhub_child_pnpinfo_string(device_t paren
 		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 {

Modified: head/sys/dev/usb/usb_parse.c
==============================================================================
--- head/sys/dev/usb/usb_parse.c	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_parse.c	Thu Jul 30 00:15:50 2009	(r195963)
@@ -215,20 +215,29 @@ usbd_get_no_descriptors(struct usb_confi
  *	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_descr
 				n++;
 				if (n == 0xFF)
 					break;		/* crazy */
-			} else
-				break;			/* end */
+			}
 		}
 	}
 	return (n);

Modified: head/sys/dev/usb/usb_request.c
==============================================================================
--- head/sys/dev/usb/usb_request.c	Thu Jul 30 00:15:17 2009	(r195962)
+++ head/sys/dev/usb/usb_request.c	Thu Jul 30 00:15:50 2009	(r195963)
@@ -1059,9 +1059,9 @@ usbd_req_get_alt_interface_no(struct usb
 	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
 	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;



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