NetBSD Problem Report #39075

From www@NetBSD.org  Wed Jul  2 02:01:48 2008
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by narn.NetBSD.org (Postfix) with ESMTP id A90A663B907
	for <gnats-bugs@gnats.netbsd.org>; Wed,  2 Jul 2008 02:01:48 +0000 (UTC)
Message-Id: <20080702020148.6706063B880@narn.NetBSD.org>
Date: Wed,  2 Jul 2008 02:01:48 +0000 (UTC)
From: yuo@nui.org
Reply-To: yuo@nui.org
To: gnats-bugs@NetBSD.org
Subject: uhmodem(4) fix
X-Send-Pr-Version: www-1.0

>Number:         39075
>Category:       kern
>Synopsis:       uhmodem(4) fix
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          analyzed
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Jul 02 02:05:00 +0000 2008
>Closed-Date:    
>Last-Modified:  Mon Jul 07 06:33:22 +0000 2008
>Originator:     Yojiro UO
>Release:        4.99.67
>Organization:
>Environment:
all which has uhmodem(4).
>Description:
current uhmodem(4) assumes that the devices are compatible with ubsa(4). however, uhmodem(4) device is not compatible with that.  This patch fix following problems.

- notify message in interrupt pipe processing
- unsolicited close of interrupt pipe when multiple coms on same device opened and closed
   (it depend on sequence of open/close).
>How-To-Repeat:
always
>Fix:
apply the patch

Index: share/man/man4/uhmodem.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/uhmodem.4,v
retrieving revision 1.3
diff -u -p -r1.3 uhmodem.4
--- share/man/man4/uhmodem.4	30 Apr 2008 13:10:54 -0000	1.3
+++ share/man/man4/uhmodem.4	2 Jul 2008 01:14:32 -0000
@@ -43,6 +43,7 @@ driver supports the following adapters:
 .Pp
 .Bl -tag -width Dv -offset indent -compact
 .It Huawei E220
+.It Huawei E620
 .It E-mobile D01HW
 .It E-mobile D02HW
 .It NTT DoCoMo a2502
@@ -69,7 +70,6 @@ driver which makes it behave like a
 .Xr tty 4 .
 .Sh SEE ALSO
 .Xr tty 4 ,
-.Xr ubsa 4 ,
 .Xr ucom 4 ,
 .Xr usb 4
 .Sh HISTORY
@@ -77,5 +77,4 @@ The
 .Nm
 driver first appeared in
 .Nx 5.0 .
-Separate from ubsa driver.

