Module Name:    src
Committed By:   christos
Date:           Thu Nov  1 00:38:43 UTC 2012

Modified Files:
        src/sys/dev/usb: u3g.c

Log Message:
Match all pairs of bulkin-bulkout interfaces to ucom's, configuring multiple
ucoms per modem as they are available, instead of picking up the last bulkin
bulkout pair. This makes my Sierra U250 3G portion of the modem to work.


To generate a diff of this commit:
cvs rdiff -u -r1.26 -r1.27 src/sys/dev/usb/u3g.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/u3g.c
diff -u src/sys/dev/usb/u3g.c:1.26 src/sys/dev/usb/u3g.c:1.27
--- src/sys/dev/usb/u3g.c:1.26	Fri Oct  5 18:04:56 2012
+++ src/sys/dev/usb/u3g.c	Wed Oct 31 20:38:43 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: u3g.c,v 1.26 2012/10/05 22:04:56 khorben Exp $	*/
+/*	$NetBSD: u3g.c,v 1.27 2012/11/01 00:38:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.26 2012/10/05 22:04:56 khorben Exp $");
+__KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.27 2012/11/01 00:38:43 christos Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -113,16 +113,19 @@ struct u3g_softc {
 	device_t		sc_dev;
 	usbd_device_handle	sc_udev;
 	bool			sc_dying;	/* We're going away */
-
-	device_t		sc_ucom;	/* Child ucom(4) handle */
 	int			sc_ifaceno;	/* Device interface number */
 
-	bool			sc_open;	/* Device is in use */
-	bool			sc_purging;	/* Purging stale data */
-	struct timeval		sc_purge_start;	/* Control duration of purge */
+	struct u3g_com {
+		device_t	c_dev;		/* Child ucom(4) handle */
 
-	u_char			sc_msr;		/* Emulated 'msr' */
-	uint16_t		sc_outpins;	/* Output pin state */
+		bool		c_open;		/* Device is in use */
+		bool		c_purging;	/* Purging stale data */
+		struct timeval	c_purge_start;	/* Control duration of purge */
+
+		u_char		c_msr;		/* Emulated 'msr' */
+		uint16_t	c_outpins;	/* Output pin state */
+	} sc_com[10];
+	size_t			sc_ncom;
 
 	usbd_pipe_handle	sc_intr_pipe;	/* Interrupt pipe */
 	u_char			*sc_intr_buff;	/* Interrupt buffer */
@@ -246,7 +249,7 @@ static const struct usb_devno u3g_devs[]
 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 },
 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725 },
 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_USB305 },
-
+	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_250U },
 	/* Toshiba */
 	{ USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_HSDPA_MODEM_EU870DT1 },
 
@@ -629,20 +632,16 @@ u3g_attach(device_t parent, device_t sel
 	uca.ibufsize = U3G_BUFF_SIZE;
 	uca.obufsize = U3G_BUFF_SIZE;
 	uca.ibufsizepad = U3G_BUFF_SIZE;
-	uca.portno = uaa->ifaceno;
 	uca.opkthdrlen = 0;
 	uca.device = dev;
 	uca.iface = iface;
 	uca.methods = &u3g_methods;
 	uca.arg = sc;
+	uca.portno = -1;
 	uca.bulkin = uca.bulkout = -1;
 
-	sc->sc_outpins = 0;
-	sc->sc_msr = UMSR_DSR | UMSR_CTS | UMSR_DCD;
-	sc->sc_ifaceno = uaa->ifaceno;
-	sc->sc_open = false;
-	sc->sc_purging = false;
 
+	sc->sc_ifaceno = uaa->ifaceno;
 	intr_address = -1;
 	intr_size = 0;
 
@@ -668,25 +667,33 @@ u3g_attach(device_t parent, device_t sel
 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
 			uca.bulkout = ed->bEndpointAddress;
 		}
+		if (uca.bulkin != -1 && uca.bulkout != -1) {
+			struct u3g_com *com;
+			if (sc->sc_ncom == __arraycount(sc->sc_com)) {
+				aprint_error_dev(self, "Need to configure "
+				    "more than %zu ttys", sc->sc_ncom);
+				continue;
+			}
+			uca.portno = sc->sc_ncom++;
+			com = &sc->sc_com[uca.portno];
+			com->c_outpins = 0;
+			com->c_msr = UMSR_DSR | UMSR_CTS | UMSR_DCD;
+			com->c_open = false;
+			com->c_purging = false;
+			com->c_dev = config_found_sm_loc(self, "ucombus",
+				NULL, &uca, ucomprint, ucomsubmatch);
+			uca.bulkin = -1;
+			uca.bulkout = -1;
+		}
 	}
 
