diff options
author | Brian Feldman <green@FreeBSD.org> | 2004-10-02 22:49:54 +0000 |
---|---|---|
committer | Brian Feldman <green@FreeBSD.org> | 2004-10-02 22:49:54 +0000 |
commit | 96ee6195efd347c5dd16a23a4b6ca9cc5890564f (patch) | |
tree | 125a4bad88dc011a3c7839fc13d922df5884c914 /sys | |
parent | df3d6ec928cae98368e1c5e64b139b4e9788839f (diff) | |
download | src-96ee6195efd347c5dd16a23a4b6ca9cc5890564f.tar.gz src-96ee6195efd347c5dd16a23a4b6ca9cc5890564f.zip |
* 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.
Notes
Notes:
svn path=/head/; revision=136068
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/usb/ugen.c | 111 |
1 files changed, 91 insertions, 20 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index b0564d1dae8c..8e4b05ab6e7e 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); } |