Index: sys/dev/usb/uhmodem.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/uhmodem.c,v
retrieving revision 1.7
diff -u -p -r1.7 uhmodem.c
--- sys/dev/usb/uhmodem.c	24 May 2008 16:40:58 -0000	1.7
+++ sys/dev/usb/uhmodem.c	2 Jul 2008 01:14:32 -0000
@@ -98,18 +98,14 @@ __KERNEL_RCSID(0, "$NetBSD: uhmodem.c,v 
 #include <dev/usb/usbdevs.h>
 #include <dev/usb/usb_quirks.h>
 #include <dev/usb/ucomvar.h>
-#include <dev/usb/ubsavar.h>
-
-/* vendor specific bRequest */
-#define	UHMODEM_REGWRITE	0x20
-#define	UHMODEM_REGREAD		0x21
-#define UHMODEM_SETUP		0x22

+#define UHMODEM_INTR_INTERVAL	100	/* ms */
 #define UHMODEMIBUFSIZE	4096
 #define UHMODEMOBUFSIZE	4096

+#define UHMODEM_DEBUG
 #ifdef UHMODEM_DEBUG
-Static int	uhmodemdebug = 0;
+Static int	uhmodemdebug = 1;
 #define DPRINTFN(n, x)  do { \
 				if (uhmodemdebug > (n)) \
 					logprintf x; \
@@ -119,28 +115,72 @@ Static int	uhmodemdebug = 0;
 #endif
 #define DPRINTF(x) DPRINTFN(0, x)

-Static int uhmodem_open(void *, int);
-Static  usbd_status e220_modechange_request(usbd_device_handle);
-Static	usbd_status uhmodem_endpointhalt(struct ubsa_softc *, int);
-Static	usbd_status uhmodem_regwrite(usbd_device_handle, uint8_t *, size_t);
-Static	usbd_status uhmodem_regread(usbd_device_handle, uint8_t *, size_t);
-Static  usbd_status a2502_init(usbd_device_handle);
-#if 0
-Static	usbd_status uhmodem_regsetup(usbd_device_handle, uint16_t);
-Static  usbd_status e220_init(usbd_device_handle);
-#endif
+#define UHMODEM_MAXCONN		4
+
+#define E220_MODE_CHANGE_REQUEST 0x2
+
+struct uhmodem_iface {
+	struct uhmodem_softc	*parent;
+	usbd_interface_handle	sc_ifaceh;
+	int			sc_iface_number;		
+
+	/* intrrupt endpoint */
+	int			sc_intr_number;
+	usbd_pipe_handle	sc_intr_pipe;
+	u_char			*sc_intr_buf;
+	int			sc_isize;
+
+	u_char			sc_dtr; /* current DTR state */
+	u_char			sc_rts; /* current RTS state */
+	u_char			sc_lsr; /* local status register */
+	u_char			sc_msr;	/* status register */
+	
+	device_ptr_t		sc_subdev;	/* ucom device */
+};

 struct	uhmodem_softc {
-	struct ubsa_softc	sc_ubsa;	
+	USBBASEDEVICE		sc_dev;
+	usbd_device_handle	sc_udev;
+	int			sc_config_index;
+	int			sc_numif;
+	u_char			sc_dying;	/* disconnecting */
+	u_int16_t		sc_devflags;
+
+	struct uhmodem_iface	sc_iface[UHMODEM_MAXCONN];
 };

+int	uhmodem_open(void *, int);
+void	uhmodem_close(void*, int);
+void	uhmodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+void	uhmodem_get_status(void *, int, u_char *, u_char *);
+void	uhmodem_set(void *, int, int, int);
+int	uhmodem_param(void *, int, struct termios *);
+
+int	uhmodem_match(device_t, cfdata_t, void *);
+void	uhmodem_attach(device_t, device_t, void *);
+void	uhmodem_childdet(device_t, device_t);
+int	uhmodem_detach(device_t, int);
+int	uhmodem_activate(device_t, enum devact);
+
+Static  usbd_status e220_modechange_request(usbd_device_handle);
+Static	usbd_status uhmodem_endpointhalt(struct uhmodem_softc *, int);
+Static	usbd_status uhmodem_set_line_coding(struct uhmodem_softc *,
+     int, usb_cdc_line_state_t *);
+Static	usbd_status uhmodem_get_line_coding(struct uhmodem_softc *,
+     int, usb_cdc_line_state_t *);
+Static	usbd_status uhmodem_set_ctrl_line_state(struct uhmodem_softc *,
+     int, uint16_t);
+Static	usbd_status uhmodem_set_comm_feature(struct uhmodem_softc *,
+     int, usb_cdc_abstract_state_t *);
+Static  usbd_status e220_init(struct uhmodem_softc *, int);
+
 struct	ucom_methods uhmodem_methods = {
-	ubsa_get_status,
-	ubsa_set,
-	ubsa_param,
+	uhmodem_get_status,
+	uhmodem_set,
+	uhmodem_param,
 	NULL,
 	uhmodem_open,
-	ubsa_close,
+	uhmodem_close,
 	NULL,
 	NULL
 };
@@ -149,27 +189,20 @@ struct uhmodem_type {
 	struct usb_devno	uhmodem_dev;
 	u_int16_t		uhmodem_coms;	/* number of serial interfaces on the device */
 	u_int16_t		uhmodem_flags;
-#define	E220	0x0001
-#define	A2502	0x0002
-#define	E620	0x0004		/* XXX */
-				/* Whether or not it is a device different from E220 is not clear. */
+#define DEV_DEFAULT	0x0000
+#define	DEV_E220	0x0001
 };

 Static const struct uhmodem_type uhmodem_devs[] = {
 	/* HUAWEI E220 / Emobile D0[12]HW */
-	{{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, 2,	E220},
+	{{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, 2,	DEV_E220},
 	/* ANYDATA / NTT DoCoMo A2502 */
-	{{ USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_A2502 }, 3,	A2502},
+	{{ USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_A2502 }, 3,	0},
 	/* HUAWEI E620 */
-	{{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE }, 3,   E620},
+	{{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE }, 3,   0},
 };
 #define uhmodem_lookup(v, p) ((const struct uhmodem_type *)usb_lookup(uhmodem_devs, v, p))

-int uhmodem_match(device_t, cfdata_t, void *);
-void uhmodem_attach(device_t, device_t, void *);
-void uhmodem_childdet(device_t, device_t);
-int uhmodem_detach(device_t, int);
-int uhmodem_activate(device_t, enum devact);
 extern struct cfdriver uhmodem_cd;
 CFATTACH_DECL2_NEW(uhmodem, sizeof(struct uhmodem_softc), uhmodem_match,
     uhmodem_attach, uhmodem_detach, uhmodem_activate, NULL, uhmodem_childdet);
@@ -195,6 +228,7 @@ USB_ATTACH(uhmodem)
 	usb_endpoint_descriptor_t *ed;
 	char *devinfop;
 	usbd_status err;
+	struct uhmodem_iface *iface;
 	struct ucom_attach_args uca;
 	int i;
 	int j;
@@ -205,23 +239,25 @@ USB_ATTACH(uhmodem)
 	aprint_normal_dev(self, "%s\n", devinfop);
 	usbd_devinfo_free(devinfop);

-	sc->sc_ubsa.sc_dev = self;
-        sc->sc_ubsa.sc_udev = dev;
-	sc->sc_ubsa.sc_config_index = UBSA_DEFAULT_CONFIG_INDEX;
-	sc->sc_ubsa.sc_numif = 1; /* defaut device has one interface */
+	sc->sc_dev = self;
+        sc->sc_udev = dev;
+	sc->sc_config_index = 0;
+	sc->sc_numif = 1; /* defaut device has one interface */

 	/* Hauwei E220 need special request to change its mode to modem */
 	if ((uaa->ifaceno == 0) && (uaa->class != 255)) {
+		aprint_error_dev(self, "HUAWEI E220 need to re-attach "
+		    "to enable modem function\n");
 		err = e220_modechange_request(dev);
 		if (err) {
 			aprint_error_dev(self, "failed to change mode: %s\n", 
 				usbd_errstr(err));
-			sc->sc_ubsa.sc_dying = 1;
+			sc->sc_dying = 1;
 			goto error;
 		}
 		aprint_error_dev(self,
 		    "mass storage only mode, reattach to enable modem\n");
-		sc->sc_ubsa.sc_dying = 1;
+		sc->sc_dying = 1;
 		goto error;
 	}

@@ -229,60 +265,62 @@ USB_ATTACH(uhmodem)
 	 * initialize rts, dtr variables to something
 	 * different from boolean 0, 1
 	 */
-	sc->sc_ubsa.sc_dtr = -1;
-	sc->sc_ubsa.sc_rts = -1;
+	sc->sc_numif = uhmodem_lookup(uaa->vendor, uaa->product)->uhmodem_coms;
+	sc->sc_devflags = uhmodem_lookup(uaa->vendor, uaa->product)->uhmodem_flags;

-	sc->sc_ubsa.sc_quadumts = 1;
-	sc->sc_ubsa.sc_config_index = 0;
-	sc->sc_ubsa.sc_numif = uhmodem_lookup(uaa->vendor, uaa->product)->uhmodem_coms;
-	sc->sc_ubsa.sc_devflags = uhmodem_lookup(uaa->vendor, uaa->product)->uhmodem_flags;
-
-	DPRINTF(("uhmodem attach: sc = %p\n", sc));
+	DPRINTF(("uhmodem attach: sc = %p, sc_dying = %d\n", sc, sc->sc_dying));

 	/* Move the device into the configured state. */
-	err = usbd_set_config_index(dev, sc->sc_ubsa.sc_config_index, 1);
+	err = usbd_set_config_index(dev, sc->sc_config_index, 1);
 	if (err) {
 		aprint_error_dev(self, "failed to set configuration: %s\n",
 		    usbd_errstr(err));
-		sc->sc_ubsa.sc_dying = 1;
+		sc->sc_dying = 1;
 		goto error;
 	}

 	/* get the config descriptor */
-	cdesc = usbd_get_config_descriptor(sc->sc_ubsa.sc_udev);
+	cdesc = usbd_get_config_descriptor(sc->sc_udev);
 	if (cdesc == NULL) {
 		aprint_error_dev(self,
 		    "failed to get configuration descriptor\n");
-		sc->sc_ubsa.sc_dying = 1;
+		sc->sc_dying = 1;
 		goto error;
 	}

-	sc->sc_ubsa.sc_intr_number = -1;
-	sc->sc_ubsa.sc_intr_pipe = NULL;
-
 	/* get the interfaces */
-	for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-		err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX_OFFSET+i,
-				 &sc->sc_ubsa.sc_iface[i]);
+	for (i = 0; i < sc->sc_numif; i++) {
+		iface = &sc->sc_iface[i];
+
+		/* initialize */
+		memset(iface, 0, sizeof(iface));
+		iface->sc_iface_number = -1;		
+		iface->sc_intr_number = -1;
+		iface->sc_intr_pipe = NULL;
+		iface->sc_intr_buf = NULL;
+		iface->sc_dtr = iface->sc_rts = -1;
+		iface->parent = sc;
+
+		err = usbd_device2interface_handle(dev, i, &iface->sc_ifaceh);
 		if (err) {
 			if (i == 0){
 				/* can not get main interface */
-				sc->sc_ubsa.sc_dying = 1;
+				sc->sc_dying = 1;
 				goto error;
 			} else
 				break;
 		}

 		/* Find the endpoints */
-		id = usbd_get_interface_descriptor(sc->sc_ubsa.sc_iface[i]);
-		sc->sc_ubsa.sc_iface_number[i] = id->bInterfaceNumber;
+		id = usbd_get_interface_descriptor(iface->sc_ifaceh);
+		iface->sc_iface_number = id->bInterfaceNumber;

 		/* initialize endpoints */
 		uca.bulkin = uca.bulkout = -1;

 		for (j = 0; j < id->bNumEndpoints; j++) {
 			ed = usbd_interface2endpoint_descriptor(
-				sc->sc_ubsa.sc_iface[i], j);
+				iface->sc_ifaceh, j);
 			if (ed == NULL) {
 				aprint_error_dev(self,
 				    "no endpoint descriptor for %d "
@@ -292,8 +330,10 @@ USB_ATTACH(uhmodem)

 			if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
 			    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
-				sc->sc_ubsa.sc_intr_number = ed->bEndpointAddress;
-				sc->sc_ubsa.sc_isize = UGETW(ed->wMaxPacketSize);
+				iface->sc_intr_number = ed->bEndpointAddress;
+				iface->sc_isize = UGETW(ed->wMaxPacketSize);
+				aprint_normal_dev(self, "find interrupt endpoint "
+				    "for if#%d\n", iface->sc_iface_number);
 			} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
 			    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
 				uca.bulkin = ed->bEndpointAddress;
@@ -303,27 +343,23 @@ USB_ATTACH(uhmodem)
 			}
 		} /* end of Endpoint loop */

-		if (sc->sc_ubsa.sc_intr_number == -1) {
-			aprint_error_dev(self, "HUAWEI E220 need to re-attach "
-			    "to enable modem function\n");
-			if (i == 0) {
-				/* could not get intr for main tty */
-				sc->sc_ubsa.sc_dying = 1;
-				goto error;
-			} else
-				break;
+		if (iface->sc_iface_number == 0 && iface->sc_intr_number == -1) {
+			/* could not get intr for main tty */
+			aprint_error_dev(self, "could not find intr ep. for main tty\n");
+			sc->sc_dying = 1;
+			goto error;
 		}
 		if (uca.bulkin == -1) {
 			aprint_error_dev(self,
 			    "Could not find data bulk in\n");
-			sc->sc_ubsa.sc_dying = 1;
+			sc->sc_dying = 1;
 			goto error;
 		}

 		if (uca.bulkout == -1) {
 			aprint_error_dev(self,
 			    "Could not find data bulk out\n");
-			sc->sc_ubsa.sc_dying = 1;
+			sc->sc_dying = 1;
 			goto error;
 		}

@@ -342,32 +378,32 @@ USB_ATTACH(uhmodem)
 			break;
 		}

+		/* build uca information , bulkin, bulkout set above */
 		uca.portno = i;
-		/* bulkin, bulkout set above */
 		uca.ibufsize = UHMODEMIBUFSIZE;
 		uca.obufsize = UHMODEMOBUFSIZE;
 		uca.ibufsizepad = UHMODEMIBUFSIZE;
 		uca.opkthdrlen = 0;
 		uca.device = dev;
-		uca.iface = sc->sc_ubsa.sc_iface[i];
+		uca.iface = iface->sc_ifaceh;
 		uca.methods = &uhmodem_methods;
-		uca.arg = &sc->sc_ubsa;
+		uca.arg = sc;
 		uca.info = comname;
-		DPRINTF(("uhmodem: int#=%d, in = 0x%x, out = 0x%x, intr = 0x%x\n",
-	    		i, uca.bulkin, uca.bulkout, sc->sc_ubsa.sc_intr_number));
-		sc->sc_ubsa.sc_subdevs[i] = config_found_sm_loc(self, "ucombus", NULL,
+		DPRINTF(("uhmodem: int#=%d, in=0x%x, out=0x%x, intr=0x%x\n",
+	    		i, uca.bulkin, uca.bulkout, iface->sc_intr_number));
+		iface->sc_subdev = config_found_sm_loc(self, "ucombus", NULL,
 				 &uca, ucomprint, ucomsubmatch);

 		/* issue endpoint halt to each interface */
-		err = uhmodem_endpointhalt(&sc->sc_ubsa, i);
+		err = uhmodem_endpointhalt(sc, i);
 		if (err) 
 			aprint_error("%s: endpointhalt fail\n", __func__);
 		else
-			usbd_delay_ms(sc->sc_ubsa.sc_udev, 50);
+			usbd_delay_ms(sc->sc_udev, 50);
 	} /* end of Interface loop */

-	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_ubsa.sc_udev,
-			   USBDEV(sc->sc_ubsa.sc_dev));
+	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+			   USBDEV(sc->sc_dev));

 	USB_ATTACH_SUCCESS_RETURN;

