ugen(4) fixes

Hasso Tepper hasso at estpak.ee
Mon Apr 28 07:41:24 PDT 2008


Attached patch contains some assorted fixes for ugen(4) from FreeBSD. It 
should be quite straightforward and shouldn't break anything, but please 
test and/or review before I'm going to commit this.


-- 
Hasso Tepper
# HG changeset patch
# User Hasso Tepper <hasso at estpak.ee>
# Date 1209385630 -10800
# Branch HEAD
# Node ID 172a4bc22c661b8a202e4575f8367710897275aa
# Parent  370bed0112470e3f559bc0051d719faba980e22a
[mq]: ugen-fixes.patch

diff --git a/sys/dev/usbmisc/ugen/ugen.c b/sys/dev/usbmisc/ugen/ugen.c
--- a/sys/dev/usbmisc/ugen/ugen.c
+++ b/sys/dev/usbmisc/ugen/ugen.c
@@ -250,6 +250,7 @@
 	make_dev(&ugen_ops, UGENMINOR(device_get_unit(sc->sc_dev), 0),
 		UID_ROOT, GID_OPERATOR, 0644, "%s", device_get_nameunit(sc->sc_dev));
 
+	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
 	return 0;
 }
 
@@ -294,8 +295,11 @@
 static void
 ugen_destroy_devnodes(struct ugen_softc *sc)
 {
-	int endptno;
+	int endptno, prev_sc_dying;
 	cdev_t dev;
+
+	prev_sc_dying = sc->sc_dying;
+	sc->sc_dying = 1;
 
 	/* destroy all devices for the other (existing) endpoints as well */
 	for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) {
@@ -321,6 +325,7 @@
 			}
 		}
 	}
+	sc->sc_dying = prev_sc_dying;
 }
 
 static int
@@ -413,7 +418,7 @@
  	DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
 		     ap->a_oflags, ap->a_devtype, unit, endpt));
 
-	if (sc == NULL || sc->sc_dying)
+	if (sc->sc_dying)
 		return (ENXIO);
 
 	if (sc->sc_is_open[endpt])
@@ -567,7 +572,7 @@
 		if (!(ap->a_fflag & (dir == OUT ? FWRITE : FREAD)))
 			continue;
 		sce = &sc->sc_endpoints[endpt][dir];
-		if (sce == NULL || sce->pipeh == NULL)
+		if (sce->pipeh == NULL)
 			continue;
 		DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
 			     endpt, dir, sce));
@@ -619,9 +624,7 @@
 	if (endpt == USB_CONTROL_ENDPOINT)
 		return (ENODEV);
 
-	if (sce == NULL)
-		return (EINVAL);
-
+#ifdef DIAGNOSTIC
 	if (sce->edesc == NULL) {
 		kprintf("ugenread: no edesc\n");
 		return (EIO);
@@ -630,6 +633,7 @@
 		kprintf("ugenread: no pipe\n");
 		return (EIO);
 	}
+#endif
 
 	buf = getugenbuf(ugen_bufsize, &ugen_bbsize);
 
