Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 7 Jul 2008 22:14:43 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 144851 for review
Message-ID:  <200807072214.m67MEhnD018168@repoman.freebsd.org>

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

Change 144851 by hselasky@hselasky_laptop001 on 2008/07/07 22:13:49

	
	More patches to support LibUSB 1.0.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.c#12 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.h#7 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_generic.c#10 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_generic.h#3 edit
.. //depot/projects/usb/src/sys/dev/usb2/include/usb2_ioctl.h#7 edit

Differences ...

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

@@ -77,7 +77,6 @@
 static void usb2_dev_init_post(void *arg);
 static void usb2_dev_uninit(void *arg);
 static int usb2_fifo_uiomove(struct usb2_fifo *f, void *cp, int n, struct uio *uio);
-static void usb2_fifo_wakeup(struct usb2_fifo *f);
 static void usb2_fifo_check_methods(struct usb2_fifo_methods *pm);
 static void usb2_clone(void *arg, USB_UCRED char *name, int namelen, struct cdev **dev);
 static struct usb2_fifo *usb2_fifo_alloc(void);
@@ -907,21 +906,77 @@
 }
 
 /*------------------------------------------------------------------------*
+ *	usb2_check_thread_perm
+ *
+ * Returns:
+ * 0: Has permission.
+ * Else: No permission.
+ *------------------------------------------------------------------------*/
+int
+usb2_check_thread_perm(struct usb2_device *udev, struct thread *td,
+    int fflags, uint8_t iface_index, uint8_t ep_index)
+{
+	struct usb2_perm perm;
+	struct usb2_interface *iface;
+	int err;
+
+	iface = usb2_get_iface(udev, iface_index);
+	if (iface == NULL) {
+		return (EINVAL);
+	}
+	if (iface->idesc == NULL) {
+		return (EINVAL);
+	}
+	/* set default value */
+	bzero(&perm, sizeof(perm));
+
+	/* create a permissions mask */
+	perm.uid = td->td_ucred->cr_ruid;
+	perm.gid = td->td_ucred->cr_rgid;
+	perm.mode = 0;
+	if (fflags & FREAD)
+		perm.mode |= 0444;
+	if (fflags & FWRITE)
+		perm.mode |= 0222;
+	perm.active = 1;
+
+	mtx_lock(udev->default_mtx);
+
+	/* scan down the permissions tree */
+	if ((ep_index != 0) && iface &&
+	    usb2_match_perm(&perm, &iface->perm)) {
+		/* we got access through the interface */
+		err = 0;
+	} else if (udev && usb2_match_perm(&perm, &udev->perm)) {
+		/* we got access through the device */
+		err = 0;
+	} else if (udev->bus && usb2_match_perm(&perm, &(udev->bus->perm))) {
+		/* we got access through the USB bus */
+		err = 0;
+	} else if (usb2_match_perm(&perm, &usb2_perm)) {
+		/* we got general access */
+		err = 0;
+	} else {
+		/* no access */
+		err = EPERM;
+	}
+	mtx_unlock(udev->default_mtx);
+	return (err);
+}
+
+/*------------------------------------------------------------------------*
  *	usb2_fdopen - cdev callback
  *------------------------------------------------------------------------*/
 static int
 usb2_fdopen(struct cdev *dev, int xxx_oflags, struct thread *td, struct file *fp)
 {
 	struct usb2_location loc;
-	struct usb2_perm perm;
 	uint32_t devloc;
 	int err;
 	int fflags;
 
 	DPRINTF(1, "oflags=0x%08x\n", xxx_oflags);
 
-	bzero(&perm, sizeof(perm));
-
 	devloc = usb2_last_devloc;
 	usb2_last_devloc = (0 - 1);	/* reset "usb2_devloc" */
 
@@ -960,39 +1015,9 @@
 		DPRINTF(1, "cannot ref device\n");
 		return (ENXIO);
 	}