@@ -378,40 +414,50 @@ error:
 void
 uhmodem_childdet(device_t self, device_t child)
 {
-	int i;
 	struct uhmodem_softc *sc = device_private(self);
+	struct uhmodem_iface *iface;
+	int i;

-	for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-		if (sc->sc_ubsa.sc_subdevs[i] == child)
+	iface = &sc->sc_iface[0];
+	for (i = 0; i < sc->sc_numif; i++) {
+		iface = &sc->sc_iface[i];
+		if (iface->sc_subdev == child)
 			break;
 	}
-	KASSERT(i < sc->sc_ubsa.sc_numif);
-	sc->sc_ubsa.sc_subdevs[i] = NULL;
+	KASSERT(i < sc->sc_numif);
+	iface->sc_subdev = NULL;
 }

 USB_DETACH(uhmodem)
 {
 	USB_DETACH_START(uhmodem, sc);
+	struct uhmodem_iface *iface;
 	int i;
 	int rv = 0;

 	DPRINTF(("uhmodem_detach: sc = %p\n", sc));

-	if (sc->sc_ubsa.sc_intr_pipe != NULL) {
-		usbd_abort_pipe(sc->sc_ubsa.sc_intr_pipe);
-		usbd_close_pipe(sc->sc_ubsa.sc_intr_pipe);
-		free(sc->sc_ubsa.sc_intr_buf, M_USBDEV);
-		sc->sc_ubsa.sc_intr_pipe = NULL;
+	/* close all interrupt endpoints */
+	for (i = 0; i < sc->sc_numif; i++) {
+		iface = &sc->sc_iface[i];
+		if (iface->sc_intr_pipe != NULL) {
+			usbd_abort_pipe(iface->sc_intr_pipe);
+			usbd_close_pipe(iface->sc_intr_pipe);
+			free(iface->sc_intr_buf, M_USBDEV);
+			iface->sc_intr_pipe = NULL;
+		}
 	}

-	sc->sc_ubsa.sc_dying = 1;
-	for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-		if (sc->sc_ubsa.sc_subdevs[i] != NULL)
-			rv |= config_detach(sc->sc_ubsa.sc_subdevs[i], flags);
+	sc->sc_dying = 1;
+	/* detach all sub devices */
+	for (i = 0; i < sc->sc_numif; i++) {
+		iface = &sc->sc_iface[i];
+		if (iface->sc_subdev != NULL)
+			rv |= config_detach(iface->sc_subdev, flags);
 	}

-	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ubsa.sc_udev,
-			   USBDEV(sc->sc_ubsa.sc_dev));
+	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+			   USBDEV(sc->sc_dev));

 	return (rv);
 }
