Subject: Re: kern/36276 (ucycom causes kernel panic when accessing Delorme USB Earthmate LT-20 GPS)
To: None <skrll@NetBSD.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,>
From: Nick Hudson <skrll@netbsd.org>
List: netbsd-bugs
Date: 06/21/2007 07:30:04
The following reply was made to PR kern/36276; it has been noted by GNATS.

From: Nick Hudson <skrll@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: kern-bug-people@netbsd.org, netbsd-bugs@netbsd.org,
	gnats-admin@netbsd.org, dhowland@users.sourceforge.net
Subject: Re: kern/36276 (ucycom causes kernel panic when accessing Delorme USB Earthmate LT-20 GPS)
Date: Thu, 21 Jun 2007 08:27:38 +0100

 --Boundary-00=_rhieGDlp4vAv9/6
 Content-Type: text/plain;
   charset="iso-8859-6"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline
 
 On Tuesday 22 May 2007 21:28, skrll@netbsd.org wrote:
 > Synopsis: ucycom causes kernel panic when accessing Delorme USB Earthmate 
 LT-20 GPS
 
 Can you try this patch. It deals with the transfer length in the callback
 differently to yours (and it works for me ;)
 
 I'll deal with the whitespace stuff later.
 
 Thanks,
 Nick
 
 --Boundary-00=_rhieGDlp4vAv9/6
 Content-Type: text/x-diff;
   charset="iso-8859-6";
   name="ucycom.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename="ucycom.diff"
 
 Index: dev/usb/ucycom.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ucycom.c,v
 retrieving revision 1.17
 diff -u -p -u -r1.17 ucycom.c
 --- dev/usb/ucycom.c	4 Mar 2007 06:02:49 -0000	1.17
 +++ dev/usb/ucycom.c	21 Jun 2007 07:18:51 -0000
 @@ -129,6 +129,7 @@ struct ucycom_softc {
  	size_t			sc_olen; /* output report length */
  
  	uint8_t			*sc_obuf;
 +	int			sc_wlen;
  
  	/* settings */
  	uint32_t		sc_baud;
 @@ -157,6 +158,7 @@ const struct cdevsw ucycom_cdevsw = {
  
  Static int ucycomparam(struct tty *, struct termios *);
  Static void ucycomstart(struct tty *);
 +Static void ucycomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
  Static void ucycom_intr(struct uhidev *, void *, u_int);
  Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t);
  Static void tiocm_to_ucycom(struct ucycom_softc *, u_long, int);
 @@ -168,13 +170,14 @@ Static void ucycom_rts(struct ucycom_sof
  #endif
  Static void ucycom_cleanup(struct ucycom_softc *sc);
  
 -#ifdef UCYCOM_DEBUG
 +#ifdef UCYCOM_DEBUGX
  Static void ucycom_get_cfg(struct ucycom_softc *);
  #endif
  
  Static const struct usb_devno ucycom_devs[] = {
  	{ USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232 },
  	{ USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE },
 +	{ USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE_LT20 },
  };
  #define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p)
  
 @@ -452,8 +455,9 @@ Static void
  ucycomstart(struct tty *tp)
  {
  	struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(tp->t_dev)];
 +	usbd_status err;
  	u_char *data;
 -	int cnt, len, err, s;
 +	int cnt, len, s;
  
  	if (sc->sc_dying)
  		return;
 @@ -552,7 +556,7 @@ ucycomstart(struct tty *tp)
  		    sc->sc_olen));
  		goto out;
  	}
 -	splx(s);
 +	sc->sc_wlen = len;
  
  #ifdef UCYCOM_DEBUG
  	if (ucycomdebug > 5) {
 @@ -566,26 +570,64 @@ ucycomstart(struct tty *tp)
  		}
  	}
  #endif
 -	err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen);
  
 -	if (err) {
 -		DPRINTF(("ucycomstart: error doing uhidev_write = %d\n", err));
 -	}
 +	DPRINTFN(4,("ucycomstart: %d chars\n", len));
 +	usbd_setup_xfer(sc->sc_hdev.sc_parent->sc_oxfer,
 +	    sc->sc_hdev.sc_parent->sc_opipe, (usbd_private_handle)sc,
 +	    sc->sc_obuf, sc->sc_olen, 0 /* USBD_NO_COPY */, USBD_NO_TIMEOUT,
 +	    ucycomwritecb);
  
 +	/* What can we do on error? */
 +	err = usbd_transfer(sc->sc_hdev.sc_parent->sc_oxfer);
  #ifdef UCYCOM_DEBUG
 -	ucycom_get_cfg(sc);
 +	if (err != USBD_IN_PROGRESS)
 +		DPRINTF(("ucycomstart: err=%s\n", usbd_errstr(err)));
  #endif
 -	DPRINTFN(4,("ucycomstart: req %d chars did %d chars\n", cnt, len));
  
 - 	s = spltty();
 +out:
 +	splx(s);
 +}
 +
 +Static void
 +ucycomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
 +{
 +	struct ucycom_softc *sc = (struct ucycom_softc *)p;
 +	struct tty *tp = sc->sc_tty;
 +	usbd_status stat;
 +	int len, s;
 +
 +	if (status == USBD_CANCELLED || sc->sc_dying)
 +		goto error;
 +
 +	if (status) {
 +		DPRINTF(("ucycomwritecb: status=%d\n", status));
 +		usbd_clear_endpoint_stall(sc->sc_hdev.sc_parent->sc_opipe);
 +		/* XXX we should restart after some delay. */
 +		goto error;
 +	}
 +
 +	usbd_get_xfer_status(xfer, NULL, NULL, &len, &stat);
 +
 +	if (status != USBD_NORMAL_COMPLETION) {
 +		DPRINTFN(4,("ucycomwritecb: status = %d\n", status));
 +		goto error;
 +	}
 +
 +	DPRINTFN(4,("ucycomwritecb: did %d/%d chars\n", sc->sc_wlen, len));
 +
 +	s = spltty();
  	CLR(tp->t_state, TS_BUSY);
  	if (ISSET(tp->t_state, TS_FLUSH))
  		CLR(tp->t_state, TS_FLUSH);
  	else
 -		ndflush(&tp->t_outq, len);
 +		ndflush(&tp->t_outq, sc->sc_wlen);
  	(*tp->t_linesw->l_start)(tp);
 +	splx(s);
 +	return;
  
 -out:
 +error:
 +	s = spltty();
 +	CLR(tp->t_state, TS_BUSY);
  	splx(s);
  }
  
 @@ -1036,7 +1078,7 @@ ucycom_set_status(struct ucycom_softc *s
  	}
  }
  
 -#ifdef UCYCOM_DEBUG
 +#ifdef UCYCOM_DEBUGX
  Static void
  ucycom_get_cfg(struct ucycom_softc *sc)
  {
 
 --Boundary-00=_rhieGDlp4vAv9/6--