-	/* create a permissions mask */
-	perm.uid = td->td_ucred->cr_ruid;
-	perm.gid = td->td_ucred->cr_rgid;
-	perm.mode = 0;
-	if (fflags & FREAD)
-		perm.mode |= 0444;
-	if (fflags & FWRITE)
-		perm.mode |= 0222;
-	perm.active = 1;
-
-	mtx_lock(loc.udev->default_mtx);
+	err = usb2_check_thread_perm(loc.udev, td, fflags,
+	    loc.iface_index, loc.ep_index);
 
-	/* scan down the permissions tree */
-	if ((loc.ep_index != 0) && loc.iface &&
-	    usb2_match_perm(&perm, &loc.iface->perm)) {
-		/* we got access through the interface */
-		err = 0;
-	} else if (loc.udev && usb2_match_perm(&perm, &loc.udev->perm)) {
-		/* we got access through the device */
-		err = 0;
-	} else if (loc.bus && usb2_match_perm(&perm, &loc.bus->perm)) {
-		/* we got access through the USB bus */
-		err = 0;
-	} else if (usb2_match_perm(&perm, &usb2_perm)) {
-		/* we got general access */
-		err = 0;
-	} else {
-		/* no access */
-		err = EPERM;
-	}
-
-	mtx_unlock(loc.udev->default_mtx);
-
 	/* check for error */
 	if (err) {
 		usb2_unref_device(&loc);
@@ -1281,7 +1306,6 @@
 	return (ENXIO);
 }
 
-
 /* ARGSUSED */
 static int
 usb2_poll_f(struct file *fp, int events, struct ucred *cred, struct thread *td)
@@ -1297,17 +1321,18 @@
 		return (POLLHUP);
 	}
 	fflags = fp->f_flag;