@@ -420,6 +466,7 @@ int
 uhmodem_activate(device_t self, enum devact act)
 {
 	struct uhmodem_softc *sc = device_private(self);
+	struct uhmodem_iface *iface;
 	int rv = 0;
 	int i;

@@ -428,66 +475,145 @@ uhmodem_activate(device_t self, enum dev
 		return (EOPNOTSUPP);

 	case DVACT_DEACTIVATE:
-		for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-			if (sc->sc_ubsa.sc_subdevs[i] != NULL)
-				rv |= config_deactivate(sc->sc_ubsa.sc_subdevs[i]);
+		for (i = 0; i < sc->sc_numif; i++) {
+			iface = &sc->sc_iface[i];
+			if (iface->sc_subdev != NULL)
+				rv |= config_deactivate(iface->sc_subdev);
 		}
-		sc->sc_ubsa.sc_dying = 1;
+		sc->sc_dying = 1;
 		break;
 	}
 	return (rv);
 }

-Static int
+void
+uhmodem_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+	struct uhmodem_softc *sc = addr;
+	struct uhmodem_iface *iface = &sc->sc_iface[portno];
+
+	if (lsr != NULL)
+		*lsr = iface->sc_lsr;
+	if (msr != NULL)
+		*msr = iface->sc_msr;
+}
+
+int
+uhmodem_param(void *addr, int portno, struct termios *t)
+{
+	struct uhmodem_softc *sc = addr;
+	usbd_status err;
+	usb_cdc_line_state_t ls;
+	
+	DPRINTF(("%s: called with speed:%d, flag:0x%02x\n", __func__,
+	    t->c_ospeed, t->c_cflag));
+
+	USETDW(ls.dwDTERate, t->c_ospeed);
+	if (ISSET(t->c_cflag, CSTOPB))
+		ls.bCharFormat = UCDC_STOP_BIT_2;
+	else
+		ls.bCharFormat = UCDC_STOP_BIT_1;
+	if (ISSET(t->c_cflag, PARENB)) {
+		if (ISSET(t->c_cflag, PARODD))
+			ls.bParityType = UCDC_PARITY_ODD;
+		else
+			ls.bParityType = UCDC_PARITY_EVEN;
+	} else
+		ls.bParityType = UCDC_PARITY_NONE;
+	switch (ISSET(t->c_cflag, CSIZE)) {
+	case CS5:
+		ls.bDataBits = 5;
+		break;
+	case CS6:
+		ls.bDataBits = 6;
+		break;
+	case CS7:
+		ls.bDataBits = 7;
+		break;
+	case CS8:
+		ls.bDataBits = 8;
+		break;
+	}
+
+	err = uhmodem_set_line_coding(sc, portno, &ls);
+	if (err) {
+		DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
+		return (EPASSTHROUGH);
+	}
+		
+	return (0);
+}
+
+void
+uhmodem_set(void *addr, int portno, int reg, int onoff)
+{
+	struct uhmodem_softc *sc = addr;
+	struct uhmodem_iface *iface = &sc->sc_iface[portno];
+	usb_device_request_t req;
+	int ls;
+
+	switch (reg) {
+	case UCOM_SET_DTR:
+		if (iface->sc_dtr == onoff)
+			return;
+		iface->sc_dtr = onoff;
+		break;
+	case UCOM_SET_RTS:
+		if (iface->sc_rts == onoff)
+			return;
+		iface->sc_rts = onoff;
+		break;
+	default:
+		return;
+	}
+
+	/* build a usb request */
+	ls = (iface->sc_dtr ? UCDC_LINE_DTR : 0) |
+	     (iface->sc_rts ? UCDC_LINE_RTS : 0);
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, ls);
+	USETW(req.wIndex, iface->sc_iface_number);
+	USETW(req.wLength, 0);
+
+	(void)usbd_do_request(sc->sc_udev, &req, 0);
+}
+
+int
 uhmodem_open(void *addr, int portno)
 {
-	struct ubsa_softc *sc = addr;
+	struct uhmodem_softc *sc = addr;
+	struct uhmodem_iface *iface = &sc->sc_iface[portno];
 	usbd_status err;

 	if (sc->sc_dying)
 		return (ENXIO);

-	DPRINTF(("%s: sc = %p\n", __func__, sc));
-
-	err = uhmodem_endpointhalt(sc, 0);
-	if (err) 
-		aprint_error("%s: endpointhalt fail\n", __func__);
-	else
-		usbd_delay_ms(sc->sc_udev, 50);
-
-	if (sc->sc_devflags & A2502) {
-		err = a2502_init(sc->sc_udev);
+#if 0 /* XXX: windows driver issues following request but currently disabled */
+	if (sc->sc_devflags & DEV_E220) {
+		err = e220_init(sc, portno);
 		if (err)
-			aprint_error("%s: a2502init fail\n", __func__);
+			aprint_error("%s: e220 init fail\n", __func__);
 		else
-			usbd_delay_ms(sc->sc_udev, 50);
-	}
-#if 0 /* currently disabled */
-	if (sc->sc_devflags & E220) {
-		err = e220_init(sc->sc_udev);
-		if (err)
-			aprint_error("%s: e220init fail\n", __func__);
-		else
-			usbd_delay_ms(sc->sc_udev, 50);
+			usbd_delay_ms(sc->sc_udev, 100);
 	}
 #endif
-	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
-		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
-		/* XXX only iface# = 0 has intr line */
-		/* XXX E220 specific? need to check */
-		err = usbd_open_pipe_intr(sc->sc_iface[0],
-		    sc->sc_intr_number,
+
+	if (iface->sc_intr_number != -1 && iface->sc_intr_pipe == NULL) {
+		iface->sc_intr_buf = malloc(iface->sc_isize, M_USBDEV, M_WAITOK);
+		err = usbd_open_pipe_intr(iface->sc_ifaceh,
+		    iface->sc_intr_number,
 		    USBD_SHORT_XFER_OK,
-		    &sc->sc_intr_pipe,
-		    sc,
-		    sc->sc_intr_buf,
-		    sc->sc_isize,
-		    ubsa_intr,
-		    UBSA_INTR_INTERVAL);
+		    &iface->sc_intr_pipe,
+		    iface,
+		    iface->sc_intr_buf,
+		    iface->sc_isize,
+		    uhmodem_intr,
+		    UHMODEM_INTR_INTERVAL);
 		if (err) {
 			aprint_error_dev(sc->sc_dev,
 			    "cannot open interrupt pipe (addr %d)\n",
-			    sc->sc_intr_number);
+			    iface->sc_intr_number);
 			return (EIO);
 		}
 	}
@@ -495,6 +621,85 @@ uhmodem_open(void *addr, int portno)
 	return (0);
 }

