usb/76240: USB camera panics kernel

Peter Pentchev roam at ringlet.net
Tue Jan 18 05:00:55 PST 2005


The following reply was made to PR usb/76240; it has been noted by GNATS.

From: Peter Pentchev <roam at ringlet.net>
To: bug-followup at FreeBSD.org
Cc:  
Subject: Re: usb/76240: USB camera panics kernel
Date: Tue, 18 Jan 2005 14:54:55 +0200

 On Fri, Jan 14, 2005 at 08:56:15AM +0000, Mark Ovens wrote:
 > >Synopsis:       USB camera panics kernel
 [snip]
 > ugen1: Canon Inc. Canon Digital Camera, rev 1.10/0.01, addr 2
 [snip]
 
 > I've done that, but now instead of "Can't connect to camera" the
 > computer crashes with a kernel panic
 [snip]
 > WARNING: Driver mistake: destroy_dev on 0/0
 > panic: don't do that
 
 Just to add this to the audit trail: Claude Buisson <cbuisson at nerim.net>
 submitted a patch in the <41E7DA39.6000204 at nerim.net> message to the
 freebsd-usb list.  His patch merges most of the -CURRENT version of
 ugen.c to the RELENG_5 branch.  Here's a slightly modified version,
 which takes care of the 6.0-CURRENT-specific d_purge_t reference by
 conditional compilation based on __FreeBSD_version.
 
 G'luck,
 Peter
 
 Index: src/sys/dev/usb/ugen.c
 ===================================================================
 RCS file: /home/ncvs/src/sys/dev/usb/ugen.c,v
 retrieving revision 1.88.2.2
 diff -u -r1.88.2.2 ugen.c
 --- src/sys/dev/usb/ugen.c	31 Dec 2004 08:01:48 -0000	1.88.2.2
 +++ src/sys/dev/usb/ugen.c	18 Jan 2005 12:51:10 -0000
 @@ -70,9 +70,10 @@
  #else
  #include <sys/select.h>
  #endif
 -#include <sys/vnode.h>
  #include <sys/poll.h>
  #include <sys/sysctl.h>
 +#include <sys/uio.h>
 +#include <sys/vnode.h>
  
  #include <dev/usb/usb.h>
  #include <dev/usb/usbdi.h>
 @@ -136,7 +137,30 @@
  #define OUT 0
  #define IN  1
  
 +#if defined(__FreeBSD__) && __FreeBSD_version > 600003
 +#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;
  };
  
 @@ -149,7 +173,24 @@
  d_write_t ugenwrite;
  d_ioctl_t ugenioctl;
  d_poll_t  ugenpoll;
 +#if defined(__FreeBSD__) && __FreeBSD_version > 600003
 +d_purge_t ugenpurge;
 +#endif
  
 +Static struct cdevsw ugenctl_cdevsw = {
 +	.d_version =	D_VERSION,
 +	.d_flags =	D_NEEDGIANT,
 +	.d_open =	ugenopen,
 +	.d_close =	ugenclose,
 +	.d_ioctl =	ugenioctl,
 +#if defined(__FreeBSD__) && __FreeBSD_version > 600003
 +	.d_purge =	ugenpurge,
 +#endif
 +	.d_name =	"ugenctl",
 +#if __FreeBSD_version < 500014
 +	.d_bmaj		-1
 +#endif
 +};
  
  Static struct cdevsw ugen_cdevsw = {
  	.d_version =	D_VERSION,
 @@ -160,6 +201,9 @@
  	.d_write =	ugenwrite,
  	.d_ioctl =	ugenioctl,
  	.d_poll =	ugenpoll,
 +#if defined(__FreeBSD__) && __FreeBSD_version > 600003
 +	.d_purge =	ugenpurge,
 +#endif
  	.d_name =	"ugen",
  #if __FreeBSD_version < 500014
  	.d_bmaj		-1
 @@ -241,8 +285,10 @@
  
  #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;
 @@ -271,6 +317,7 @@
  				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)
 @@ -322,10 +369,6 @@
  	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.
  	 */
 @@ -437,10 +480,6 @@
  		}
  	}
  
 -#if defined(__FreeBSD__)
 -	ugen_make_devnodes(sc);
 -#endif
 -
  	return (USBD_NORMAL_COMPLETION);
  }
  
 @@ -471,7 +510,7 @@
  
  	if (endpt == USB_CONTROL_ENDPOINT) {
  		sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
 -		sc->sc_refcnt++;
 +		UGEN_DEV_OPEN(dev, sc);
  		return (0);
  	}
  
 @@ -479,7 +518,7 @@
  	for (dir = OUT; dir <= IN; dir++) {
  		if (flag & (dir == OUT ? FWRITE : FREAD)) {
  			sce = &sc->sc_endpoints[endpt][dir];
 -			if (sce == 0 || sce->edesc == 0)
 +			if (sce->edesc == 0)
  				return (ENXIO);
  		}
  	}
 @@ -582,7 +621,7 @@
  		}
  	}
  	sc->sc_is_open[endpt] = 1;
 -	sc->sc_refcnt++;
 +	UGEN_DEV_OPEN(dev, sc);
  	return (0);
  }
  
 @@ -610,8 +649,7 @@
  	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);
  	}
  
 @@ -619,7 +657,7 @@
  		if (!(flag & (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));
 @@ -648,8 +686,7 @@
  		}
  	}
  	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);
  }
 @@ -674,9 +711,6 @@
  	if (endpt == USB_CONTROL_ENDPOINT)
  		return (ENODEV);
  
 -	if (sce == NULL)
 -		return (EINVAL);
 -
  #ifdef DIAGNOSTIC
  	if (sce->edesc == NULL) {
  		printf("ugenread: no edesc\n");
 @@ -693,7 +727,7 @@
  		/* Block until activity occurred. */
  		s = splusb();
  		while (sce->q.c_cc == 0) {
 -			if (flag & IO_NDELAY) {
 +			if (flag & O_NONBLOCK) {
  				splx(s);
  				return (EWOULDBLOCK);
  			}
 @@ -734,10 +768,10 @@
  			DPRINTFN(1, ("ugenread: start transfer %d bytes\n",n));
  			tn = n;
  			err = usbd_bulk_transfer(
 -				xfer, sce->pipeh,
 -				sce->state & UGEN_SHORT_OK ?
 -				    USBD_SHORT_XFER_OK : 0,
 -				sce->timeout, buf, &tn, "ugenrb");
 +				  xfer, sce->pipeh,
 +				  sce->state & UGEN_SHORT_OK ?
 +				      USBD_SHORT_XFER_OK : 0,
 +				  sce->timeout, buf, &tn, "ugenrb");
  			if (err) {
  				if (err == USBD_INTERRUPTED)
  					error = EINTR;
 @@ -757,7 +791,7 @@
  	case UE_ISOCHRONOUS:
  		s = splusb();
  		while (sce->cur == sce->fill) {
 -			if (flag & IO_NDELAY) {
 +			if (flag & O_NONBLOCK) {
  				splx(s);
  				return (EWOULDBLOCK);
  			}
 @@ -808,7 +842,9 @@
  
  	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);
  }
  
 @@ -830,9 +866,6 @@
  	if (endpt == USB_CONTROL_ENDPOINT)
  		return (ENODEV);
  
 -	if (sce == NULL)
 -		return (EINVAL);
 -
  #ifdef DIAGNOSTIC
  	if (sce->edesc == NULL) {
  		printf("ugenwrite: no edesc\n");
 @@ -907,7 +940,9 @@
  
  	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);
  }
  
 @@ -929,14 +964,33 @@
  }
  #endif
  
 +#if defined(__FreeBSD__) && __FreeBSD_version > 600003
 +void
 +ugenpurge(struct cdev *dev)
 +{
 +	int endpt = UGENENDPOINT(dev);
 +	struct ugen_endpoint *sce;
 +	struct ugen_softc *sc;
 +
 +	if (endpt == USB_CONTROL_ENDPOINT)
 +		return;
 +	USB_GET_SC(ugen, UGENUNIT(dev), sc);
 +	sce = &sc->sc_endpoints[endpt][IN];
 +	if (sce->pipeh)
 +		usbd_abort_pipe(sce->pipeh);
 +}
 +#endif
 +
  USB_DETACH(ugen)
  {
  	USB_DETACH_START(ugen, sc);
  	struct ugen_endpoint *sce;
  	int i, dir;
 +/* Not sure if this declaration must be moved inside the
 +   defined(__NetBSD__) || defined(__OpenBSD__) below, or not */
  	int s;
  #if defined(__NetBSD__) || defined(__OpenBSD__)
 -	int maj, mn;
 +	int maj, mn, c;
  #endif
  
  #if defined(__NetBSD__) || defined(__OpenBSD__)
 @@ -950,11 +1004,13 @@
  	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
  		for (dir = OUT; dir <= IN; dir++) {
  			sce = &sc->sc_endpoints[i][dir];
 -			if (sce && sce->pipeh)
 +			if (sce->pipeh)
  				usbd_abort_pipe(sce->pipeh);
  		}
  	}
  
 +/* Not sure if this instruction group must be moved inside the
 +   defined(__NetBSD__) || defined(__OpenBSD__) below, or not */
  	s = splusb();
  	if (sc->sc_refcnt > 0) {
  		/* Wake everyone */
 @@ -967,6 +1023,17 @@
  	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)
 @@ -978,7 +1045,6 @@
  #elif defined(__FreeBSD__)
  	/* destroy the device for the control endpoint */
  	destroy_dev(sc->dev);
 -	ugen_destroy_devnodes(sc);
  #endif
  
  	return (0);
 @@ -1230,6 +1296,7 @@
  
  	switch (cmd) {
  	case FIONBIO:
 +	case FIOASYNC:
  		/* All handled in the upper FS layer. */
  		return (0);
  	case USB_SET_SHORT_XFER:
 @@ -1237,8 +1304,6 @@
  			return (EINVAL);
  		/* This flag only affects read */
  		sce = &sc->sc_endpoints[endpt][IN];
 -		if (sce == NULL)
 -			return (EINVAL);
  
  		if (sce->pipeh == NULL) {
  			printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n");
 @@ -1252,8 +1317,6 @@
  		return (0);
  	case USB_SET_TIMEOUT:
  		sce = &sc->sc_endpoints[endpt][IN];
 -		if (sce == NULL)
 -			return (EINVAL);
  		sce->timeout = *(int *)addr;
  		return (0);
  	default:
 @@ -1281,6 +1344,9 @@
  		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);
 @@ -1488,7 +1554,9 @@
  
  	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);
  }
  
 @@ -1507,9 +1575,6 @@
  
  	/* XXX always IN */
  	sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
 -	if (sce == NULL)
 -		return (EINVAL);
 -
  #ifdef DIAGNOSTIC
  	if (!sce->edesc) {
  		printf("ugenpoll: no edesc\n");
 
 -- 
 Peter Pentchev	roam at ringlet.net    roam at cnsys.bg    roam at FreeBSD.org
 PGP key:	http://people.FreeBSD.org/~roam/roam.key.asc
 Key fingerprint	FDBA FD79 C26F 3C51 C95E  DF9E ED18 B68D 1619 4553
 I had to translate this sentence into English because I could not read the original Sanskrit.


More information about the freebsd-usb mailing list