xref: /openbsd-src/sys/dev/usb/umsm.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: umsm.c,v 1.45 2009/03/25 15:02:13 yuo Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Yojiro UO <yuo@nui.org>
5  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Driver for Qualcomm MSM EVDO and UMTS communication devices */
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
26 #include <sys/device.h>
27 #include <sys/conf.h>
28 #include <sys/tty.h>
29 
30 #include <dev/usb/usb.h>
31 #include <dev/usb/usbdi.h>
32 #include <dev/usb/usbdi_util.h>
33 #include <dev/usb/usbdevs.h>
34 #include <dev/usb/ucomvar.h>
35 #include <dev/usb/usbcdc.h>
36 #include <dev/usb/umassvar.h>
37 #undef DPRINTF	/* undef DPRINTF for umass */
38 
39 #ifdef USB_DEBUG
40 #define UMSM_DEBUG
41 #endif
42 
43 #ifdef UMSM_DEBUG
44 int     umsmdebug = 0;
45 #define DPRINTFN(n, x)  do { if (umsmdebug > (n)) printf x; } while (0)
46 #else
47 #define DPRINTFN(n, x)
48 #endif
49 
50 #define DPRINTF(x) DPRINTFN(0, x)
51 
52 #define UMSMBUFSZ	4096
53 #define	UMSM_INTR_INTERVAL	100	/* ms */
54 #define E220_MODE_CHANGE_REQUEST 0x2
55 
56 int umsm_match(struct device *, void *, void *);
57 void umsm_attach(struct device *, struct device *, void *);
58 int umsm_detach(struct device *, int);
59 int umsm_activate(struct device *, enum devact);
60 
61 int umsm_open(void *, int);
62 void umsm_close(void *, int);
63 void umsm_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
64 void umsm_get_status(void *, int, u_char *, u_char *);
65 void umsm_set(void *, int, int, int);
66 
67 struct umsm_softc {
68 	struct device		 sc_dev;
69 	usbd_device_handle	 sc_udev;
70 	usbd_interface_handle	 sc_iface;
71 	int			 sc_iface_no;
72 	struct device		*sc_subdev;
73 	u_char			 sc_dying;
74 	uint16_t		 sc_flag;
75 
76 	/* interrupt ep */
77 	int			 sc_intr_number;
78 	usbd_pipe_handle	 sc_intr_pipe;
79 	u_char			*sc_intr_buf;
80 	int			 sc_isize;
81 
82 	u_char			 sc_lsr;	/* Local status register */
83 	u_char			 sc_msr;	/* status register */
84 	u_char			 sc_dtr;	/* current DTR state */
85 	u_char			 sc_rts;	/* current RTS state */
86 };
87 
88 usbd_status umsm_huawei_changemode(usbd_device_handle);
89 usbd_status umsm_umass_changemode(struct umsm_softc *);
90 
91 struct ucom_methods umsm_methods = {
92 	umsm_get_status,
93 	umsm_set,
94 	NULL,
95 	NULL,
96 	umsm_open,
97 	umsm_close,
98 	NULL,
99 	NULL,
100 };
101 
102 struct umsm_type {
103 	struct usb_devno	umsm_dev;
104 	uint16_t		umsm_flag;
105 /* device type */
106 #define	DEV_NORMAL	0x0000
107 #define	DEV_HUAWEI	0x0001
108 #define	DEV_UMASS1	0x0010
109 #define	DEV_UMASS2	0x0020
110 #define	DEV_UMASS3	0x0040
111 #define DEV_UMASS	(DEV_UMASS1 | DEV_UMASS2 | DEV_UMASS3)
112 };
113 
114 static const struct umsm_type umsm_devs[] = {
115 	{{ USB_VENDOR_AIRPRIME,	USB_PRODUCT_AIRPRIME_PC5220 }, 0},
116 
117 	{{ USB_VENDOR_ANYDATA,	USB_PRODUCT_ANYDATA_A2502 }, 0},
118 	{{ USB_VENDOR_ANYDATA,	USB_PRODUCT_ANYDATA_ADU_500A }, 0},
119 	{{ USB_VENDOR_ANYDATA,  USB_PRODUCT_ANYDATA_ADU_E100H }, 0},
120 
121 	{{ USB_VENDOR_DELL,	USB_PRODUCT_DELL_U740 }, 0},
122 	{{ USB_VENDOR_DELL,	USB_PRODUCT_DELL_W5500 }, 0},
123 
124 	{{ USB_VENDOR_HUAWEI,	USB_PRODUCT_HUAWEI_E220 }, DEV_HUAWEI},
125 	{{ USB_VENDOR_HUAWEI,	USB_PRODUCT_HUAWEI_E510 }, DEV_HUAWEI},
126 	{{ USB_VENDOR_HUAWEI,	USB_PRODUCT_HUAWEI_E618 }, DEV_HUAWEI},
127 
128 	{{ USB_VENDOR_LONGCHEER, USB_PRODUCT_LONGCHEER_D21LCMASS }, DEV_UMASS3},
129 	{{ USB_VENDOR_LONGCHEER, USB_PRODUCT_LONGCHEER_D21LC }, 0},
130 
131 	{{ USB_VENDOR_KYOCERA2,	USB_PRODUCT_KYOCERA2_KPC650 }, 0},
132 
133 	/* XXX Some qualcomm devices are missing */
134 	{{ USB_VENDOR_QUALCOMM,	USB_PRODUCT_QUALCOMM_MSM_DRIVER }, DEV_UMASS1},
135 	{{ USB_VENDOR_QUALCOMM,	USB_PRODUCT_QUALCOMM_MSM_HSDPA }, 0},
136 	{{ USB_VENDOR_QUALCOMM,	USB_PRODUCT_QUALCOMM_MSM_HSDPA2 }, 0},
137 
138 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_EXPRESSCARD }, 0},
139 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MERLINV620 }, 0},
140 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MERLINV740 }, 0},
141 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720 }, 0},
142 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MERLINU740 }, 0},
143 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MERLINU740_2 }, 0},
144 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870 }, 0},
145 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870 }, 0},
146 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D }, 0},
147 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620 }, 0},
148 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720 }, 0},
149 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727 }, DEV_UMASS1},
150 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D }, 0},
151 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MERLINX950D }, 0},
152 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD2 }, 0},
153 	{{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U760 }, 0},
154 
155 	{{ USB_VENDOR_NOVATEL1,	USB_PRODUCT_NOVATEL1_FLEXPACKGPS }, 0},
156 
157 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GFUSION }, 0},
158 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS }, 0},
159 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD }, 0},
160 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUADPLUS }, 0},
161 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GSICON72 }, DEV_UMASS1},
162 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTHSDPA225 }, DEV_UMASS2},
163 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36 }, 0},
164 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_SCORPION }, 0},
165 	{{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G }, 0},
166 
167 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625 }, 0},
168 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720 }, 0},
169 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD_595 }, 0},
170 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725 }, 0},
171 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E }, 0},
172 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597 }, 0},
173 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U }, 0},
174 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD_580 }, 0},
175 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2 }, 0},
176 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725_2 }, 0},
177 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2 }, 0},
178 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765 }, 0},
179 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755 }, 0},
180 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775 }, 0},
181 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3 }, 0},
182 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2 }, 0},
183 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD_875 }, 0},
184 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780 }, 0},
185 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 }, 0},
186 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880 }, 0},
187 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881 }, 0},
188 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E }, 0},
189 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E }, 0},
190 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U }, 0},
191 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U }, 0},
192 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC885U }, 0},
193 	{{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL }, DEV_UMASS1},
194 
195 	{{ USB_VENDOR_HP, USB_PRODUCT_HP_HS2300 }, 0},
196 
197 	{{ USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CNU510 }, 0}, /* ??? */
198 	{{ USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CCU550 }, 0}, /* ??? */
199 	{{ USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CGU628 }, DEV_UMASS1},
200 	{{ USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CGU628_DISK }, 0},
201 };
202 
203 #define umsm_lookup(v, p) ((const struct umsm_type *)usb_lookup(umsm_devs, v, p))
204 
205 struct cfdriver umsm_cd = {
206 	NULL, "umsm", DV_DULL
207 };
208 
209 const struct cfattach umsm_ca = {
210 	sizeof(struct umsm_softc),
211 	umsm_match,
212 	umsm_attach,
213 	umsm_detach,
214 	umsm_activate,
215 };
216 
217 int
218 umsm_match(struct device *parent, void *match, void *aux)
219 {
220 	struct usb_attach_arg *uaa = aux;
221 	usb_interface_descriptor_t *id;
222 	uint16_t flag;
223 
224 	if (uaa->iface == NULL)
225 		return UMATCH_NONE;
226 
227 	/*
228 	 * Some devices (eg Huawei E220) have multiple interfaces and some
229 	 * of them are of class umass. Don't claim ownership in such case.
230 	 */
231 	if (umsm_lookup(uaa->vendor, uaa->product) != NULL) {
232 		id = usbd_get_interface_descriptor(uaa->iface);
233 		flag = umsm_lookup(uaa->vendor, uaa->product)->umsm_flag;
234 
235 		if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
236 			/*
237 			 * Some high-speed modems require special care.
238 			 */
239 			if (flag & DEV_HUAWEI) {
240 				if  (uaa->ifaceno != 2)
241 					return UMATCH_VENDOR_IFACESUBCLASS;
242 				else
243 					return UMATCH_NONE;
244 			} else if (flag & DEV_UMASS)
245 				return UMATCH_VENDOR_IFACESUBCLASS;
246 			else
247 				return UMATCH_NONE;
248 		} else
249 			return UMATCH_VENDOR_IFACESUBCLASS;
250 	}
251 
252 	return UMATCH_NONE;
253 }
254 
255 void
256 umsm_attach(struct device *parent, struct device *self, void *aux)
257 {
258 	struct umsm_softc *sc = (struct umsm_softc *)self;
259 	struct usb_attach_arg *uaa = aux;
260 	struct ucom_attach_args uca;
261 	usb_interface_descriptor_t *id;
262 	usb_endpoint_descriptor_t *ed;
263 	int i;
264 
265 	bzero(&uca, sizeof(uca));
266 	sc->sc_udev = uaa->device;
267 	sc->sc_iface = uaa->iface;
268 	sc->sc_flag  = umsm_lookup(uaa->vendor, uaa->product)->umsm_flag;
269 
270 	id = usbd_get_interface_descriptor(sc->sc_iface);
271 
272 	/*
273 	 * Some 3G modems have multiple interfaces and some of them
274 	 * are umass class. Don't claim ownership in such case.
275 	 */
276 	if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
277 		/*
278 		 * Some 3G modems require a special request to
279 		 * enable their modem function.
280 		 */
281 		if ((sc->sc_flag & DEV_HUAWEI) && uaa->ifaceno == 0) {
282                         umsm_huawei_changemode(uaa->device);
283 			printf("%s: umass only mode. need to reattach\n",
284 				sc->sc_dev.dv_xname);
285 		} else if ((sc->sc_flag & DEV_UMASS) && uaa->ifaceno == 0) {
286 			umsm_umass_changemode(sc);
287 		}
288 
289 		/*
290 		 * The device will reset its own bus from the device side
291 		 * when its mode was changed, so just return.
292 		 */
293 		return;
294 	}
295 
296 	sc->sc_iface_no = id->bInterfaceNumber;
297 	uca.bulkin = uca.bulkout = -1;
298 	sc->sc_intr_number = sc->sc_isize = -1;
299 	for (i = 0; i < id->bNumEndpoints; i++) {
300 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
301 		if (ed == NULL) {
302 			printf("%s: no endpoint descriptor found for %d\n",
303 			    sc->sc_dev.dv_xname, i);
304 			sc->sc_dying = 1;
305 			return;
306 		}
307 
308 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
309 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
310 			sc->sc_intr_number = ed->bEndpointAddress;
311 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
312 			DPRINTF(("%s: find interrupt endpoint for %s\n",
313 				__func__, sc->sc_dev.dv_xname));
314 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
315 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
316 			uca.bulkin = ed->bEndpointAddress;
317 		else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
318 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
319 			uca.bulkout = ed->bEndpointAddress;
320 	}
321 	if (uca.bulkin == -1 || uca.bulkout == -1) {
322 		printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
323 		sc->sc_dying = 1;
324 		return;
325 	}
326 
327 	sc->sc_dtr = sc->sc_rts = -1;
328 
329 	/* We need to force size as some devices lie */
330 	uca.ibufsize = UMSMBUFSZ;
331 	uca.obufsize = UMSMBUFSZ;
332 	uca.ibufsizepad = UMSMBUFSZ;
333 	uca.opkthdrlen = 0;
334 	uca.device = sc->sc_udev;
335 	uca.iface = sc->sc_iface;
336 	uca.methods = &umsm_methods;
337 	uca.arg = sc;
338 	uca.info = NULL;
339 	uca.portno = UCOM_UNK_PORTNO;
340 
341 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
342 	    &sc->sc_dev);
343 
344 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
345 }
346 
347 int
348 umsm_detach(struct device *self, int flags)
349 {
350 	struct umsm_softc *sc = (struct umsm_softc *)self;
351 	int rv = 0;
352 
353 	/* close the interrupt endpoint if that is opened */
354 	if (sc->sc_intr_pipe != NULL) {
355 		usbd_abort_pipe(sc->sc_intr_pipe);
356 		usbd_close_pipe(sc->sc_intr_pipe);
357 		free(sc->sc_intr_buf, M_USBDEV);
358 		sc->sc_intr_pipe = NULL;
359 	}
360 
361 	sc->sc_dying = 1;
362 	if (sc->sc_subdev != NULL) {
363 		rv = config_detach(sc->sc_subdev, flags);
364 		sc->sc_subdev = NULL;
365 	}
366 
367 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
368 			   &sc->sc_dev);
369 
370 	return (rv);
371 }
372 
373 int
374 umsm_activate(struct device *self, enum devact act)
375 {
376 	struct umsm_softc *sc = (struct umsm_softc *)self;
377 	int rv = 0;
378 
379 	switch (act) {
380 	case DVACT_ACTIVATE:
381 		break;
382 
383 	case DVACT_DEACTIVATE:
384 		if (sc->sc_subdev != NULL)
385 			rv = config_deactivate(sc->sc_subdev);
386 		sc->sc_dying = 1;
387 		break;
388 	}
389 	return (rv);
390 }
391 
392 int
393 umsm_open(void *addr, int portno)
394 {
395 	struct umsm_softc *sc = addr;
396 	int err;
397 
398 	if (sc->sc_dying)
399 		return (ENXIO);
400 
401 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
402 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
403 		err = usbd_open_pipe_intr(sc->sc_iface,
404 		    sc->sc_intr_number,
405 		    USBD_SHORT_XFER_OK,
406 		    &sc->sc_intr_pipe,
407 		    sc,
408 		    sc->sc_intr_buf,
409 		    sc->sc_isize,
410 		    umsm_intr,
411 		    UMSM_INTR_INTERVAL);
412 		if (err) {
413 			printf("%s: cannot open interrupt pipe (addr %d)\n",
414 			    sc->sc_dev.dv_xname,
415 			    sc->sc_intr_number);
416 			return (EIO);
417 		}
418 	}
419 
420 	return (0);
421 }
422 
423 void
424 umsm_close(void *addr, int portno)
425 {
426 	struct umsm_softc *sc = addr;
427 	int err;
428 
429 	if (sc->sc_dying)
430 		return;
431 
432 	if (sc->sc_intr_pipe != NULL) {
433 		err = usbd_abort_pipe(sc->sc_intr_pipe);
434        		if (err)
435 			printf("%s: abort interrupt pipe failed: %s\n",
436 			    sc->sc_dev.dv_xname,
437 			    usbd_errstr(err));
438 		err = usbd_close_pipe(sc->sc_intr_pipe);
439 		if (err)
440 			printf("%s: close interrupt pipe failed: %s\n",
441 			    sc->sc_dev.dv_xname,
442 			    usbd_errstr(err));
443 		free(sc->sc_intr_buf, M_USBDEV);
444 		sc->sc_intr_pipe = NULL;
445 	}
446 }
447 
448 void
449 umsm_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
450 	usbd_status status)
451 {
452 	struct umsm_softc *sc = priv;
453 	usb_cdc_notification_t *buf;
454 	u_char mstatus;
455 
456 	buf = (usb_cdc_notification_t *)sc->sc_intr_buf;
457 	if (sc->sc_dying)
458 		return;
459 
460 	if (status != USBD_NORMAL_COMPLETION) {
461 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
462 			return;
463 
464 		DPRINTF(("%s: umsm_intr: abnormal status: %s\n",
465 		    sc->sc_dev.dv_xname, usbd_errstr(status)));
466 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
467 		return;
468 	}
469 
470 	if (buf->bmRequestType != UCDC_NOTIFICATION) {
471 #if 1 /* test */
472 		printf("%s: this device is not using CDC notify message in intr pipe.\n"
473 		    "Please send your dmesg to <bugs@openbsd.org>, thanks.\n",
474 		    sc->sc_dev.dv_xname);
475 		printf("%s: intr buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
476 		    sc->sc_dev.dv_xname,
477 		    sc->sc_intr_buf[0], sc->sc_intr_buf[1],
478 		    sc->sc_intr_buf[2], sc->sc_intr_buf[3],
479 		    sc->sc_intr_buf[4], sc->sc_intr_buf[5],
480 		    sc->sc_intr_buf[6]);
481 #else
482 		DPRINTF(("%s: umsm_intr: unknown message type(0x%02x)\n",
483 		    sc->sc_dev.dv_xname, buf->bmRequestType));
484 #endif
485 		return;
486 	}
487 
488 	if (buf->bNotification == UCDC_N_SERIAL_STATE) {
489 		/* invalid message length, discard it */
490 		if (UGETW(buf->wLength) != 2)
491 			return;
492 		/* XXX: sc_lsr is always 0 */
493 		sc->sc_lsr = sc->sc_msr = 0;
494 		mstatus = buf->data[0];
495 		if (ISSET(mstatus, UCDC_N_SERIAL_RI))
496 			sc->sc_msr |= UMSR_RI;
497 		if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
498 			sc->sc_msr |= UMSR_DSR;
499 		if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
500 			sc->sc_msr |= UMSR_DCD;
501 	} else if (buf->bNotification != UCDC_N_CONNECTION_SPEED_CHANGE) {
502 		DPRINTF(("%s: umsm_intr: unknown notify message (0x%02x)\n",
503 		    sc->sc_dev.dv_xname, buf->bNotification));
504 		return;
505 	}
506 
507 	ucom_status_change((struct ucom_softc *)sc->sc_subdev);
508 }
509 
510 void
511 umsm_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
512 {
513 	struct umsm_softc *sc = addr;
514 
515 	if (lsr != NULL)
516 		*lsr = sc->sc_lsr;
517 	if (msr != NULL)
518 		*msr = sc->sc_msr;
519 }
520 
521 void
522 umsm_set(void *addr, int portno, int reg, int onoff)
523 {
524 	struct umsm_softc *sc = addr;
525 	usb_device_request_t req;
526 	int ls;
527 
528 	switch (reg) {
529 	case UCOM_SET_DTR:
530 		if (sc->sc_dtr == onoff)
531 			return;
532 		sc->sc_dtr = onoff;
533 		break;
534 	case UCOM_SET_RTS:
535 		if (sc->sc_rts == onoff)
536 			return;
537 		sc->sc_rts = onoff;
538 		break;
539 	default:
540 		return;
541 	}
542 
543 	/* build a usb request */
544 	ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
545 	     (sc->sc_rts ? UCDC_LINE_RTS : 0);
546 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
547 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
548 	USETW(req.wValue, ls);
549 	USETW(req.wIndex, sc->sc_iface_no);
550 	USETW(req.wLength, 0);
551 
552 	(void)usbd_do_request(sc->sc_udev, &req, 0);
553 }
554 
555 usbd_status
556 umsm_huawei_changemode(usbd_device_handle dev)
557 {
558 	usb_device_request_t req;
559 	usbd_status err;
560 
561 	req.bmRequestType = UT_WRITE_DEVICE;
562 	req.bRequest = UR_SET_FEATURE;
563 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
564 	USETW(req.wIndex, E220_MODE_CHANGE_REQUEST);
565 	USETW(req.wLength, 0);
566 
567 	err = usbd_do_request(dev, &req, 0);
568 	if (err)
569 		return (EIO);
570 
571 	return (0);
572 }
573 
574 usbd_status
575 umsm_umass_changemode(struct umsm_softc *sc)
576 {
577 #define UMASS_CMD_REZERO_UNIT	0x01
578 	usb_interface_descriptor_t *id;
579 	usb_endpoint_descriptor_t *ed;
580 	usbd_xfer_handle xfer;
581 	usbd_pipe_handle cmdpipe;
582 	usbd_status err;
583 	u_int32_t n;
584 	void *bufp;
585 	int target_ep, i;
586 
587 	umass_bbb_cbw_t	cbw;
588 	static int dCBWTag = 0x12345678;
589 
590 	USETDW(cbw.dCBWSignature, CBWSIGNATURE);
591 	USETDW(cbw.dCBWTag, dCBWTag);
592 	cbw.bCBWLUN   = 0;
593 	cbw.bCDBLength= 6;
594 	bzero(cbw.CBWCDB, sizeof(cbw.CBWCDB));
595 
596 	switch (sc->sc_flag) {
597 	case DEV_UMASS1:
598 		USETDW(cbw.dCBWDataTransferLength, 0x0);
599 		cbw.bCBWFlags = CBWFLAGS_OUT;
600 		cbw.CBWCDB[0] = UMASS_CMD_REZERO_UNIT;
601 		cbw.CBWCDB[1] = 0x0;	/* target LUN: 0 */
602 		break;
603 	case DEV_UMASS2:
604 		USETDW(cbw.dCBWDataTransferLength, 0x1);
605 		cbw.bCBWFlags = CBWFLAGS_IN;
606 		cbw.CBWCDB[0] = UMASS_CMD_REZERO_UNIT;
607 		cbw.CBWCDB[1] = 0x0;	/* target LUN: 0 */
608 		break;
609 	case DEV_UMASS3: /* longcheer */
610 		USETDW(cbw.dCBWDataTransferLength, 0x80);
611 		cbw.bCBWFlags = CBWFLAGS_IN;
612 		cbw.CBWCDB[0] = 0x06;
613 		cbw.CBWCDB[1] = 0xf5;
614 		cbw.CBWCDB[2] = 0x04;
615 		cbw.CBWCDB[3] = 0x02;
616 		cbw.CBWCDB[4] = 0x52;
617 		cbw.CBWCDB[5] = 0x70;
618 		break;
619 	default:
620 		DPRINTF(("%s: unknown device type.\n", sc->sc_dev.dv_xname));
621 		break;
622 	}
623 
624 	/* get command endpoint address */
625 	id = usbd_get_interface_descriptor(sc->sc_iface);
626 	for (i = 0; i < id->bNumEndpoints; i++) {
627 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
628 		if (ed == NULL) {
629 			return (USBD_IOERROR);
630 		}
631 
632 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
633 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
634 			target_ep = ed->bEndpointAddress;
635 	}
636 
637 	/* open command endppoint */
638 	err = usbd_open_pipe(sc->sc_iface, target_ep,
639 		USBD_EXCLUSIVE_USE, &cmdpipe);
640 	if (err) {
641 		DPRINTF(("%s: open pipe for modem change cmd failed: %s\n",
642 		    sc->sc_dev.dv_xname, usbd_errstr(err)));
643 		return (err);
644 	}
645 
646 	xfer = usbd_alloc_xfer(sc->sc_udev);
647 	if (xfer == NULL) {
648 		usbd_close_pipe(cmdpipe);
649 		return (USBD_NOMEM);
650 	} else {
651 		bufp = usbd_alloc_buffer(xfer, UMASS_BBB_CBW_SIZE);
652 		if (bufp == NULL)
653 			err = USBD_NOMEM;
654 		else {
655 			n = UMASS_BBB_CBW_SIZE;
656 			memcpy(bufp, &cbw, UMASS_BBB_CBW_SIZE);
657 			err = usbd_bulk_transfer(xfer, cmdpipe, USBD_NO_COPY,
658 			    USBD_NO_TIMEOUT, bufp, &n, "umsm");
659 			if (err)
660 				DPRINTF(("%s: send error:%s", __func__,
661 				    usbd_errstr(err)));
662 		}
663 		usbd_close_pipe(cmdpipe);
664 		usbd_free_buffer(xfer);
665 		usbd_free_xfer(xfer);
666 	}
667 
668 	return (err);
669 }
670