+void
+uhmodem_close(void *addr, int portno)
+{
+	struct uhmodem_softc *sc = addr;
+	struct uhmodem_iface *iface = &sc->sc_iface[portno];
+	int err;
+
+	if (sc->sc_dying)
+		return;
+
+	if (iface->sc_intr_pipe != NULL) {
+		err = usbd_abort_pipe(iface->sc_intr_pipe);
+		if (err)
+			aprint_error_dev(sc->sc_dev, 
+			    "abort interrupt pipe failed: %s\n", 
+			    usbd_errstr(err));
+		err = usbd_close_pipe(iface->sc_intr_pipe);
+		if (err)
+			aprint_error_dev(sc->sc_dev, 
+			    "close interrupt pipe failed: %s\n", 
+			    usbd_errstr(err));
+		free(iface->sc_intr_buf, M_USBDEV);
+		iface->sc_intr_pipe = NULL;
+	}
+}
+
+void
+uhmodem_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
+	usbd_status status)
+{
+	struct uhmodem_iface *iface = priv;
+	struct uhmodem_softc *sc = iface->parent;
+	usb_cdc_notification_t *buf;
+	u_char mstatus;
+
+
+	buf = (usb_cdc_notification_t *)iface->sc_intr_buf;
+	if (sc->sc_dying)
+		return;
+
+	if (status != USBD_NORMAL_COMPLETION) {
+		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+			return;
+		aprint_error_dev(sc->sc_dev, 
+		    "uhmodem interrupt abnormal status: %s\n", 
+		    usbd_errstr(status));
+		usbd_clear_endpoint_stall_async(iface->sc_intr_pipe);
+		return;
+	}
+
+	if (buf->bmRequestType != UCDC_NOTIFICATION) {
+		aprint_error_dev(sc->sc_dev, 
+		    "uhmodem_intr: unknown message type(0x%02x)\n",
+		    buf->bmRequestType);
+		return;
+	}
+	if (buf->bNotification == UCDC_N_SERIAL_STATE) {
+		/* invalid message length, discard it */
+		if (UGETW(buf->wLength) != 2)
+			return;
+		/* XXX: sc_lsr is always 0 */
+		iface->sc_lsr = iface->sc_msr = 0;
+		mstatus = buf->data[0];
+		if (ISSET(mstatus, UCDC_N_SERIAL_RI))
+			iface->sc_msr |= UMSR_RI;
+		if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
+			iface->sc_msr |= UMSR_DSR;
+		if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
+			iface->sc_msr |= UMSR_DCD;
+	} else if (buf->bNotification != UCDC_N_CONNECTION_SPEED_CHANGE) {
+		aprint_error_dev(sc->sc_dev, 
+		    "uhmodem_intr: unknown notify message (0x%02x)\n",
+		     buf->bNotification);
+		return;
+	}
+
+	ucom_status_change(device_private(iface->sc_subdev));
+}
+
 /*
  * Hauwei E220 needs special request to enable modem function.
  * -- DEVICE_REMOTE_WAKEUP ruquest to endpoint 2.
@@ -502,7 +707,6 @@ uhmodem_open(void *addr, int portno)
 Static  usbd_status 
 e220_modechange_request(usbd_device_handle dev)
 {
-#define E220_MODE_CHANGE_REQUEST 0x2
 	usb_device_request_t req;
 	usbd_status err;

@@ -520,28 +724,30 @@ e220_modechange_request(usbd_device_hand
 	}

 	return (0);
-#undef E220_MODE_CHANGE_REQUEST
 }

 Static  usbd_status 
-uhmodem_endpointhalt(struct ubsa_softc *sc, int iface)
+uhmodem_endpointhalt(struct uhmodem_softc *sc, int port)
 {
 	usb_device_request_t req;
 	usb_endpoint_descriptor_t *ed;
 	usb_interface_descriptor_t *id;
+	struct uhmodem_iface *iface;
 	usbd_status err;
 	int i;

-	/* Find the endpoints */
-	id = usbd_get_interface_descriptor(sc->sc_iface[iface]);
+	iface = &sc->sc_iface[port];
+	id = usbd_get_interface_descriptor(iface->sc_ifaceh);

 	for (i = 0; i < id->bNumEndpoints; i++) {
-		ed = usbd_interface2endpoint_descriptor(sc->sc_iface[iface], i);
+		ed = usbd_interface2endpoint_descriptor(iface->sc_ifaceh, i);
 		if (ed == NULL)	
 			return (EIO);

 		if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
 			/* issue ENDPOINT_HALT request */
+			DPRINTF(("%s: endpoint halt to EP:0x%.2x\n", 
+				__func__, ed->bEndpointAddress));
 			req.bmRequestType = UT_WRITE_ENDPOINT;
 			req.bRequest = UR_CLEAR_FEATURE;
 			USETW(req.wValue, UF_ENDPOINT_HALT);
@@ -553,7 +759,6 @@ uhmodem_endpointhalt(struct ubsa_softc *
 					__func__, ed->bEndpointAddress));
 				return (EIO);
 			}