@@ -638,21 +642,25 @@
 		/* Block until activity occurred. */
 		crit_enter();
 		while (sce->q.c_cc == 0) {
-			if (flag & IO_NDELAY) {
+			if (flag & O_NONBLOCK) {
 				crit_exit();
 				error = EWOULDBLOCK;
 				goto done;
 			}
 			sce->state |= UGEN_ASLP;
 			DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
-			error = tsleep(sce, PCATCH, "ugenri", 0);
+			error = tsleep(sce, PCATCH, "ugenri",
+			    (sce->timeout * hz + 999) / 1000);
+			sce->state &= ~UGEN_ASLP;
 			DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
 			if (sc->sc_dying)
 				error = EIO;
-			if (error) {
-				sce->state &= ~UGEN_ASLP;
+			if (error == EAGAIN) {
+				error = 0;	/* timeout, return 0 bytes */
 				break;
 			}
+			if (error)
+				break;
 		}
 		crit_exit();
 
@@ -705,25 +713,29 @@
 	case UE_ISOCHRONOUS:
 		crit_enter();
 		while (sce->cur == sce->fill) {
-			if (flag & IO_NDELAY) {
+			if (flag & O_NONBLOCK) {
 				crit_exit();
 				error = EWOULDBLOCK;
 				goto done;
 			}
 			sce->state |= UGEN_ASLP;
 			DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
-			error = tsleep(sce, PCATCH, "ugenri", 0);
+			error = tsleep(sce, PCATCH, "ugenri",
+			    (sce->timeout * hz + 999) / 1000);
+			sce->state &= ~UGEN_ASLP;
 			DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
 			if (sc->sc_dying)
 				error = EIO;
-			if (error) {
-				sce->state &= ~UGEN_ASLP;
+			if (error == EAGAIN) {
+				error = 0;	/* timeout, return 0 bytes */
 				break;
 			}
+			if (error)
+				break;
 		}
 
 		while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
-			if(sce->fill > sce->cur)
+			if (sce->fill > sce->cur)
 				n = min(sce->fill - sce->cur, uio->uio_resid);
 			else
 				n = min(sce->limit - sce->cur, uio->uio_resid);
@@ -760,6 +772,9 @@
 	int error;
 
 	sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+
+	if (sc->sc_dying)
+		return (EIO);
 
 	sc->sc_refcnt++;
 	error = ugen_do_read(sc, endpt, ap->a_uio, ap->a_ioflag);
@@ -787,9 +802,7 @@
 	if (endpt == USB_CONTROL_ENDPOINT)
 		return (ENODEV);
 
-	if (sce == NULL)
-		return (EINVAL);
-
+#ifdef DIAGNOSTIC
 	if (sce->edesc == NULL) {
 		kprintf("ugenwrite: no edesc\n");
 		return (EIO);
@@ -798,6 +811,7 @@
 		kprintf("ugenwrite: no pipe\n");
 		return (EIO);
 	}
+#endif
 
 	buf = getugenbuf(ugen_bufsize, &ugen_bbsize);
 
@@ -872,6 +886,9 @@
 
 	sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
 
+	if (sc->sc_dying)
+		return (EIO);
+
 	sc->sc_refcnt++;
 	error = ugen_do_write(sc, endpt, ap->a_uio, ap->a_ioflag);
 	if (--sc->sc_refcnt < 0)
@@ -895,6 +912,7 @@
 			sce = &sc->sc_endpoints[i][dir];
 			if (sce && sce->pipeh)
 				usbd_abort_pipe(sce->pipeh);
+			selwakeup(&sce->rsel);
 		}
 	}
 	crit_enter();
@@ -911,6 +929,7 @@
 	ugen_destroy_devnodes(sc);
 	dev_ops_remove(&ugen_ops, 
 		    UGENUNITMASK, UGENMINOR(device_get_unit(sc->sc_dev), 0));
+	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
 	return (0);
 }
 
@@ -1146,8 +1165,6 @@
 		if (endpt == USB_CONTROL_ENDPOINT)
 			return (EINVAL);
 		sce = &sc->sc_endpoints[endpt][IN];
-		if (sce == NULL)
-			return (EINVAL);
 
 		if (sce->pipeh == NULL) {
 			kprintf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n");
@@ -1161,8 +1178,8 @@
 		return (0);
 	case USB_SET_TIMEOUT:
 		sce = &sc->sc_endpoints[endpt][IN];
-		if (sce == NULL)
-			return (EINVAL);
+		sce->timeout = *(int *)addr;
+		sce = &sc->sc_endpoints[endpt][OUT];
 		sce->timeout = *(int *)addr;
 		return (0);
 	default:
@@ -1397,6 +1414,8 @@
 	int error;
 
 	sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+	if (sc->sc_dying)
+		return (EIO);
 
 	sc->sc_refcnt++;
 	error = ugen_do_ioctl(sc, endpt, ap->a_cmd, ap->a_data, ap->a_fflag);
@@ -1410,44 +1429,61 @@
 {
 	cdev_t dev = ap->a_head.a_dev;
 	struct ugen_softc *sc;
-	struct ugen_endpoint *sce;
+	struct ugen_endpoint *sce_in, *sce_out;
+	usb_endpoint_descriptor_t *edesc;
 	int revents = 0;
 
 	sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
 
-	if (sc->sc_dying)
-		return (EIO);
-
-	/* XXX always IN */
-	sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
-	if (sce == NULL)
-		return (EINVAL);
-
-	if (!sce->edesc) {
-		kprintf("ugenpoll: no edesc\n");
-		return (EIO);
-	}
-	if (!sce->pipeh) {
-		kprintf("ugenpoll: no pipe\n");
-		return (EIO);
+	if (sc->sc_dying) {
+		return ((ap->a_events & (POLLIN | POLLOUT | POLLRDNORM |
+			POLLWRNORM)) | POLLHUP);
 	}
 
+	/* Do not allow to poll a control endpoint */
+	if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT) {
+		return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM |
+			POLLWRNORM));
+	}
+
+	sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
+	sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT];
+	edesc = (sce_in->edesc != NULL) ? sce_in->edesc : sce_out->edesc;
+	KASSERT(edesc != NULL, ("ugenpoll: NULL edesc"));
+
+	if (sce_in->edesc == NULL || sce_in->pipeh == NULL)
+		sce_in = NULL;
+	if (sce_out->edesc == NULL || sce_out->pipeh == NULL)
+		sce_out = NULL;
+
 	crit_enter();
-	switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+	switch (edesc->bmAttributes & UE_XFERTYPE) {
 	case UE_INTERRUPT:
-		if (ap->a_events & (POLLIN | POLLRDNORM)) {
-			if (sce->q.c_cc > 0)
+		if (sce_in != NULL && (ap->a_events & (POLLIN | POLLRDNORM))) {
+			if (sce_in->q.c_cc > 0)
 				revents |= ap->a_events & (POLLIN | POLLRDNORM);
 			else
-				selrecord(curthread, &sce->rsel);
+				selrecord(curthread, &sce_in->rsel);
+		}
+		if (sce_out != NULL && (ap->a_events & (POLLOUT | POLLWRNORM))) {
+			if (sce_out->q.c_cc > 0)
+				revents |= ap->a_events & (POLLOUT | POLLWRNORM);
+			else
+				selrecord(curthread, &sce_out->rsel);
 		}
 		break;
 	case UE_ISOCHRONOUS:
-		if (ap->a_events & (POLLIN | POLLRDNORM)) {
-			if (sce->cur != sce->fill)
+		if (sce_in != NULL && (ap->a_events & (POLLIN | POLLRDNORM))) {
+			if (sce_in->cur != sce_in->fill)
 				revents |= ap->a_events & (POLLIN | POLLRDNORM);
 			else
-				selrecord(curthread, &sce->rsel);
+				selrecord(curthread, &sce_in->rsel);
+		}
+		if (sce_out != NULL && (ap->a_events & (POLLOUT | POLLWRNORM))) {
+			if (sce_out->cur != sce_out->fill)
+				revents |= ap->a_events & (POLLOUT | POLLWRNORM);
+			else
+				selrecord(curthread, &sce_out->rsel);
 		}
 		break;
 	case UE_BULK:




More information about the Submit mailing list