-	if (uca.bulkin == -1) {
-		aprint_error_dev(self, "Missing bulk in for interface %d\n",
-		    sc->sc_ifaceno);
-		sc->sc_dying = true;
-		return;
-	}
-
-	if (uca.bulkout == -1) {
-		aprint_error_dev(self, "Missing bulk out for interface %d\n",
+	if (sc->sc_ncom == 0) {
+		aprint_error_dev(self, "Missing bulk in/out for interface %d\n",
 		    sc->sc_ifaceno);
 		sc->sc_dying = true;
 		return;
 	}
 
-	sc->sc_ucom = config_found_sm_loc(self, "ucombus",
-	    NULL, &uca, ucomprint, ucomsubmatch);
-
 	/*
 	 * If the interface has an interrupt pipe, open it immediately so
 	 * that we can track input pin state changes regardless of whether
@@ -722,13 +729,14 @@ u3g_detach(device_t self, int flags)
 
 	pmf_device_deregister(self);
 
-	if (sc->sc_ucom != NULL) {
-		rv = config_detach(sc->sc_ucom, flags);
-		if (rv != 0) {
-			aprint_verbose_dev(self, "Can't deallocate "
-			    "port (%d)", rv);
+	for (size_t i = 0; i < sc->sc_ncom; i++)
+		if (sc->sc_com[i].c_dev != NULL) {
+			rv = config_detach(sc->sc_com[i].c_dev, flags);
+			if (rv != 0) {
+				aprint_verbose_dev(self, "Can't deallocate "
+				    "port (%d)", rv);
+			}
 		}
-	}
 
 	if (sc->sc_intr_pipe != NULL) {
 		(void) usbd_abort_pipe(sc->sc_intr_pipe);
@@ -748,30 +756,32 @@ u3g_childdet(device_t self, device_t chi
 {
 	struct u3g_softc *sc = device_private(self);
 
-	if (sc->sc_ucom == child)
-		sc->sc_ucom = NULL;
+	for (size_t i = 0; i < sc->sc_ncom; i++)
+		    if (sc->sc_com[i].c_dev == child)
+			    sc->sc_com[i].c_dev = NULL;
 }
 
 static int
 u3g_activate(device_t self, enum devact act)
 {
 	struct u3g_softc *sc = device_private(self);
-	int rv;
+	int rv = 0;
 
 	switch (act) {
 	case DVACT_DEACTIVATE:
-		if (sc->sc_ucom != NULL && config_deactivate(sc->sc_ucom))
+		for (size_t i = 0; i < sc->sc_ncom; i++)
+			if (sc->sc_com[i].c_dev != NULL &&
+			    config_deactivate(sc->sc_com[i].c_dev) && rv == 0)
 			rv = -1;
 		else
 			rv = 0;
 		break;
 
 	default:
-		rv = 0;
 		break;
 	}
 
-	return (rv);
+	return rv;
 }
 
 static void
@@ -779,6 +789,8 @@ u3g_intr(usbd_xfer_handle xfer, usbd_pri
 {
 	struct u3g_softc *sc = (struct u3g_softc *)priv;
 	u_char *buf;
+	int portno = 0;	/* XXX */
+	struct u3g_com *com = &sc->sc_com[portno];
 
 	if (sc->sc_dying)
 		return;
@@ -794,7 +806,7 @@ u3g_intr(usbd_xfer_handle xfer, usbd_pri
 	if (buf[0] == 0xa1 && buf[1] == 0x20) {
 		u_char msr;
 
-		msr = sc->sc_msr & ~(UMSR_DCD | UMSR_DSR | UMSR_RI);
+		msr = com->c_msr & ~(UMSR_DCD | UMSR_DSR | UMSR_RI);
 
 		if (buf[8] & U3G_INPIN_DCD)
 			msr |= UMSR_DCD;
@@ -805,10 +817,10 @@ u3g_intr(usbd_xfer_handle xfer, usbd_pri
 		if (buf[8] & U3G_INPIN_RI)
 			msr |= UMSR_RI;
 
-		if (msr != sc->sc_msr) {
-			sc->sc_msr = msr;
-			if (sc->sc_open)
-				ucom_status_change(device_private(sc->sc_ucom));
+		if (msr != com->c_msr) {
+			com->c_msr = msr;
+			if (com->c_open)
+				ucom_status_change(device_private(com->c_dev));
 		}
 	}
 }
@@ -822,7 +834,7 @@ u3g_get_status(void *arg, int portno, u_
 	if (lsr != NULL)
 		*lsr = 0;	/* LSR isn't supported */
 	if (msr != NULL)
-		*msr = sc->sc_msr;
+		*msr = sc->sc_com[portno].c_msr;
 }
 
 /*ARGSUSED*/
@@ -833,6 +845,7 @@ u3g_set(void *arg, int portno, int reg, 
 	usb_device_request_t req;
 	uint16_t mask, new_state;
 	usbd_status err;
+	struct u3g_com *com = &sc->sc_com[portno];
 
 	if (sc->sc_dying)
 		return;
@@ -848,14 +861,14 @@ u3g_set(void *arg, int portno, int reg, 
 		return;
 	}
 
-	new_state = sc->sc_outpins & ~mask;
+	new_state = com->c_outpins & ~mask;
 	if (onoff)
 		new_state |= mask;
 
-	if (new_state == sc->sc_outpins)
+	if (new_state == com->c_outpins)
 		return;
 
-	sc->sc_outpins = new_state;
+	com->c_outpins = new_state;
 
 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
 	req.bRequest = U3G_SET_PIN;
@@ -878,23 +891,26 @@ u3g_open(void *arg, int portno)
 	usb_interface_descriptor_t *id;
 	usbd_interface_handle ih;
 	usbd_status err;
-	int i;
+	struct u3g_com *com = &sc->sc_com[portno];
+	int i, nin;
 
 	if (sc->sc_dying)
 		return (0);
 
-	err = usbd_device2interface_handle(sc->sc_udev, portno, &ih);
+	err = usbd_device2interface_handle(sc->sc_udev, sc->sc_ifaceno, &ih);
 	if (err)
 		return (EIO);
 
 	id = usbd_get_interface_descriptor(ih);
 
-	for (i = 0; i < id->bNumEndpoints; i++) {
+	for (nin = i = 0; i < id->bNumEndpoints; i++) {
 		ed = usbd_interface2endpoint_descriptor(ih, i);
 		if (ed == NULL)	
 			return (EIO);
 
-		if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
+		    nin++ == portno) {
 			/* Issue ENDPOINT_HALT request */
 			req.bmRequestType = UT_WRITE_ENDPOINT;
 			req.bRequest = UR_CLEAR_FEATURE;
@@ -907,9 +923,9 @@ u3g_open(void *arg, int portno)
 		}
 	}
 
-	sc->sc_open = true;
-	sc->sc_purging = true;
-	getmicrotime(&sc->sc_purge_start);
+	com->c_open = true;
+	com->c_purging = true;
+	getmicrotime(&com->c_purge_start);
 
 	return (0);
 }
@@ -919,8 +935,9 @@ static void 
 u3g_close(void *arg, int portno)
 {
 	struct u3g_softc *sc = arg;
+	struct u3g_com *com = &sc->sc_com[portno];
 
-	sc->sc_open = false;
+	com->c_open = false;
 }
 
 /*ARGSUSED*/
@@ -929,22 +946,23 @@ u3g_read(void *arg, int portno, u_char *
 {
 	struct u3g_softc *sc = arg;
 	struct timeval curr_tv, diff_tv;
+	struct u3g_com *com = &sc->sc_com[portno];
 
 	/*
 	 * If we're not purging input data following first open, do nothing.
 	 */
-	if (sc->sc_purging == false)
+	if (com->c_purging == false)
 		return;
 
 	/*
 	 * Otherwise check if the purge timeout has expired
 	 */
 	getmicrotime(&curr_tv);
-	timersub(&curr_tv, &sc->sc_purge_start, &diff_tv);
+	timersub(&curr_tv, &com->c_purge_start, &diff_tv);
 
 	if (diff_tv.tv_sec >= U3G_PURGE_SECS) {
 		/* Timeout expired. */
-		sc->sc_purging = false;
+		com->c_purging = false;
 	} else {
 		/* Still purging. Adjust the caller's byte count. */
 		*ccp = 0;
@@ -956,10 +974,11 @@ static void
 u3g_write(void *arg, int portno, u_char *to, u_char *from, u_int32_t *count)
 {
 	struct u3g_softc *sc = arg;
+	struct u3g_com *com = &sc->sc_com[portno];
 
 	/*
 	 * Stop purging as soon as the first data is written to the device.
 	 */
-	sc->sc_purging = false;
+	com->c_purging = false;
 	memcpy(to, from, *count);
 }

Reply via email to