-
 		}
 	} /* end of Endpoint loop */

@@ -561,147 +766,105 @@ uhmodem_endpointhalt(struct ubsa_softc *
 }

 Static usbd_status
-uhmodem_regwrite(usbd_device_handle dev, uint8_t *data, size_t len)
+uhmodem_set_line_coding(struct uhmodem_softc *sc, int portno,
+    usb_cdc_line_state_t *state)
 {
 	usb_device_request_t req;
 	usbd_status err;
+	int iface = sc->sc_iface[portno].sc_iface_number;

 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-	req.bRequest = UHMODEM_REGWRITE;
-	USETW(req.wValue, 0x0000);
-	USETW(req.wIndex, 0x0000);
-	USETW(req.wLength, len);
-	err = usbd_do_request(dev, &req, data);
+	req.bRequest = UCDC_SET_LINE_CODING;
+	USETW(req.wValue, 0);	 /* zero */
+	USETW(req.wIndex, iface);	 /* iface */
+	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+	err = usbd_do_request(sc->sc_udev, &req, state);
 	if (err) 
 		return err;

-	return 0;
+	return (USBD_NORMAL_COMPLETION);
 }

 Static usbd_status
-uhmodem_regread(usbd_device_handle dev, uint8_t *data, size_t len)
+uhmodem_get_line_coding(struct uhmodem_softc *sc, int portno, 
+    usb_cdc_line_state_t *state)
 {
 	usb_device_request_t req;
 	usbd_status err;
+	int iface = sc->sc_iface[portno].sc_iface_number;

 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
-	req.bRequest = UHMODEM_REGREAD;
-	USETW(req.wValue, 0x0000);
-	USETW(req.wIndex, 0x0000);
-	USETW(req.wLength, len);
-	err = usbd_do_request(dev, &req, data);
+	req.bRequest = UCDC_GET_LINE_CODING;
+	USETW(req.wValue, 0);		/* zero */
+	USETW(req.wIndex, iface);	/* iface */
+	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+	err = usbd_do_request(sc->sc_udev, &req, state);

 	if (err)
 		return err;

-	return 0;
+	return (USBD_NORMAL_COMPLETION);
 }