-	f = loc.txfifo;
 
 	if ((events & (POLLOUT | POLLWRNORM)) &&
 	    (fflags & FWRITE)) {
 
+		f = loc.txfifo;
+
 		mtx_lock(f->priv_mtx);
 
 		/* check if any packets are available */
 		USB_IF_POLL(&(f->free_q), m);
 
-		if (f->flag_iserror || m) {
+		if (f->flag_iserror || f->flag_iscomplete || m) {
 			revents |= events & (POLLOUT | POLLWRNORM);
 		} else {
 			f->flag_isselect = 1;
@@ -1316,17 +1341,17 @@
 
 		mtx_unlock(f->priv_mtx);
 	}
-	f = loc.rxfifo;
-
 	if ((events & (POLLIN | POLLRDNORM)) &&
 	    (fflags & FREAD)) {
 
+		f = loc.rxfifo;
+
 		mtx_lock(f->priv_mtx);
 
 		/* check if any packets are available */
 		USB_IF_POLL(&(f->used_q), m);
 
-		if (f->flag_iserror || m) {
+		if (f->flag_iserror || f->flag_iscomplete || m) {
 			revents |= events & (POLLIN | POLLRDNORM);
 		} else {
 			f->flag_isselect = 1;
@@ -1381,8 +1406,6 @@
 		err = EIO;
 		goto done;
 	}
-	/* XXX TODO: support IO-vectors */
-
 	while (uio->uio_resid > 0) {
 
 		USB_IF_DEQUEUE(&(f->used_q), m);
@@ -1505,8 +1528,6 @@
 		err = EIO;
 		goto done;
 	}
-	/* XXX TODO: support IO-vectors */
-
 	while (uio->uio_resid > 0) {
 
 		USB_IF_DEQUEUE(&(f->free_q), m);
@@ -1613,7 +1634,7 @@
 	return;
 }
 
-static void
+void
 usb2_fifo_wakeup(struct usb2_fifo *f)
 {
 	usb2_fifo_signal(f);

==== //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.h#7 (text+ko) ====

@@ -74,8 +74,10 @@
 	struct usb2_fifo_methods *methods;
 	struct cdev *symlink[2];	/* our symlinks */
 	struct proc *async_p;		/* process that wants SIGIO */
+	struct usb2_fs_endpoint *fs_ep_ptr;
 	struct usb2_device *udev;
 	struct usb2_xfer *xfer[2];
+	struct usb2_xfer **fs_xfer;
 	struct mtx *priv_mtx;		/* client data */
 	struct file *curr_file;		/* set if FIFO is opened by a FILE */
 	void   *priv_sc0;		/* client data */
@@ -87,6 +89,7 @@
 	uint16_t dev_ep_index;		/* our device endpoint index */
 	uint8_t	flag_no_uref;		/* set if FIFO is not control endpoint */
 	uint8_t	flag_sleeping;		/* set if FIFO is sleeping */
+	uint8_t	flag_iscomplete;	/* set if a USB transfer is complete */
 	uint8_t	flag_iserror;		/* set if FIFO error happened */
 	uint8_t	flag_isselect;		/* set if FIFO is selected */
 	uint8_t	flag_flushing;		/* set if FIFO is flushing data */
@@ -96,6 +99,7 @@
 	uint8_t	iface_index;		/* set to the interface we belong to */
 	uint8_t	fifo_index;		/* set to the FIFO index in "struct
 					 * usb2_device" */
+	uint8_t	fs_ep_max;
 	uint8_t	fifo_zlp;		/* zero length packet count */
 	uint8_t	refcount;
 #define	USB_FIFO_REF_MAX 0xFF
@@ -121,5 +125,7 @@
 uint8_t	usb2_fifo_opened(struct usb2_fifo *fifo);
 void	usb2_fifo_free(struct usb2_fifo *f);
 void	usb2_fifo_reset(struct usb2_fifo *f);
+int	usb2_check_thread_perm(struct usb2_device *udev, struct thread *td, int fflags, uint8_t iface_index, uint8_t ep_index);
+void	usb2_fifo_wakeup(struct usb2_fifo *f);
 
 #endif					/* _USB2_DEV_H_ */

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

@@ -43,6 +43,7 @@
 #include <dev/usb2/core/usb2_util.h>
 #include <dev/usb2/core/usb2_hub.h>
 #include <dev/usb2/core/usb2_generic.h>
+#include <dev/usb2/core/usb2_transfer.h>
 
 #include <dev/usb2/controller/usb2_controller.h>
 #include <dev/usb2/controller/usb2_bus.h>
@@ -61,6 +62,7 @@
 static usb2_callback_t ugen_default_write_callback;
 static usb2_callback_t ugen_isoc_read_callback;
 static usb2_callback_t ugen_isoc_write_callback;
+static usb2_callback_t ugen_default_fs_callback;
 
 static usb2_fifo_open_t ugen_open;
 static usb2_fifo_close_t ugen_close;
@@ -78,8 +80,11 @@
 static int ugen_get_sdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd);
 static int usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di);
 static int ugen_re_enumerate(struct usb2_fifo *f);
-static int ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr);
+static int ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags);
 static int ugen_ctrl_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags);
+static int ugen_fs_uninit(struct usb2_fifo *f);
+static uint8_t ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex);
+
 
 /* structures */
 
@@ -184,6 +189,10 @@
 
 	usb2_transfer_unsetup(f->xfer, 2);
 	usb2_fifo_free_buffer(f);
+
+	if (ugen_fs_uninit(f)) {
+		/* ignore any errors - we are closing */
+	}
 	return;
 }
 
@@ -200,6 +209,10 @@
 		/* transfers are already opened */
 		return (0);
 	}
+	if (f->fs_xfer) {
+		/* should not happen */
+		return (EINVAL);
+	}
 	bzero(usb2_config, sizeof(usb2_config));
 
 	usb2_config[1].type = UE_CONTROL;
@@ -230,8 +243,8 @@
 		if (ugen_transfer_setup(f, usb2_config, 2)) {
 			return (EIO);
 		}
-		/* first transfer clears stall */
-		f->flag_stall = 1;
+		/* first transfer does not clear stall */
+		f->flag_stall = 0;
 		break;
 
 	case UE_ISOCHRONOUS:
@@ -269,6 +282,10 @@
 		/* transfers are already opened */
 		return (0);
 	}
