summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorgreen <green@FreeBSD.org>2004-10-02 22:49:54 +0000
committergreen <green@FreeBSD.org>2004-10-02 22:49:54 +0000
commit9f302cddd10c0f628b3434f06deb3d7ce48c3482 (patch)
tree125a4bad88dc011a3c7839fc13d922df5884c914 /sys
parentc0dc03c5fc1ace84fd4d1de81f0d357cb9068311 (diff)
downloadFreeBSD-src-9f302cddd10c0f628b3434f06deb3d7ce48c3482.zip
FreeBSD-src-9f302cddd10c0f628b3434f06deb3d7ce48c3482.tar.gz
* Use two cdevsw's for ugen(4): one for control endpoints, and one for
data endpoints. The control endpoint doesn't need read/write/poll operations, and more importantly, the thread counts should be separate so that the control endpoint can properly reference itself while deleting and recreating the data endpoints. * Add some macros that handle referencing/releasing devices, and use them for sleeping/woken-up and open/close operations as apppropriate. * Use d_purge for FreeBSD, and a loop testing the open status for all the endpoints for NetBSD and OpenBSD, so that when the device is detached, the right thing always happens.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/ugen.c111
1 files changed, 91 insertions, 20 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
index b0564d1..8e4b05a 100644
--- a/sys/dev/usb/ugen.c
+++ b/sys/dev/usb/ugen.c
@@ -144,7 +144,30 @@ struct ugen_softc {
#define OUT 0
#define IN 1
+#ifdef __FreeBSD__
+#define UGEN_DEV_REF(dev, sc) \
+ if ((sc)->sc_dying || dev_refthread(dev) == NULL) \
+ return (ENXIO)
+#define UGEN_DEV_RELE(dev, sc) \
+ dev_relthread(dev)
+#define UGEN_DEV_OPEN(dev, sc) \
+ /* handled by dev layer */
+#define UGEN_DEV_CLOSE(dev, sc) \
+ /* handled by dev layer */
+#else
int sc_refcnt;
+#define UGEN_DEV_REF(dev, sc) \
+ if ((sc)->sc_dying) \
+ return (ENXIO); \
+ (sc)->sc_refcnt++
+#define UGEN_DEV_RELE(dev, sc) \
+ if (--(sc)->sc_refcnt < 0) \
+ usb_detach_wakeup(USBDEV((sc)->sc_dev))
+#define UGEN_DEV_OPEN(dev, sc) \
+ (sc)->sc_refcnt++
+#define UGEN_DEV_CLOSE(dev, sc) \
+ UGEN_DEV_RELE(dev, sc)
+#endif
u_char sc_dying;
};
@@ -157,7 +180,20 @@ d_read_t ugenread;
d_write_t ugenwrite;
d_ioctl_t ugenioctl;
d_poll_t ugenpoll;
+d_purge_t ugenpurge;
+Static struct cdevsw ugenctl_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ugenopen,
+ .d_close = ugenclose,
+ .d_ioctl = ugenioctl,
+ .d_purge = ugenpurge,
+ .d_name = "ugenctl",
+#if __FreeBSD_version < 500014
+ .d_bmaj -1
+#endif
+};
Static struct cdevsw ugen_cdevsw = {
.d_version = D_VERSION,
@@ -168,6 +204,7 @@ Static struct cdevsw ugen_cdevsw = {
.d_write = ugenwrite,
.d_ioctl = ugenioctl,
.d_poll = ugenpoll,
+ .d_purge = ugenpurge,
.d_name = "ugen",
#if __FreeBSD_version < 500014
.d_bmaj -1
@@ -250,8 +287,10 @@ USB_ATTACH(ugen)
#if defined(__FreeBSD__)
/* the main device, ctrl endpoint */
- sc->dev = make_dev(&ugen_cdevsw, UGENMINOR(USBDEVUNIT(sc->sc_dev), 0),
- UID_ROOT, GID_OPERATOR, 0644, "%s", USBDEVNAME(sc->sc_dev));
+ sc->dev = make_dev(&ugenctl_cdevsw,
+ UGENMINOR(USBDEVUNIT(sc->sc_dev), 0), UID_ROOT, GID_OPERATOR, 0644,
+ "%s", USBDEVNAME(sc->sc_dev));
+ ugen_make_devnodes(sc);
#endif
USB_ATTACH_SUCCESS_RETURN;
@@ -280,6 +319,7 @@ ugen_make_devnodes(struct ugen_softc *sc)
UID_ROOT, GID_OPERATOR, 0644,
"%s.%d",
USBDEVNAME(sc->sc_dev), endptno);
+ dev_depends(sc->dev, dev);
if (sc->sc_endpoints[endptno][IN].sc != NULL)
sc->sc_endpoints[endptno][IN].dev = dev;
if (sc->sc_endpoints[endptno][OUT].sc != NULL)
@@ -331,10 +371,6 @@ ugen_set_config(struct ugen_softc *sc, int configno)
DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
USBDEVNAME(sc->sc_dev), configno, sc));
-#if defined(__FreeBSD__)
- ugen_destroy_devnodes(sc);
-#endif
-
/* We start at 1, not 0, because we don't care whether the
* control endpoint is open or not. It is always present.
*/
@@ -380,10 +416,6 @@ ugen_set_config(struct ugen_softc *sc, int configno)
}
}
-#if defined(__FreeBSD__)
- ugen_make_devnodes(sc);
-#endif
-
return (USBD_NORMAL_COMPLETION);
}
@@ -414,7 +446,7 @@ ugenopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
if (endpt == USB_CONTROL_ENDPOINT) {
sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
- sc->sc_refcnt++;
+ UGEN_DEV_OPEN(dev, sc);
return (0);
}
@@ -551,7 +583,7 @@ ugenopen(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
}
}
sc->sc_is_open[endpt] = 1;
- sc->sc_refcnt++;
+ UGEN_DEV_OPEN(dev, sc);
return (0);
}
@@ -569,6 +601,12 @@ ugenclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
flag, mode, UGENUNIT(dev), endpt));
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if (sc->sc_dying)
+ wakeup(&sc->sc_dying);
+#endif
+
#ifdef DIAGNOSTIC
if (!sc->sc_is_open[endpt]) {
printf("ugenclose: not open\n");
@@ -579,8 +617,7 @@ ugenclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
if (endpt == USB_CONTROL_ENDPOINT) {
DPRINTFN(5, ("ugenclose: close control\n"));
sc->sc_is_open[endpt] = 0;
- if (--sc->sc_refcnt == 0)
- usb_detach_wakeup(USBDEV(sc->sc_dev));
+ UGEN_DEV_CLOSE(dev, sc);
return (0);
}
@@ -625,8 +662,7 @@ ugenclose(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
}
sc->sc_is_open[endpt] = 0;
- if (--sc->sc_refcnt == 0)
- usb_detach_wakeup(USBDEV(sc->sc_dev));
+ UGEN_DEV_CLOSE(dev, sc);
return (0);
}
@@ -821,7 +857,9 @@ ugenread(struct cdev *dev, struct uio *uio, int flag)
USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ UGEN_DEV_REF(dev, sc);
error = ugen_do_read(sc, endpt, uio, flag);
+ UGEN_DEV_RELE(dev, sc);
return (error);
}
@@ -915,7 +953,9 @@ ugenwrite(struct cdev *dev, struct uio *uio, int flag)
USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ UGEN_DEV_REF(dev, sc);
error = ugen_do_write(sc, endpt, uio, flag);
+ UGEN_DEV_RELE(dev, sc);
return (error);
}
@@ -937,14 +977,27 @@ ugen_activate(device_ptr_t self, enum devact act)
}
#endif
+#ifdef __FreeBSD__
+void
+ugenpurge(struct cdev *dev)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return;
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ wakeup(&sc->sc_endpoints[endpt][IN]);
+}
+#endif
+
USB_DETACH(ugen)
{
USB_DETACH_START(ugen, sc);
struct ugen_endpoint *sce;
int i, dir;
- int s;
#if defined(__NetBSD__) || defined(__OpenBSD__)
- int maj, mn;
+ int maj, mn, c, s;
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
@@ -967,6 +1020,7 @@ USB_DETACH(ugen)
}
}
+#if defined(__NetBSD__) || defined(__OpenBSD__)
s = splusb();
if (sc->sc_refcnt > 0) {
/* Wake everyone */
@@ -978,7 +1032,17 @@ USB_DETACH(ugen)
}
splx(s);
-#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* Wait for opens to go away. */
+ do {
+ c = 0;
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ if (sc->sc_is_open[i])
+ c++;
+ }
+ if (c != 0)
+ tsleep(&sc->sc_dying, PZERO, "ugendr", hz);
+ } while (c != 0);
+
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == ugenopen)
@@ -990,7 +1054,6 @@ USB_DETACH(ugen)
#elif defined(__FreeBSD__)
/* destroy the device for the control endpoint */
destroy_dev(sc->dev);
- ugen_destroy_devnodes(sc);
#endif
return (0);
@@ -1285,9 +1348,15 @@ ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd,
case USB_SET_CONFIG:
if (!(flag & FWRITE))
return (EPERM);
+#if defined(__FreeBSD__)
+ ugen_destroy_devnodes(sc);
+#endif
err = ugen_set_config(sc, *(int *)addr);
switch (err) {
case USBD_NORMAL_COMPLETION:
+#if defined(__FreeBSD__)
+ ugen_make_devnodes(sc);
+#endif
break;
case USBD_IN_USE:
return (EBUSY);
@@ -1493,7 +1562,9 @@ ugenioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ UGEN_DEV_REF(dev, sc);
error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p);
+ UGEN_DEV_RELE(dev, sc);
return (error);
}
OpenPOWER on IntegriCloud