-#if 0
 Static usbd_status
-uhmodem_regsetup(usbd_device_handle dev, uint16_t cmd)
+uhmodem_set_ctrl_line_state(struct uhmodem_softc *sc, int portno, uint16_t cmd)
 {
 	usb_device_request_t req;
 	usbd_status err;
+	int iface = sc->sc_iface[portno].sc_iface_number;

 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
-	req.bRequest = UHMODEM_SETUP;
-	USETW(req.wValue, cmd);
-	USETW(req.wIndex, 0x0000);
-	USETW(req.wLength, 0);
-	err = usbd_do_request(dev, &req, 0);
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, cmd);		/* control signal bitmap */
+	USETW(req.wIndex, iface);	/* iface */
+	USETW(req.wLength, 0);		/* zero */
+	err = usbd_do_request(sc->sc_udev, &req, 0);

 	if (err)
 		return err;

-	return 0;
+	return (USBD_NORMAL_COMPLETION);
 }
-#endif

-Static  usbd_status 
-a2502_init(usbd_device_handle dev)
-{
-	uint8_t data[8];
-	static uint8_t init_cmd[] = {0x00, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x08};
-#ifdef UHMODEM_DEBUG
-	int i;
-#endif
-	if (uhmodem_regread(dev, data, 7)) {
-		DPRINTF(("%s: read fail\n", __func__));
-		return EIO;
-	}
-#ifdef UHMODEM_DEBUG
-	printf("%s: readdata: ", __func__);
-	for (i = 0; i < 7; i++)
-		printf("0x%x ", data[i]);
-#endif
-	if (uhmodem_regwrite(dev, init_cmd, sizeof(init_cmd)) ) {
-		DPRINTF(("%s: write fail\n", __func__));
-		return EIO;
-	}
-
-	if (uhmodem_regread(dev, data, 7)) { 
-		DPRINTF(("%s: read fail\n", __func__));
-		return EIO;
-	}
-#ifdef UHMODEM_DEBUG
-	printf("%s: readdata: ", __func__);
-	printf(" => ");
-	for (i = 0; i < 7; i++)
-		printf("0x%x ", data[i]);
-	printf("\n");
-#endif
-	return 0;
-}
-
-
-#if 0
-/* 
- * Windows device driver send these sequens of USB requests.
- * However currently I can't understand what the messege is,
- * disable this code when I get more information about it.
- */ 
-Static  usbd_status 
-e220_init(usbd_device_handle dev)
+Static usbd_status
+uhmodem_set_comm_feature(struct uhmodem_softc *sc, int portno, 
+    usb_cdc_abstract_state_t *as)
 {
-	uint8_t data[8];
 	usb_device_request_t req;
-	int i;
+	usbd_status err;
+	int iface = sc->sc_iface[portno].sc_iface_number;

-	/* vendor specific unknown request */
 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-	req.bRequest = 0x02;
-	USETW(req.wValue, 0x0001);
-	USETW(req.wIndex, 0x0000);
-	USETW(req.wLength, 2);
-	data[0] = 0x0;
-	data[1] = 0x0;
-	if (usbd_do_request(dev, &req, data))
-		goto error;
+	req.bRequest = UCDC_SET_COMM_FEATURE;
+	USETW(req.wValue, UCDC_ABSTRACT_STATE); /* feature selector */
+	USETW(req.wIndex, iface); /* iface */
+	USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);	