+	if (f->fs_xfer) {
+		/* should not happen */
+		return (EINVAL);
+	}
 	bzero(usb2_config, sizeof(usb2_config));
 
 	usb2_config[1].type = UE_CONTROL;
@@ -300,8 +317,8 @@
 		if (ugen_transfer_setup(f, usb2_config, 2)) {
 			return (EIO);
 		}
-		/* first transfer clears stall */
-		f->flag_stall = 1;
+		/* first transfer does not clear stall */
+		f->flag_stall = 0;
 		break;
 
 	case UE_ISOCHRONOUS:
@@ -793,26 +810,25 @@
 	return (0);
 }
 
-int
-ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur)
+/*------------------------------------------------------------------------*
+ *	ugen_check_request
+ *
+ * Return values:
+ * 0: Access allowed
+ * Else: No access
+ *------------------------------------------------------------------------*/
+static int
+ugen_check_request(struct usb2_device_request *req)
 {
-	int error;
-	uint16_t len;
-	uint16_t actlen;
-	uint8_t isread;
-	void *data = NULL;
-
-	if (f->flag_no_uref) {
-		/* control endpoint only */
-		return (EINVAL);
-	}
-	/* avoid requests that would damage the bus integrity */
-	if (((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE) &&
-	    (ur->ucr_request.bRequest == UR_SET_ADDRESS)) ||
-	    ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE) &&
-	    (ur->ucr_request.bRequest == UR_SET_CONFIG)) ||
-	    ((ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE) &&
-	    (ur->ucr_request.bRequest == UR_SET_INTERFACE))) {
+	/*
+	 * Avoid requests that would damage the bus integrity:
+	 */
+	if (((req->bmRequestType == UT_WRITE_DEVICE) &&
+	    (req->bRequest == UR_SET_ADDRESS)) ||
+	    ((req->bmRequestType == UT_WRITE_DEVICE) &&
+	    (req->bRequest == UR_SET_CONFIG)) ||
+	    ((req->bmRequestType == UT_WRITE_INTERFACE) &&
+	    (req->bRequest == UR_SET_INTERFACE))) {
 		if (suser(curthread)) {
 			return (EPERM);
 		}
@@ -821,13 +837,32 @@
 	 * Clearing the stall this way is not allowed, hence it does
 	 * not update the data toggle value in "struct usb2_pipe" !
 	 */
-	if (ur->ucr_request.bmRequestType == UT_WRITE_ENDPOINT) {
+	if (req->bmRequestType == UT_WRITE_ENDPOINT) {
 		if (suser(curthread)) {
 			return (EPERM);
 		}
 	}
 	/* TODO: add more checks to verify the interface index */
 
+	return (0);
+}
+
+int
+ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur)
+{
+	int error;
+	uint16_t len;
+	uint16_t actlen;
+	uint8_t isread;
+	void *data = NULL;
+
+	if (f->flag_no_uref) {
+		/* control endpoint only */
+		return (EINVAL);
+	}
+	if (ugen_check_request(&ur->ucr_request)) {
+		return (EPERM);
+	}
 	len = UGETW(ur->ucr_request.wLength);
 	isread = (ur->ucr_request.bmRequestType & UT_READ) ? 1 : 0;
 
@@ -901,7 +936,648 @@
 }
 
 static int
-ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr)
+ugen_fs_uninit(struct usb2_fifo *f)
+{
+	if (f->fs_xfer == NULL) {
+		return (EINVAL);
+	}
+	usb2_transfer_unsetup(f->fs_xfer, f->fs_ep_max);
+	free(f->fs_xfer, M_USB);
+	f->fs_xfer = NULL;
+	f->fs_ep_max = 0;
+	f->fs_ep_ptr = NULL;
+	f->flag_iscomplete = 0;
+	usb2_fifo_free_buffer(f);
+	return (0);
+}
+
+static uint8_t
+ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex)
+{
+	struct usb2_mbuf *m;
+
+	USB_IF_DEQUEUE(&(f->used_q), m);
+
+	if (m) {
+		*pindex = *((uint8_t *)(m->cur_data_ptr));
+
+		USB_IF_ENQUEUE(&(f->free_q), m);
+
+		return (0);		/* success */
+	} else {
+		f->flag_iscomplete = 0;
+	}
+	return (1);			/* failure */
+}
+
+static void
+ugen_fs_set_complete(struct usb2_fifo *f, uint8_t index)
+{
+	struct usb2_mbuf *m;
+
+	USB_IF_DEQUEUE(&(f->free_q), m);
+
+	USB_MBUF_RESET(m);
+
+	*((uint8_t *)(m->cur_data_ptr)) = index;
+
+	USB_IF_ENQUEUE(&(f->used_q), m);
+
+	f->flag_iscomplete = 1;
+
+	usb2_fifo_wakeup(f);
+
+	return;
+}
+
+static int
+ugen_fs_copy_in(struct usb2_fifo *f, uint8_t ep_index)
+{
+	struct usb2_device_request *req;
+	struct usb2_xfer *xfer;
+	struct usb2_fs_endpoint fs_ep;
+	void *uaddr;
+	uint32_t offset;
+	uint32_t length;
+	uint32_t n;
+	uint32_t rem;
+	int error;
+	uint8_t isread;
+
+	if (ep_index >= f->fs_ep_max) {
+		return (EINVAL);
+	}
+	xfer = f->fs_xfer[ep_index];
+	if (xfer == NULL) {
+		return (EINVAL);
+	}
+	mtx_lock(f->priv_mtx);
+	if (usb2_transfer_pending(xfer)) {
+		mtx_unlock(f->priv_mtx);
+		return (EBUSY);		/* should not happen */
+	}
+	mtx_unlock(f->priv_mtx);
+
+	/* security checks */
+
+	if (fs_ep.nFrames > xfer->max_frame_count) {
+		return (EINVAL);
+	}
+	if (fs_ep.nFrames == 0) {
+		return (EINVAL);
+	}
+	error = copyin(f->fs_ep_ptr +
+	    ep_index, &fs_ep, sizeof(fs_ep));
+	if (error) {
+		return (error);
+	}
+	error = copyin(fs_ep.ppBuffer,
+	    &uaddr, sizeof(uaddr));
+	if (error) {
+		return (error);
+	}
+	/* reset first frame */
+	usb2_set_frame_offset(xfer, 0, 0);
+
+	if (xfer->flags_int.control_xfr) {
+
+		req = xfer->frbuffers[0].buffer;
+
+		error = copyin(fs_ep.pLength,
+		    &length, sizeof(length));
+		if (error) {
+			return (error);
+		}
+		if (length >= sizeof(*req)) {
+			return (EINVAL);
+		}
+		if (length) {
+			error = copyin(uaddr, req, length);
+			if (error) {
+				return (error);
+			}
+		}
+		if (ugen_check_request(req)) {
+			return (EPERM);
+		}
+		xfer->frlengths[0] = length;
+
+		/* Host mode only ! */
+		if ((req->bmRequestType &
+		    (UT_READ | UT_WRITE)) == UT_READ) {
+			isread = 1;
+		} else {
+			isread = 0;
+		}
+		n = 1;
+		offset = sizeof(*req);
+
+	} else {
+		/* Device and Host mode */
+		if (USB_GET_DATA_ISREAD(xfer)) {
+			isread = 1;
+		} else {
+			isread = 0;
+		}
+		n = 0;
+		offset = 0;
+	}
+
+	rem = xfer->max_data_length;
+	xfer->nframes = fs_ep.nFrames;
+	xfer->timeout = fs_ep.timeout;
+	if (xfer->timeout > 65535) {
+		xfer->timeout = 65535;
+	}
+	if (fs_ep.flags & USB2_FS_FLAG_SINGLE_SHORT_OK)
+		xfer->flags.short_xfer_ok = 1;
+	else
+		xfer->flags.short_xfer_ok = 0;
+
+	if (fs_ep.flags & USB2_FS_FLAG_MULTI_SHORT_OK)
+		xfer->flags.short_frames_ok = 1;
+	else
+		xfer->flags.short_frames_ok = 0;
+
+	if (fs_ep.flags & USB2_FS_FLAG_FORCE_SHORT)
+		xfer->flags.force_short_xfer = 1;
+	else
+		xfer->flags.force_short_xfer = 0;
+
+	if (fs_ep.flags & USB2_FS_FLAG_CLEAR_STALL)
+		xfer->flags.stall_pipe = 1;
+	else
+		xfer->flags.stall_pipe = 0;
+
+	for (; n != xfer->nframes; n++) {
+
+		error = copyin(fs_ep.pLength + n,
+		    &length, sizeof(length));
+		if (error) {
+			return (error);
+		}
+		xfer->frlengths[n] = length;
+
+		if (length > rem) {
+			return (EINVAL);
+		}
+		rem -= length;
+
+		if (!isread) {
+
+			if (xfer->flags_int.isochronous_xfr) {
+
+				/* move data */
+				error = copyin(USB_ADD_BYTES(uaddr, offset),
+				    USB_ADD_BYTES(xfer->frbuffers[0].buffer,
+				    offset), length);
+				if (error) {
+					return (error);
+				}
+			} else {
+				/* we need to know the source buffer */
+				error = copyin(fs_ep.ppBuffer + n,
+				    &uaddr, sizeof(uaddr));
+				if (error) {
+					return (error);
+				}
+				/* set current frame offset */
+				usb2_set_frame_offset(xfer, offset, n);
+
+				/* move data */
+				error = copyin(uaddr, xfer->frbuffers[n].buffer,
+				    length);
+				if (error) {
+					return (error);
+				}
+			}
+		}
+		offset += length;
+	}
+	return (error);
+}
+
+static int
+ugen_fs_copy_out(struct usb2_fifo *f, uint8_t ep_index)
+{
+	struct usb2_device_request *req;
+	struct usb2_xfer *xfer;
+	struct usb2_fs_endpoint fs_ep;
+	void *uaddr;
+	uint32_t offset;
+	uint32_t length;
+	uint32_t temp;
+	uint32_t n;
+	uint32_t rem;
+	int error;
+
+	if (ep_index >= f->fs_ep_max) {
+		return (EINVAL);
+	}
+	xfer = f->fs_xfer[ep_index];
+	if (xfer == NULL) {
+		return (EINVAL);
+	}
+	mtx_lock(f->priv_mtx);
+	if (usb2_transfer_pending(xfer)) {
+		mtx_unlock(f->priv_mtx);
+		return (EBUSY);		/* should not happen */
+	}
+	mtx_unlock(f->priv_mtx);
+
+	error = copyin(f->fs_ep_ptr +
+	    ep_index, &fs_ep, sizeof(fs_ep));
+	if (error) {
+		return (error);
+	}
+	fs_ep.status = xfer->error;
+	fs_ep.aFrames = xfer->aframes;
+	if (xfer->error) {
+		goto complete;
+	}
+	error = copyin(fs_ep.ppBuffer,
+	    &uaddr, sizeof(uaddr));
+	if (error) {
+		return (error);
+	}
+	if (xfer->flags_int.control_xfr) {
+		req = xfer->frbuffers[0].buffer;
+
+		/* Host mode only ! */
+		if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_WRITE) {
+			goto complete;
+		}
+		n = 1;
+	} else {
+		/* Device and Host mode */
+		if (!USB_GET_DATA_ISREAD(xfer)) {
+			goto complete;
+		}
+		n = 0;
+	}
+
+	rem = xfer->max_data_length;
+
+	for (; n != xfer->nframes; n++) {
+
+		if (xfer->flags_int.isochronous_xfr) {
+
+			/* we need to know the initial length */
+			error = copyin(fs_ep.pLength + n,
+			    &length, sizeof(length));
+			if (error) {
+				return (error);
+			}
+			/* range check */
+			if (length > rem) {
+				return (EINVAL);
+			}
+			rem -= length;
+			temp = offset + length;
+
+			/* limit */
+			if (length > xfer->frlengths[n]) {
+				length = xfer->frlengths[n];
+			}
+			/* move data */
+			error = copyout(USB_ADD_BYTES(xfer->frbuffers[0].buffer,
+			    offset), USB_ADD_BYTES(uaddr, offset), length);
+			if (error) {
+				return (error);
+			}
+			offset = temp;
+		} else {
+
+			length = xfer->frlengths[n];
+
+			/* we need to know the destination buffer */
+			error = copyin(fs_ep.ppBuffer + n,
+			    &uaddr, sizeof(uaddr));
+			if (error) {
+				return (error);
+			}
+			/* move data */
+			error = copyout(xfer->frbuffers[n].buffer,
+			    uaddr, length);
+			if (error) {
+				return (error);
+			}
+		}
+
+		/* update length */
+		error = copyout(&length,
+		    fs_ep.pLength + n, sizeof(length));
+		if (error) {
+			return (error);
+		}
+	}
+
+complete:
+	/* update "aFrames" */
+	error = copyout(&fs_ep.aFrames, &(f->fs_ep_ptr +
+	    ep_index)->aFrames, sizeof(fs_ep.aFrames));
+
+	if (error) {
+		return (error);
+	}
+	/* update "status" */
+	error = copyout(&fs_ep.status, &(f->fs_ep_ptr +
+	    ep_index)->status, sizeof(fs_ep.status));
+	return (error);
+}
+
+static uint8_t
+ugen_fifo_in_use(struct usb2_fifo *f, int fflags)
+{
+	struct usb2_fifo *f_rx;
+	struct usb2_fifo *f_tx;
+
+	f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX];
+	f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX];
+
+	if ((fflags & FREAD) && f_rx &&
+	    (f_rx->xfer[0] || f_rx->xfer[1])) {
+		return (1);		/* RX FIFO in use */
+	}
+	if ((fflags & FWRITE) && f_tx &&
+	    (f_tx->xfer[0] || f_tx->xfer[1])) {
+		return (1);		/* TX FIFO in use */
+	}
+	return (0);			/* not in use */
+}
+
+static int
+ugen_fs_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case USB_FS_COMPLETE:{
+			struct usb2_fs_complete *pd = addr;
+			uint8_t ep_index;
+
+			mtx_lock(f->priv_mtx);
+			error = ugen_fs_get_complete(f, &ep_index);
+			mtx_unlock(f->priv_mtx);
+
+			if (error) {
+				error = EBUSY;
+				break;
+			}
+			pd->ep_index = ep_index;
+			error = ugen_fs_copy_out(f, pd->ep_index);
+			break;
+		}
+
+	case USB_FS_START:{
+			struct usb2_fs_start *pd = addr;
+
+			error = ugen_fs_copy_in(f, pd->ep_index);
+			if (error) {
+				break;
+			}
+			mtx_lock(f->priv_mtx);
+			usb2_transfer_start(f->fs_xfer[pd->ep_index]);
+			mtx_lock(f->priv_mtx);
+			break;
+		}
+	case USB_FS_STOP:{
+			struct usb2_fs_stop *pd = addr;
+
+			if (pd->ep_index >= f->fs_ep_max) {
+				error = EINVAL;
+				break;
+			}
+			mtx_lock(f->priv_mtx);
+			usb2_transfer_stop(f->fs_xfer[pd->ep_index]);
+			mtx_unlock(f->priv_mtx);
+			break;
+		}
+	case USB_FS_INIT:{
+			struct usb2_fs_init *pd = addr;
+
+			/* verify input parameters */
+			if (pd->pEndpoints == NULL) {
+				error = EINVAL;
+				break;
+			}
+			if (pd->ep_index_max > 127) {
+				error = EINVAL;
+				break;
+			}
+			if (pd->ep_index_max == 0) {
+				error = EINVAL;
+				break;
+			}
+			if (f->fs_xfer != NULL) {
+				error = EBUSY;
+				break;
+			}
+			if (f->dev_ep_index != 0) {
+				error = EINVAL;
+				break;
+			}
+			if (ugen_fifo_in_use(f, fflags)) {
+				error = EBUSY;
+				break;
+			}
+			error = usb2_fifo_alloc_buffer(f, 1, pd->ep_index_max);
+			if (error) {
+				break;
+			}
+			f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) *
+			    pd->ep_index_max, M_USB, M_WAITOK | M_ZERO);
+			if (f->fs_xfer == NULL) {
+				usb2_fifo_free_buffer(f);
+				error = ENOMEM;
+				break;
+			}
+			f->fs_ep_max = pd->ep_index_max;
+			f->fs_ep_ptr = pd->pEndpoints;
+			f->flag_no_uref = 1;	/* drop locks we don't need */
+			break;
+		}
+
+	case USB_FS_UNINIT:{
+			struct usb2_fs_uninit *pd = addr;
+
+			if (pd->dummy != 0) {
+				error = EINVAL;
+				break;
+			}
+			error = ugen_fs_uninit(f);
+			if (error == 0) {
+				f->flag_no_uref = 0;	/* restore operation */
+			}
+			break;
+		}
+
+	case USB_FS_OPEN:{
+			struct usb2_config usb2_config[1];
+			struct usb2_fs_open *pd = addr;
+			struct usb2_pipe *pipe;
+			struct usb2_endpoint_descriptor *ed;
+			uint8_t iface_index;
+			uint8_t isread;
+
+			if (pd->ep_index >= f->fs_ep_max) {
+				error = EINVAL;
+				break;
+			}
+			if (f->fs_xfer[pd->ep_index] != NULL) {
+				error = EBUSY;
+				break;
+			}
+			if (pd->max_bufsize > USB_FS_MAX_BUFSIZE) {
+				error = EINVAL;
+				break;
+			}
+			if (pd->max_frames > USB_FS_MAX_FRAMES) {
+				error = EINVAL;
+				break;
+			}
+			if (pd->max_frames == 0) {
+				error = EINVAL;
+				break;
+			}
+			pipe = usb2_get_pipe_by_addr(f->udev, pd->ep_no);
+			if (pipe == NULL) {
+				error = EINVAL;
+				break;
+			}
+			ed = pipe->edesc;
+			if (ed == NULL) {
+				error = ENXIO;
+				break;
+			}
+			iface_index = pipe->iface_index;
+
+			error = usb2_check_thread_perm(f->udev, curthread, fflags,
+			    iface_index, pd->ep_no);
+			if (error) {
+				break;
+			}
+			bzero(usb2_config, sizeof(usb2_config));
+
+			usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE;
+			usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
+			usb2_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN);
+			usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL;
+			usb2_config[0].mh.flags.proxy_buffer = 1;
+			usb2_config[0].mh.callback = &ugen_default_fs_callback;
+			usb2_config[0].mh.timeout = 0;	/* no timeout */
+			usb2_config[0].mh.frames = pd->max_frames;
+			usb2_config[0].mh.bufsize = pd->max_bufsize;
+			usb2_config[0].md = usb2_config[0].mh;	/* symmetric config */
+
+			if (usb2_config[0].type == UE_CONTROL) {
+				if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+					error = EINVAL;
+					break;
+				}
+			} else {
+
+				isread = ((usb2_config[0].endpoint &
+				    (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN);
+
+				if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+					isread = !isread;
+				}
+				/* check permissions */
+				if (isread) {
+					if (!(fflags & FREAD)) {
+						error = EPERM;
+						break;
+					}
+				} else {
+					if (!(fflags & FWRITE)) {
+						error = EPERM;
+						break;
+					}
+				}
+			}
+			error = usb2_transfer_setup(f->udev, &iface_index,
+			    f->fs_xfer + pd->ep_index, usb2_config, 1,
+			    f, f->priv_mtx);
+			if (error == 0) {
+				/* update maximum buffer size */
+				pd->max_bufsize =

>>> TRUNCATED FOR MAIL (1000 lines) <<<



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