xref: /openbsd/sys/dev/usb/umsm.c (revision 7b36286a)
1 /*	$OpenBSD: umsm.c,v 1.33 2008/07/30 15:40:39 canacar 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 = 1;
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_UMASS	(DEV_UMASS1 | DEV_UMASS2)
111 };
112 
113 static const struct umsm_type umsm_devs[] = {
114 	{{ USB_VENDOR_AIRPRIME,	USB_PRODUCT_AIRPRIME_PC5220 }, 0},
115 	{{ USB_VENDOR_ANYDATA,	USB_PRODUCT_ANYDATA_A2502 }, 0},
116 	{{ USB_VENDOR_ANYDATA,	USB_PRODUCT_ANYDATA_ADU_500A }, 0},
117 	{{ USB_VENDOR_DELL,	USB_PRODUCT_DELL_W5500 }, 0},
118 	{{ USB_VENDOR_HUAWEI,	USB_PRODUCT_HUAWEI_E220 }, DEV_HUAWEI},
119 	{{ USB_VENDOR_HUAWEI,	USB_PRODUCT_HUAWEI_E618 }, 0},
120 	{{ USB_VENDOR_KYOCERA2,	USB_PRODUCT_KYOCERA2_KPC650 }, 0},
121 	{{ USB_VENDOR_NOVATEL1,	USB_PRODUCT_NOVATEL1_FLEXPACKGPS }, 0},
122 	{{ USB_VENDOR_NOVATEL,	USB_PRODUCT_NOVATEL_EXPRESSCARD }, 0},
123 	{{ USB_VENDOR_NOVATEL,	USB_PRODUCT_NOVATEL_MERLINV620 }, 0},
124 	{{ USB_VENDOR_NOVATEL,	USB_PRODUCT_NOVATEL_S720 }, 0},
125 	{{ USB_VENDOR_NOVATEL,	USB_PRODUCT_NOVATEL_U720 }, 0},
126 	{{ USB_VENDOR_NOVATEL,	USB_PRODUCT_NOVATEL_XU870 }, 0},
127 	{{ USB_VENDOR_NOVATEL,	USB_PRODUCT_NOVATEL_ES620 }, 0},
128 	{{ USB_VENDOR_OPTION,	USB_PRODUCT_OPTION_GT3GPLUS }, 0},
129 	{{ USB_VENDOR_OPTION,	USB_PRODUCT_OPTION_GSICON72 }, DEV_UMASS1},
130 	{{ USB_VENDOR_OPTION,	USB_PRODUCT_OPTION_GTHSDPA225 }, DEV_UMASS2},
131 	{{ USB_VENDOR_QUALCOMM,	USB_PRODUCT_QUALCOMM_MSM_DRIVER }, DEV_UMASS1},
132 	{{ USB_VENDOR_QUALCOMM,	USB_PRODUCT_QUALCOMM_MSM_HSDPA }, 0},
133 	{{ USB_VENDOR_QUALCOMM,	USB_PRODUCT_QUALCOMM_MSM_HSDPA2 }, 0},
134 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_EM5625 }, 0},
135 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_AIRCARD_580 }, 0},
136 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_AIRCARD_595 }, 0},
137 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_AIRCARD_875 }, 0},
138 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC5720 }, 0},
139 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC5725 }, 0},
140 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC5725_2 }, 0},
141 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC8755 }, 0},
142 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC8755_2 }, 0},
143 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC8755_3 }, 0},
144 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC8765 }, 0},
145 	{{ USB_VENDOR_SIERRA,	USB_PRODUCT_SIERRA_MC8775 }, 0},
146 };
147 
148 #define umsm_lookup(v, p) ((const struct umsm_type *)usb_lookup(umsm_devs, v, p))
149 
150 struct cfdriver umsm_cd = {
151 	NULL, "umsm", DV_DULL
152 };
153 
154 const struct cfattach umsm_ca = {
155 	sizeof(struct umsm_softc),
156 	umsm_match,
157 	umsm_attach,
158 	umsm_detach,
159 	umsm_activate,
160 };
161 
162 int
163 umsm_match(struct device *parent, void *match, void *aux)
164 {
165 	struct usb_attach_arg *uaa = aux;
166 	usb_interface_descriptor_t *id;
167 	uint16_t flag;
168 
169 	if (uaa->iface == NULL)
170 		return UMATCH_NONE;
171 
172 	/*
173 	 * Some devices (eg Huawei E220) have multiple interfaces and some
174 	 * of them are of class umass. Don't claim ownership in such case.
175 	 */
176 	if (umsm_lookup(uaa->vendor, uaa->product) != NULL) {
177 		id = usbd_get_interface_descriptor(uaa->iface);
178 		flag = umsm_lookup(uaa->vendor, uaa->product)->umsm_flag;
179 
180 		if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
181 			/*
182 			 * Some high-speed modem requre special care.
183 			 */
184 			if (flag & DEV_HUAWEI) {
185 				if  (uaa->ifaceno != 2)
186 					return UMATCH_VENDOR_IFACESUBCLASS;
187 				else
188 					return UMATCH_NONE;
189 			} else if (flag & DEV_UMASS)
190 				return UMATCH_VENDOR_IFACESUBCLASS;
191 			else
192 				return UMATCH_NONE;
193 		} else
194 			return UMATCH_VENDOR_IFACESUBCLASS;
195 	}
196 
197 	return UMATCH_NONE;
198 }
199 
200 void
201 umsm_attach(struct device *parent, struct device *self, void *aux)
202 {
203 	struct umsm_softc *sc = (struct umsm_softc *)self;
204 	struct usb_attach_arg *uaa = aux;
205 	struct ucom_attach_args uca;
206 	usb_interface_descriptor_t *id;
207 	usb_endpoint_descriptor_t *ed;
208 	int i;
209 
210 	bzero(&uca, sizeof(uca));
211 	sc->sc_udev = uaa->device;
212 	sc->sc_iface = uaa->iface;
213 	sc->sc_flag  = umsm_lookup(uaa->vendor, uaa->product)->umsm_flag;
214 
215 	id = usbd_get_interface_descriptor(sc->sc_iface);
216 
217 	/*
218 	 * Some 3G modem devices have multiple interface and some
219 	 * of them are umass class. Don't claim ownership in such case.
220 	 */
221 	if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
222 		/*
223 		 * Some 3G modems require special request to
224 		 * enable it's modem function.
225 		 */
226 		if ((sc->sc_flag & DEV_HUAWEI) && uaa->ifaceno == 0) {
227                         umsm_huawei_changemode(uaa->device);
228 			printf("%s: umass only mode. need to reattach\n",
229 				sc->sc_dev.dv_xname);
230 		} else if ((sc->sc_flag & DEV_UMASS) && uaa->ifaceno == 0) {
231 			umsm_umass_changemode(sc);
232 		}
233 
234 		/*
235 		 * The device will reset its own bus from the device side
236 		 * when its mode was changed, so just return.
237 		 */
238 		return;
239 	}
240 
241 	sc->sc_iface_no = id->bInterfaceNumber;
242 	uca.bulkin = uca.bulkout = -1;
243 	sc->sc_intr_number = sc->sc_isize = -1;
244 	for (i = 0; i < id->bNumEndpoints; i++) {
245 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
246 		if (ed == NULL) {
247 			printf("%s: no endpoint descriptor found for %d\n",
248 			    sc->sc_dev.dv_xname, i);
249 			sc->sc_dying = 1;
250 			return;
251 		}
252 
253 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
254 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
255 			sc->sc_intr_number = ed->bEndpointAddress;
256 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
257 			DPRINTF(("%s: find interrupt endpoint for %s\n",
258 				__func__, sc->sc_dev.dv_xname));
259 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
260 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
261 			uca.bulkin = ed->bEndpointAddress;
262 		else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
263 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
264 			uca.bulkout = ed->bEndpointAddress;
265 	}
266 	if (uca.bulkin == -1 || uca.bulkout == -1) {
267 		printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
268 		sc->sc_dying = 1;
269 		return;
270 	}
271 
272 	sc->sc_dtr = sc->sc_rts = -1;
273 
274 	/* We need to force size as some devices lie */
275 	uca.ibufsize = UMSMBUFSZ;
276 	uca.obufsize = UMSMBUFSZ;
277 	uca.ibufsizepad = UMSMBUFSZ;
278 	uca.opkthdrlen = 0;
279 	uca.device = sc->sc_udev;
280 	uca.iface = sc->sc_iface;
281 	uca.methods = &umsm_methods;
282 	uca.arg = sc;
283 	uca.info = NULL;
284 	uca.portno = UCOM_UNK_PORTNO;
285 
286 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
287 	    &sc->sc_dev);
288 
289 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
290 }
291 
292 int
293 umsm_detach(struct device *self, int flags)
294 {
295 	struct umsm_softc *sc = (struct umsm_softc *)self;
296 	int rv = 0;
297 
298 	/* close the interrupt endpoint if that is opened */
299 	if (sc->sc_intr_pipe != NULL) {
300 		usbd_abort_pipe(sc->sc_intr_pipe);
301 		usbd_close_pipe(sc->sc_intr_pipe);
302 		free(sc->sc_intr_buf, M_USBDEV);
303 		sc->sc_intr_pipe = NULL;
304 	}
305 
306 	sc->sc_dying = 1;
307 	if (sc->sc_subdev != NULL) {
308 		rv = config_detach(sc->sc_subdev, flags);
309 		sc->sc_subdev = NULL;
310 	}
311 
312 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
313 			   &sc->sc_dev);
314 
315 	return (rv);
316 }
317 
318 int
319 umsm_activate(struct device *self, enum devact act)
320 {
321 	struct umsm_softc *sc = (struct umsm_softc *)self;
322 	int rv = 0;
323 
324 	switch (act) {
325 	case DVACT_ACTIVATE:
326 		break;
327 
328 	case DVACT_DEACTIVATE:
329 		if (sc->sc_subdev != NULL)
330 			rv = config_deactivate(sc->sc_subdev);
331 		sc->sc_dying = 1;
332 		break;
333 	}
334 	return (rv);
335 }
336 
337 int
338 umsm_open(void *addr, int portno)
339 {
340 	struct umsm_softc *sc = addr;
341 	int err;
342 
343 	if (sc->sc_dying)
344 		return (ENXIO);
345 
346 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
347 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
348 		err = usbd_open_pipe_intr(sc->sc_iface,
349 		    sc->sc_intr_number,
350 		    USBD_SHORT_XFER_OK,
351 		    &sc->sc_intr_pipe,
352 		    sc,
353 		    sc->sc_intr_buf,
354 		    sc->sc_isize,
355 		    umsm_intr,
356 		    UMSM_INTR_INTERVAL);
357 		if (err) {
358 			printf("%s: cannot open interrupt pipe (addr %d)\n",
359 			    sc->sc_dev.dv_xname,
360 			    sc->sc_intr_number);
361 			return (EIO);
362 		}
363 	}
364 
365 	return (0);
366 }
367 
368 void
369 umsm_close(void *addr, int portno)
370 {
371 	struct umsm_softc *sc = addr;
372 	int err;
373 
374 	if (sc->sc_dying)
375 		return;
376 
377 	if (sc->sc_intr_pipe != NULL) {
378 		err = usbd_abort_pipe(sc->sc_intr_pipe);
379        		if (err)
380 			printf("%s: abort interrupt pipe failed: %s\n",
381 			    sc->sc_dev.dv_xname,
382 			    usbd_errstr(err));
383 		err = usbd_close_pipe(sc->sc_intr_pipe);
384 		if (err)
385 			printf("%s: close interrupt pipe failed: %s\n",
386 			    sc->sc_dev.dv_xname,
387 			    usbd_errstr(err));
388 		free(sc->sc_intr_buf, M_USBDEV);
389 		sc->sc_intr_pipe = NULL;
390 	}
391 }
392 
393 void
394 umsm_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
395 	usbd_status status)
396 {
397 	struct umsm_softc *sc = priv;
398 	usb_cdc_notification_t *buf;
399 	u_char mstatus;
400 
401 	buf = (usb_cdc_notification_t *)sc->sc_intr_buf;
402 	if (sc->sc_dying)
403 		return;
404 
405 	if (status != USBD_NORMAL_COMPLETION) {
406 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
407 			return;
408 
409 		DPRINTF(("%s: umsm_intr: abnormal status: %s\n",
410 		    sc->sc_dev.dv_xname, usbd_errstr(status)));
411 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
412 		return;
413 	}
414 
415 	if (buf->bmRequestType != UCDC_NOTIFICATION) {
416 #if 1 /* test */
417 		printf("%s: this device is not using CDC notify message in intr pipe.\n"
418 		    "Please send your dmesg to <bugs@openbsd.org>, thanks.\n",
419 		    sc->sc_dev.dv_xname);
420 		printf("%s: intr buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
421 		    sc->sc_dev.dv_xname,
422 		    sc->sc_intr_buf[0], sc->sc_intr_buf[1],
423 		    sc->sc_intr_buf[2], sc->sc_intr_buf[3],
424 		    sc->sc_intr_buf[4], sc->sc_intr_buf[5],
425 		    sc->sc_intr_buf[6]);
426 #else
427 		DPRINTF(("%s: umsm_intr: unknown message type(0x%02x)\n",
428 		    sc->sc_dev.dv_xname, buf->bmRequestType));
429 #endif
430 		return;
431 	}
432 
433 	if (buf->bNotification == UCDC_N_SERIAL_STATE) {
434 		/* invalid message length, discard it */
435 		if (UGETW(buf->wLength) != 2)
436 			return;
437 		/* XXX: sc_lsr is always 0 */
438 		sc->sc_lsr = sc->sc_msr = 0;
439 		mstatus = buf->data[0];
440 		if (ISSET(mstatus, UCDC_N_SERIAL_RI))
441 			sc->sc_msr |= UMSR_RI;
442 		if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
443 			sc->sc_msr |= UMSR_DSR;
444 		if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
445 			sc->sc_msr |= UMSR_DCD;
446 	} else if (buf->bNotification != UCDC_N_CONNECTION_SPEED_CHANGE) {
447 		DPRINTF(("%s: umsm_intr: unknown notify message (0x%02x)\n",
448 		    sc->sc_dev.dv_xname, buf->bNotification));
449 		return;
450 	}
451 
452 	ucom_status_change((struct ucom_softc *)sc->sc_subdev);
453 }
454 
455 void
456 umsm_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
457 {
458 	struct umsm_softc *sc = addr;
459 
460 	if (lsr != NULL)
461 		*lsr = sc->sc_lsr;
462 	if (msr != NULL)
463 		*msr = sc->sc_msr;
464 }
465 
466 void
467 umsm_set(void *addr, int portno, int reg, int onoff)
468 {
469 	struct umsm_softc *sc = addr;
470 	usb_device_request_t req;
471 	int ls;
472 
473 	switch (reg) {
474 	case UCOM_SET_DTR:
475 		if (sc->sc_dtr == onoff)
476 			return;
477 		sc->sc_dtr = onoff;
478 		break;
479 	case UCOM_SET_RTS:
480 		if (sc->sc_rts == onoff)
481 			return;
482 		sc->sc_rts = onoff;
483 		break;
484 	default:
485 		return;
486 	}
487 
488 	/* build a usb request */
489 	ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
490 	     (sc->sc_rts ? UCDC_LINE_RTS : 0);
491 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
492 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
493 	USETW(req.wValue, ls);
494 	USETW(req.wIndex, sc->sc_iface_no);
495 	USETW(req.wLength, 0);
496 
497 	(void)usbd_do_request(sc->sc_udev, &req, 0);
498 }
499 
500 usbd_status
501 umsm_huawei_changemode(usbd_device_handle dev)
502 {
503 	usb_device_request_t req;
504 	usbd_status err;
505 
506 	req.bmRequestType = UT_WRITE_DEVICE;
507 	req.bRequest = UR_SET_FEATURE;
508 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
509 	USETW(req.wIndex, E220_MODE_CHANGE_REQUEST);
510 	USETW(req.wLength, 0);
511 
512 	err = usbd_do_request(dev, &req, 0);
513 	if (err)
514 		return (EIO);
515 
516 	return (0);
517 }
518 
519 usbd_status
520 umsm_umass_changemode(struct umsm_softc *sc)
521 {
522 #define UMASS_CMD_REZERO_UNIT	0x01
523 	usb_interface_descriptor_t *id;
524 	usb_endpoint_descriptor_t *ed;
525 	usbd_xfer_handle xfer;
526 	usbd_pipe_handle cmdpipe;
527 	usbd_status err;
528 	u_int32_t n;
529 	void *bufp;
530 	int target_ep, i;
531 
532 	umass_bbb_cbw_t	cbw;
533 	static int dCBWTag = 0x12345678;
534 
535 	USETDW(cbw.dCBWSignature, CBWSIGNATURE);
536 	USETDW(cbw.dCBWTag, dCBWTag);
537 	cbw.bCBWLUN   = 0;
538 	cbw.bCDBLength= 6;
539 	bzero(cbw.CBWCDB, sizeof(cbw.CBWCDB));
540 	cbw.CBWCDB[0] = UMASS_CMD_REZERO_UNIT;
541 	cbw.CBWCDB[1] = 0x0;	/* target LUN: 0 */
542 
543 	switch (sc->sc_flag) {
544 	case DEV_UMASS1:
545 		USETDW(cbw.dCBWDataTransferLength, 0x0);
546 		cbw.bCBWFlags = CBWFLAGS_OUT;
547 		break;
548 	case DEV_UMASS2:
549 		USETDW(cbw.dCBWDataTransferLength, 0x1);
550 		cbw.bCBWFlags = CBWFLAGS_IN;
551 		break;
552 	default:
553 		DPRINTF(("%s: unknown device type.\n", sc->sc_dev.dv_xname));
554 		break;
555 	}
556 
557 	/* get command endpoint address */
558 	id = usbd_get_interface_descriptor(sc->sc_iface);
559 	for (i = 0; i < id->bNumEndpoints; i++) {
560 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
561 		if (ed == NULL) {
562 			return (USBD_IOERROR);
563 		}
564 
565 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
566 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
567 			target_ep = ed->bEndpointAddress;
568 	}
569 
570 	/* open command endppoint */
571 	err = usbd_open_pipe(sc->sc_iface, target_ep,
572 		USBD_EXCLUSIVE_USE, &cmdpipe);
573 	if (err) {
574 		DPRINTF(("%s: open pipe for modem change cmd failed: %s\n",
575 		    sc->sc_dev.dv_xname, usbd_errstr(err)));
576 		return (err);
577 	}
578 
579 	xfer = usbd_alloc_xfer(sc->sc_udev);
580 	if (xfer == NULL) {
581 		usbd_close_pipe(cmdpipe);
582 		return (USBD_NOMEM);
583 	} else {
584 		bufp = usbd_alloc_buffer(xfer, UMASS_BBB_CBW_SIZE);
585 		if (bufp == NULL)
586 			err = USBD_NOMEM;
587 		else {
588 			n = UMASS_BBB_CBW_SIZE;
589 			memcpy(bufp, &cbw, UMASS_BBB_CBW_SIZE);
590 			err = usbd_bulk_transfer(xfer, cmdpipe, USBD_NO_COPY,
591 			    USBD_NO_TIMEOUT, bufp, &n, "umsm");
592 			if (err)
593 				DPRINTF(("%s: send error:%s", __func__,
594 				    usbd_errstr(err)));
595 		}
596 		usbd_close_pipe(cmdpipe);
597 		usbd_free_buffer(xfer);
598 		usbd_free_xfer(xfer);
599 	}
600 
601 	return (err);
602 }
603