-	/* vendor specific unknown sequence */
-	if(uhmodem_regsetup(dev, 0x1)) 
-		goto error;
+	err = usbd_do_request(sc->sc_udev, &req, as);
+	if (err)
+		return err;

-	if (uhmodem_regread(dev, data, 7))
-		goto error;
+	return (USBD_NORMAL_COMPLETION);
+}

-	data[1] = 0x8;
-	data[2] = 0x7;
-	if (uhmodem_regwrite(dev, data, sizeof(data)) )
-		goto error;
+#if 0
+Static  usbd_status 
+e220_init(struct uhmodem_softc *sc, int portno)
+{
+	usb_cdc_abstract_state_t as;

-	if (uhmodem_regread(dev, data, 7))
+	USETW(as.wState, 0); /* state no multiplex, no idle */
+	if (uhmodem_set_comm_feature(sc, portno, &as))
 		goto error;
-		/* XXX should verify the read data ? */

-	if (uhmodem_regsetup(dev, 0x3))
+	if (uhmodem_set_ctrl_line_state(sc, portno, 
+	    UCDC_LINE_DTR | UCDC_LINE_RTS))
 		goto error;

 	return (0);
 error:
 	DPRINTF(("%s: E220 init request fail\n", __func__));
-	return (EIO);
+	return (USBD_IOERROR);
 }
 #endif

Index: sys/dev/usb/files.usb
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/files.usb,v
retrieving revision 1.84
diff -u -p -r1.84 files.usb
--- sys/dev/usb/files.usb	26 May 2008 00:23:05 -0000	1.84
+++ sys/dev/usb/files.usb	2 Jul 2008 01:14:32 -0000
@@ -242,7 +242,7 @@ attach	ubsa at usbifif
 file	dev/usb/ubsa.c			ubsa

 # Huawei E220 3G/HSDPA modem (ubsa)
-device	uhmodem: ucombus, ubsa_common
+device	uhmodem: ucombus
 attach	uhmodem at usbifif
 file	dev/usb/uhmodem.c		uhmodem



>Release-Note:

>Audit-Trail:

State-Changed-From-To: open->Analyzed
State-Changed-By: ichiro@NetBSD.org
State-Changed-When: Wed, 02 Jul 2008 02:13:57 +0000
State-Changed-Why:
analyzed..


State-Changed-From-To: Analyzed->analyzed
State-Changed-By: dholland@NetBSD.org
State-Changed-When: Mon, 07 Jul 2008 06:33:22 +0000
State-Changed-Why:
typo.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.