xref: /freebsd/sys/dev/usb/serial/umodem.c (revision 71ccf09269546d398fa847168fc74c22d6338a62)
1 /*	$NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $	*/
2 
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5 
6 /*-
7  * Copyright (c) 2003, M. Warner Losh <imp@FreeBSD.org>.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*-
33  * Copyright (c) 1998 The NetBSD Foundation, Inc.
34  * All rights reserved.
35  *
36  * This code is derived from software contributed to The NetBSD Foundation
37  * by Lennart Augustsson (lennart@augustsson.net) at
38  * Carlstedt Research & Technology.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. All advertising materials mentioning features or use of this software
49  *    must display the following acknowledgement:
50  *        This product includes software developed by the NetBSD
51  *        Foundation, Inc. and its contributors.
52  * 4. Neither the name of The NetBSD Foundation nor the names of its
53  *    contributors may be used to endorse or promote products derived
54  *    from this software without specific prior written permission.
55  *
56  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
57  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
58  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
60  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
62  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
64  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
65  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66  * POSSIBILITY OF SUCH DAMAGE.
67  */
68 
69 /*
70  * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
71  *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
72  *                   http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
73  */
74 
75 /*
76  * TODO:
77  * - Add error recovery in various places; the big problem is what
78  *   to do in a callback if there is an error.
79  * - Implement a Call Device for modems without multiplexed commands.
80  *
81  */
82 
83 #include <sys/stdint.h>
84 #include <sys/stddef.h>
85 #include <sys/param.h>
86 #include <sys/queue.h>
87 #include <sys/types.h>
88 #include <sys/systm.h>
89 #include <sys/kernel.h>
90 #include <sys/bus.h>
91 #include <sys/linker_set.h>
92 #include <sys/module.h>
93 #include <sys/lock.h>
94 #include <sys/mutex.h>
95 #include <sys/condvar.h>
96 #include <sys/sysctl.h>
97 #include <sys/sx.h>
98 #include <sys/unistd.h>
99 #include <sys/callout.h>
100 #include <sys/malloc.h>
101 #include <sys/priv.h>
102 
103 #include <dev/usb/usb.h>
104 #include <dev/usb/usbdi.h>
105 #include <dev/usb/usbdi_util.h>
106 #include <dev/usb/usbhid.h>
107 #include <dev/usb/usb_cdc.h>
108 #include "usbdevs.h"
109 
110 #include <dev/usb/usb_ioctl.h>
111 
112 #define	USB_DEBUG_VAR umodem_debug
113 #include <dev/usb/usb_debug.h>
114 #include <dev/usb/usb_process.h>
115 #include <dev/usb/quirk/usb_quirk.h>
116 
117 #include <dev/usb/serial/usb_serial.h>
118 
119 #if USB_DEBUG
120 static int umodem_debug = 0;
121 
122 SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
123 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW,
124     &umodem_debug, 0, "Debug level");
125 #endif
126 
127 static const struct usb_device_id umodem_devs[] = {
128 	/* Generic Modem class match */
129 	{USB_IFACE_CLASS(UICLASS_CDC),
130 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
131 	USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
132 	/* Kyocera AH-K3001V */
133 	{USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
134 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
135 	{USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
136 };
137 
138 /*
139  * As speeds for umodem deivces increase, these numbers will need to
140  * be increased. They should be good for G3 speeds and below.
141  *
142  * TODO: The TTY buffers should be increased!
143  */
144 #define	UMODEM_BUF_SIZE 1024
145 
146 enum {
147 	UMODEM_BULK_WR,
148 	UMODEM_BULK_RD,
149 	UMODEM_INTR_RD,
150 	UMODEM_N_TRANSFER,
151 };
152 
153 #define	UMODEM_MODVER			1	/* module version */
154 
155 struct umodem_softc {
156 	struct ucom_super_softc sc_super_ucom;
157 	struct ucom_softc sc_ucom;
158 
159 	struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER];
160 	struct usb_device *sc_udev;
161 	struct mtx sc_mtx;
162 
163 	uint16_t sc_line;
164 
165 	uint8_t	sc_lsr;			/* local status register */
166 	uint8_t	sc_msr;			/* modem status register */
167 	uint8_t	sc_ctrl_iface_no;
168 	uint8_t	sc_data_iface_no;
169 	uint8_t sc_iface_index[2];
170 	uint8_t	sc_cm_over_data;
171 	uint8_t	sc_cm_cap;		/* CM capabilities */
172 	uint8_t	sc_acm_cap;		/* ACM capabilities */
173 };
174 
175 static device_probe_t umodem_probe;
176 static device_attach_t umodem_attach;
177 static device_detach_t umodem_detach;
178 
179 static usb_callback_t umodem_intr_callback;
180 static usb_callback_t umodem_write_callback;
181 static usb_callback_t umodem_read_callback;
182 
183 static void	umodem_start_read(struct ucom_softc *);
184 static void	umodem_stop_read(struct ucom_softc *);
185 static void	umodem_start_write(struct ucom_softc *);
186 static void	umodem_stop_write(struct ucom_softc *);
187 static void	umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *);
188 static void	umodem_cfg_get_status(struct ucom_softc *, uint8_t *,
189 		    uint8_t *);
190 static int	umodem_pre_param(struct ucom_softc *, struct termios *);
191 static void	umodem_cfg_param(struct ucom_softc *, struct termios *);
192 static int	umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
193 		    struct thread *);
194 static void	umodem_cfg_set_dtr(struct ucom_softc *, uint8_t);
195 static void	umodem_cfg_set_rts(struct ucom_softc *, uint8_t);
196 static void	umodem_cfg_set_break(struct ucom_softc *, uint8_t);
197 static void	*umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t);
198 static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t,
199 		    uint16_t, uint16_t);
200 static void	umodem_poll(struct ucom_softc *ucom);
201 
202 static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
203 
204 	[UMODEM_BULK_WR] = {
205 		.type = UE_BULK,
206 		.endpoint = UE_ADDR_ANY,
207 		.direction = UE_DIR_OUT,
208 		.if_index = 0,
209 		.bufsize = UMODEM_BUF_SIZE,
210 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
211 		.callback = &umodem_write_callback,
212 	},
213 
214 	[UMODEM_BULK_RD] = {
215 		.type = UE_BULK,
216 		.endpoint = UE_ADDR_ANY,
217 		.direction = UE_DIR_IN,
218 		.if_index = 0,
219 		.bufsize = UMODEM_BUF_SIZE,
220 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
221 		.callback = &umodem_read_callback,
222 	},
223 
224 	[UMODEM_INTR_RD] = {
225 		.type = UE_INTERRUPT,
226 		.endpoint = UE_ADDR_ANY,
227 		.direction = UE_DIR_IN,
228 		.if_index = 1,
229 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
230 		.bufsize = 0,	/* use wMaxPacketSize */
231 		.callback = &umodem_intr_callback,
232 	},
233 };
234 
235 static const struct ucom_callback umodem_callback = {
236 	.ucom_cfg_get_status = &umodem_cfg_get_status,
237 	.ucom_cfg_set_dtr = &umodem_cfg_set_dtr,
238 	.ucom_cfg_set_rts = &umodem_cfg_set_rts,
239 	.ucom_cfg_set_break = &umodem_cfg_set_break,
240 	.ucom_cfg_param = &umodem_cfg_param,
241 	.ucom_pre_param = &umodem_pre_param,
242 	.ucom_ioctl = &umodem_ioctl,
243 	.ucom_start_read = &umodem_start_read,
244 	.ucom_stop_read = &umodem_stop_read,
245 	.ucom_start_write = &umodem_start_write,
246 	.ucom_stop_write = &umodem_stop_write,
247 	.ucom_poll = &umodem_poll,
248 };
249 
250 static device_method_t umodem_methods[] = {
251 	DEVMETHOD(device_probe, umodem_probe),
252 	DEVMETHOD(device_attach, umodem_attach),
253 	DEVMETHOD(device_detach, umodem_detach),
254 	{0, 0}
255 };
256 
257 static devclass_t umodem_devclass;
258 
259 static driver_t umodem_driver = {
260 	.name = "umodem",
261 	.methods = umodem_methods,
262 	.size = sizeof(struct umodem_softc),
263 };
264 
265 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0);
266 MODULE_DEPEND(umodem, ucom, 1, 1, 1);
267 MODULE_DEPEND(umodem, usb, 1, 1, 1);
268 MODULE_VERSION(umodem, UMODEM_MODVER);
269 
270 static int
271 umodem_probe(device_t dev)
272 {
273 	struct usb_attach_arg *uaa = device_get_ivars(dev);
274 	int error;
275 
276 	DPRINTFN(11, "\n");
277 
278 	if (uaa->usb_mode != USB_MODE_HOST) {
279 		return (ENXIO);
280 	}
281 	error = usbd_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
282 	return (error);
283 }
284 
285 static int
286 umodem_attach(device_t dev)
287 {
288 	struct usb_attach_arg *uaa = device_get_ivars(dev);
289 	struct umodem_softc *sc = device_get_softc(dev);
290 	struct usb_cdc_cm_descriptor *cmd;
291 	struct usb_cdc_union_descriptor *cud;
292 	uint8_t i;
293 	int error;
294 
295 	device_set_usb_desc(dev);
296 	mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF);
297 
298 	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
299 	sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
300 	sc->sc_udev = uaa->device;
301 
302 	umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
303 
304 	/* get the data interface number */
305 
306 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
307 
308 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
309 
310 		cud = usbd_find_descriptor(uaa->device, NULL,
311 		    uaa->info.bIfaceIndex, UDESC_CS_INTERFACE,
312 		    0 - 1, UDESCSUB_CDC_UNION, 0 - 1);
313 
314 		if ((cud == NULL) || (cud->bLength < sizeof(*cud))) {
315 			device_printf(dev, "no CM or union descriptor\n");
316 			goto detach;
317 		}
318 
319 		sc->sc_data_iface_no = cud->bSlaveInterface[0];
320 	} else {
321 		sc->sc_data_iface_no = cmd->bDataInterface;
322 	}
323 
324 	device_printf(dev, "data interface %d, has %sCM over "
325 	    "data, has %sbreak\n",
326 	    sc->sc_data_iface_no,
327 	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
328 	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
329 
330 	/* get the data interface too */
331 
332 	for (i = 0;; i++) {
333 		struct usb_interface *iface;
334 		struct usb_interface_descriptor *id;
335 
336 		iface = usbd_get_iface(uaa->device, i);
337 
338 		if (iface) {
339 
340 			id = usbd_get_interface_descriptor(iface);
341 
342 			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
343 				sc->sc_iface_index[0] = i;
344 				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
345 				break;
346 			}
347 		} else {
348 			device_printf(dev, "no data interface\n");
349 			goto detach;
350 		}
351 	}
352 
353 	if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) {
354 		sc->sc_cm_over_data = 1;
355 	} else {
356 		if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
357 			if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
358 
359 				error = umodem_set_comm_feature
360 				(uaa->device, sc->sc_ctrl_iface_no,
361 				 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
362 
363 				/* ignore any errors */
364 			}
365 			sc->sc_cm_over_data = 1;
366 		}
367 	}
368 	error = usbd_transfer_setup(uaa->device,
369 	    sc->sc_iface_index, sc->sc_xfer,
370 	    umodem_config, UMODEM_N_TRANSFER,
371 	    sc, &sc->sc_mtx);
372 	if (error) {
373 		goto detach;
374 	}
375 
376 	/* clear stall at first run */
377 	mtx_lock(&sc->sc_mtx);
378 	usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
379 	usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
380 	mtx_unlock(&sc->sc_mtx);
381 
382 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
383 	    &umodem_callback, &sc->sc_mtx);
384 	if (error) {
385 		goto detach;
386 	}
387 	return (0);
388 
389 detach:
390 	umodem_detach(dev);
391 	return (ENXIO);
392 }
393 
394 static void
395 umodem_start_read(struct ucom_softc *ucom)
396 {
397 	struct umodem_softc *sc = ucom->sc_parent;
398 
399 	/* start interrupt endpoint, if any */
400 	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
401 
402 	/* start read endpoint */
403 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
404 }
405 
406 static void
407 umodem_stop_read(struct ucom_softc *ucom)
408 {
409 	struct umodem_softc *sc = ucom->sc_parent;
410 
411 	/* stop interrupt endpoint, if any */
412 	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
413 
414 	/* stop read endpoint */
415 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
416 }
417 
418 static void
419 umodem_start_write(struct ucom_softc *ucom)
420 {
421 	struct umodem_softc *sc = ucom->sc_parent;
422 
423 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
424 }
425 
426 static void
427 umodem_stop_write(struct ucom_softc *ucom)
428 {
429 	struct umodem_softc *sc = ucom->sc_parent;
430 
431 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
432 }
433 
434 static void
435 umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
436 {
437 	struct usb_cdc_cm_descriptor *cmd;
438 	struct usb_cdc_acm_descriptor *cad;
439 
440 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
441 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
442 		DPRINTF("no CM desc (faking one)\n");
443 		*cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA;
444 	} else
445 		*cm = cmd->bmCapabilities;
446 
447 	cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
448 	if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
449 		DPRINTF("no ACM desc\n");
450 		*acm = 0;
451 	} else
452 		*acm = cad->bmCapabilities;
453 }
454 
455 static void
456 umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
457 {
458 	struct umodem_softc *sc = ucom->sc_parent;
459 
460 	DPRINTF("\n");
461 
462 	*lsr = sc->sc_lsr;
463 	*msr = sc->sc_msr;
464 }
465 
466 static int
467 umodem_pre_param(struct ucom_softc *ucom, struct termios *t)
468 {
469 	return (0);			/* we accept anything */
470 }
471 
472 static void
473 umodem_cfg_param(struct ucom_softc *ucom, struct termios *t)
474 {
475 	struct umodem_softc *sc = ucom->sc_parent;
476 	struct usb_cdc_line_state ls;
477 	struct usb_device_request req;
478 
479 	DPRINTF("sc=%p\n", sc);
480 
481 	bzero(&ls, sizeof(ls));
482 
483 	USETDW(ls.dwDTERate, t->c_ospeed);
484 
485 	ls.bCharFormat = (t->c_cflag & CSTOPB) ?
486 	    UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
487 
488 	ls.bParityType = (t->c_cflag & PARENB) ?
489 	    ((t->c_cflag & PARODD) ?
490 	    UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
491 
492 	switch (t->c_cflag & CSIZE) {
493 	case CS5:
494 		ls.bDataBits = 5;
495 		break;
496 	case CS6:
497 		ls.bDataBits = 6;
498 		break;
499 	case CS7:
500 		ls.bDataBits = 7;
501 		break;
502 	case CS8:
503 		ls.bDataBits = 8;
504 		break;
505 	}
506 
507 	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
508 	    UGETDW(ls.dwDTERate), ls.bCharFormat,
509 	    ls.bParityType, ls.bDataBits);
510 
511 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
512 	req.bRequest = UCDC_SET_LINE_CODING;
513 	USETW(req.wValue, 0);
514 	req.wIndex[0] = sc->sc_ctrl_iface_no;
515 	req.wIndex[1] = 0;
516 	USETW(req.wLength, sizeof(ls));
517 
518 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
519 	    &req, &ls, 0, 1000);
520 }
521 
522 static int
523 umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
524     int flag, struct thread *td)
525 {
526 	struct umodem_softc *sc = ucom->sc_parent;
527 	int error = 0;
528 
529 	DPRINTF("cmd=0x%08x\n", cmd);
530 
531 	switch (cmd) {
532 	case USB_GET_CM_OVER_DATA:
533 		*(int *)data = sc->sc_cm_over_data;
534 		break;
535 
536 	case USB_SET_CM_OVER_DATA:
537 		if (*(int *)data != sc->sc_cm_over_data) {
538 			/* XXX change it */
539 		}
540 		break;
541 
542 	default:
543 		DPRINTF("unknown\n");
544 		error = ENOIOCTL;
545 		break;
546 	}
547 
548 	return (error);
549 }
550 
551 static void
552 umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
553 {
554 	struct umodem_softc *sc = ucom->sc_parent;
555 	struct usb_device_request req;
556 
557 	DPRINTF("onoff=%d\n", onoff);
558 
559 	if (onoff)
560 		sc->sc_line |= UCDC_LINE_DTR;
561 	else
562 		sc->sc_line &= ~UCDC_LINE_DTR;
563 
564 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
565 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
566 	USETW(req.wValue, sc->sc_line);
567 	req.wIndex[0] = sc->sc_ctrl_iface_no;
568 	req.wIndex[1] = 0;
569 	USETW(req.wLength, 0);
570 
571 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
572 	    &req, NULL, 0, 1000);
573 }
574 
575 static void
576 umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
577 {
578 	struct umodem_softc *sc = ucom->sc_parent;
579 	struct usb_device_request req;
580 
581 	DPRINTF("onoff=%d\n", onoff);
582 
583 	if (onoff)
584 		sc->sc_line |= UCDC_LINE_RTS;
585 	else
586 		sc->sc_line &= ~UCDC_LINE_RTS;
587 
588 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
589 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
590 	USETW(req.wValue, sc->sc_line);
591 	req.wIndex[0] = sc->sc_ctrl_iface_no;
592 	req.wIndex[1] = 0;
593 	USETW(req.wLength, 0);
594 
595 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
596 	    &req, NULL, 0, 1000);
597 }
598 
599 static void
600 umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
601 {
602 	struct umodem_softc *sc = ucom->sc_parent;
603 	struct usb_device_request req;
604 	uint16_t temp;
605 
606 	DPRINTF("onoff=%d\n", onoff);
607 
608 	if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
609 
610 		temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
611 
612 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
613 		req.bRequest = UCDC_SEND_BREAK;
614 		USETW(req.wValue, temp);
615 		req.wIndex[0] = sc->sc_ctrl_iface_no;
616 		req.wIndex[1] = 0;
617 		USETW(req.wLength, 0);
618 
619 		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
620 		    &req, NULL, 0, 1000);
621 	}
622 }
623 
624 static void
625 umodem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
626 {
627 	struct usb_cdc_notification pkt;
628 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
629 	struct usb_page_cache *pc;
630 	uint16_t wLen;
631 	int actlen;
632 
633 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
634 
635 	switch (USB_GET_STATE(xfer)) {
636 	case USB_ST_TRANSFERRED:
637 
638 		if (actlen < 8) {
639 			DPRINTF("received short packet, "
640 			    "%d bytes\n", actlen);
641 			goto tr_setup;
642 		}
643 		if (actlen > sizeof(pkt)) {
644 			DPRINTF("truncating message\n");
645 			actlen = sizeof(pkt);
646 		}
647 		pc = usbd_xfer_get_frame(xfer, 0);
648 		usbd_copy_out(pc, 0, &pkt, actlen);
649 
650 		actlen -= 8;
651 
652 		wLen = UGETW(pkt.wLength);
653 		if (actlen > wLen) {
654 			actlen = wLen;
655 		}
656 		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
657 			DPRINTF("unknown message type, "
658 			    "0x%02x, on notify pipe!\n",
659 			    pkt.bmRequestType);
660 			goto tr_setup;
661 		}
662 		switch (pkt.bNotification) {
663 		case UCDC_N_SERIAL_STATE:
664 			/*
665 			 * Set the serial state in ucom driver based on
666 			 * the bits from the notify message
667 			 */
668 			if (actlen < 2) {
669 				DPRINTF("invalid notification "
670 				    "length, %d bytes!\n", actlen);
671 				break;
672 			}
673 			DPRINTF("notify bytes = %02x%02x\n",
674 			    pkt.data[0],
675 			    pkt.data[1]);
676 
677 			/* Currently, lsr is always zero. */
678 			sc->sc_lsr = 0;
679 			sc->sc_msr = 0;
680 
681 			if (pkt.data[0] & UCDC_N_SERIAL_RI) {
682 				sc->sc_msr |= SER_RI;
683 			}
684 			if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
685 				sc->sc_msr |= SER_DSR;
686 			}
687 			if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
688 				sc->sc_msr |= SER_DCD;
689 			}
690 			ucom_status_change(&sc->sc_ucom);
691 			break;
692 
693 		default:
694 			DPRINTF("unknown notify message: 0x%02x\n",
695 			    pkt.bNotification);
696 			break;
697 		}
698 
699 	case USB_ST_SETUP:
700 tr_setup:
701 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
702 		usbd_transfer_submit(xfer);
703 		return;
704 
705 	default:			/* Error */
706 		if (error != USB_ERR_CANCELLED) {
707 			/* try to clear stall first */
708 			usbd_xfer_set_stall(xfer);
709 			goto tr_setup;
710 		}
711 		return;
712 
713 	}
714 }
715 
716 static void
717 umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
718 {
719 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
720 	struct usb_page_cache *pc;
721 	uint32_t actlen;
722 
723 	switch (USB_GET_STATE(xfer)) {
724 	case USB_ST_SETUP:
725 	case USB_ST_TRANSFERRED:
726 tr_setup:
727 		pc = usbd_xfer_get_frame(xfer, 0);
728 		if (ucom_get_data(&sc->sc_ucom, pc, 0,
729 		    UMODEM_BUF_SIZE, &actlen)) {
730 
731 			usbd_xfer_set_frame_len(xfer, 0, actlen);
732 			usbd_transfer_submit(xfer);
733 		}
734 		return;
735 
736 	default:			/* Error */
737 		if (error != USB_ERR_CANCELLED) {
738 			/* try to clear stall first */
739 			usbd_xfer_set_stall(xfer);
740 			goto tr_setup;
741 		}
742 		return;
743 	}
744 }
745 
746 static void
747 umodem_read_callback(struct usb_xfer *xfer, usb_error_t error)
748 {
749 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
750 	struct usb_page_cache *pc;
751 	int actlen;
752 
753 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
754 
755 	switch (USB_GET_STATE(xfer)) {
756 	case USB_ST_TRANSFERRED:
757 
758 		DPRINTF("actlen=%d\n", actlen);
759 
760 		pc = usbd_xfer_get_frame(xfer, 0);
761 		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
762 
763 	case USB_ST_SETUP:
764 tr_setup:
765 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
766 		usbd_transfer_submit(xfer);
767 		return;
768 
769 	default:			/* Error */
770 		if (error != USB_ERR_CANCELLED) {
771 			/* try to clear stall first */
772 			usbd_xfer_set_stall(xfer);
773 			goto tr_setup;
774 		}
775 		return;
776 	}
777 }
778 
779 static void *
780 umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype)
781 {
782 	return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
783 	    type, 0 - 1, subtype, 0 - 1));
784 }
785 
786 static usb_error_t
787 umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no,
788     uint16_t feature, uint16_t state)
789 {
790 	struct usb_device_request req;
791 	struct usb_cdc_abstract_state ast;
792 
793 	DPRINTF("feature=%d state=%d\n",
794 	    feature, state);
795 
796 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
797 	req.bRequest = UCDC_SET_COMM_FEATURE;
798 	USETW(req.wValue, feature);
799 	req.wIndex[0] = iface_no;
800 	req.wIndex[1] = 0;
801 	USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
802 	USETW(ast.wState, state);
803 
804 	return (usbd_do_request(udev, NULL, &req, &ast));
805 }
806 
807 static int
808 umodem_detach(device_t dev)
809 {
810 	struct umodem_softc *sc = device_get_softc(dev);
811 
812 	DPRINTF("sc=%p\n", sc);
813 
814 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
815 	usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
816 	mtx_destroy(&sc->sc_mtx);
817 
818 	return (0);
819 }
820 
821 static void
822 umodem_poll(struct ucom_softc *ucom)
823 {
824 	struct umodem_softc *sc = ucom->sc_parent;
825 	usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
826 }
827