xref: /freebsd/sys/dev/usb/serial/umodem.c (revision 0f27aaf940f2fa5a6540285537b33115a96161a4)
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/module.h>
92 #include <sys/lock.h>
93 #include <sys/mutex.h>
94 #include <sys/condvar.h>
95 #include <sys/sysctl.h>
96 #include <sys/sx.h>
97 #include <sys/unistd.h>
98 #include <sys/callout.h>
99 #include <sys/malloc.h>
100 #include <sys/priv.h>
101 
102 #include <dev/usb/usb.h>
103 #include <dev/usb/usbdi.h>
104 #include <dev/usb/usbdi_util.h>
105 #include <dev/usb/usbhid.h>
106 #include <dev/usb/usb_cdc.h>
107 #include "usbdevs.h"
108 
109 #include <dev/usb/usb_ioctl.h>
110 
111 #define	USB_DEBUG_VAR umodem_debug
112 #include <dev/usb/usb_debug.h>
113 #include <dev/usb/usb_process.h>
114 #include <dev/usb/quirk/usb_quirk.h>
115 
116 #include <dev/usb/serial/usb_serial.h>
117 
118 #ifdef USB_DEBUG
119 static int umodem_debug = 0;
120 
121 SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
122 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW,
123     &umodem_debug, 0, "Debug level");
124 #endif
125 
126 static const struct usb_device_id umodem_devs[] = {
127 	/* Generic Modem class match */
128 	{USB_IFACE_CLASS(UICLASS_CDC),
129 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
130 	USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
131 	/* Kyocera AH-K3001V */
132 	{USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
133 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
134 	{USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
135 };
136 
137 /*
138  * As speeds for umodem deivces increase, these numbers will need to
139  * be increased. They should be good for G3 speeds and below.
140  *
141  * TODO: The TTY buffers should be increased!
142  */
143 #define	UMODEM_BUF_SIZE 1024
144 
145 enum {
146 	UMODEM_BULK_WR,
147 	UMODEM_BULK_RD,
148 	UMODEM_INTR_RD,
149 	UMODEM_N_TRANSFER,
150 };
151 
152 #define	UMODEM_MODVER			1	/* module version */
153 
154 struct umodem_softc {
155 	struct ucom_super_softc sc_super_ucom;
156 	struct ucom_softc sc_ucom;
157 
158 	struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER];
159 	struct usb_device *sc_udev;
160 	struct mtx sc_mtx;
161 
162 	uint16_t sc_line;
163 
164 	uint8_t	sc_lsr;			/* local status register */
165 	uint8_t	sc_msr;			/* modem status register */
166 	uint8_t	sc_ctrl_iface_no;
167 	uint8_t	sc_data_iface_no;
168 	uint8_t sc_iface_index[2];
169 	uint8_t	sc_cm_over_data;
170 	uint8_t	sc_cm_cap;		/* CM capabilities */
171 	uint8_t	sc_acm_cap;		/* ACM capabilities */
172 };
173 
174 static device_probe_t umodem_probe;
175 static device_attach_t umodem_attach;
176 static device_detach_t umodem_detach;
177 
178 static usb_callback_t umodem_intr_callback;
179 static usb_callback_t umodem_write_callback;
180 static usb_callback_t umodem_read_callback;
181 
182 static void	umodem_start_read(struct ucom_softc *);
183 static void	umodem_stop_read(struct ucom_softc *);
184 static void	umodem_start_write(struct ucom_softc *);
185 static void	umodem_stop_write(struct ucom_softc *);
186 static void	umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *);
187 static void	umodem_cfg_get_status(struct ucom_softc *, uint8_t *,
188 		    uint8_t *);
189 static int	umodem_pre_param(struct ucom_softc *, struct termios *);
190 static void	umodem_cfg_param(struct ucom_softc *, struct termios *);
191 static int	umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
192 		    struct thread *);
193 static void	umodem_cfg_set_dtr(struct ucom_softc *, uint8_t);
194 static void	umodem_cfg_set_rts(struct ucom_softc *, uint8_t);
195 static void	umodem_cfg_set_break(struct ucom_softc *, uint8_t);
196 static void	*umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t);
197 static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t,
198 		    uint16_t, uint16_t);
199 static void	umodem_poll(struct ucom_softc *ucom);
200 
201 static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
202 
203 	[UMODEM_BULK_WR] = {
204 		.type = UE_BULK,
205 		.endpoint = UE_ADDR_ANY,
206 		.direction = UE_DIR_OUT,
207 		.if_index = 0,
208 		.bufsize = UMODEM_BUF_SIZE,
209 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
210 		.callback = &umodem_write_callback,
211 	},
212 
213 	[UMODEM_BULK_RD] = {
214 		.type = UE_BULK,
215 		.endpoint = UE_ADDR_ANY,
216 		.direction = UE_DIR_IN,
217 		.if_index = 0,
218 		.bufsize = UMODEM_BUF_SIZE,
219 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
220 		.callback = &umodem_read_callback,
221 	},
222 
223 	[UMODEM_INTR_RD] = {
224 		.type = UE_INTERRUPT,
225 		.endpoint = UE_ADDR_ANY,
226 		.direction = UE_DIR_IN,
227 		.if_index = 1,
228 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
229 		.bufsize = 0,	/* use wMaxPacketSize */
230 		.callback = &umodem_intr_callback,
231 	},
232 };
233 
234 static const struct ucom_callback umodem_callback = {
235 	.ucom_cfg_get_status = &umodem_cfg_get_status,
236 	.ucom_cfg_set_dtr = &umodem_cfg_set_dtr,
237 	.ucom_cfg_set_rts = &umodem_cfg_set_rts,
238 	.ucom_cfg_set_break = &umodem_cfg_set_break,
239 	.ucom_cfg_param = &umodem_cfg_param,
240 	.ucom_pre_param = &umodem_pre_param,
241 	.ucom_ioctl = &umodem_ioctl,
242 	.ucom_start_read = &umodem_start_read,
243 	.ucom_stop_read = &umodem_stop_read,
244 	.ucom_start_write = &umodem_start_write,
245 	.ucom_stop_write = &umodem_stop_write,
246 	.ucom_poll = &umodem_poll,
247 };
248 
249 static device_method_t umodem_methods[] = {
250 	DEVMETHOD(device_probe, umodem_probe),
251 	DEVMETHOD(device_attach, umodem_attach),
252 	DEVMETHOD(device_detach, umodem_detach),
253 	{0, 0}
254 };
255 
256 static devclass_t umodem_devclass;
257 
258 static driver_t umodem_driver = {
259 	.name = "umodem",
260 	.methods = umodem_methods,
261 	.size = sizeof(struct umodem_softc),
262 };
263 
264 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0);
265 MODULE_DEPEND(umodem, ucom, 1, 1, 1);
266 MODULE_DEPEND(umodem, usb, 1, 1, 1);
267 MODULE_VERSION(umodem, UMODEM_MODVER);
268 
269 static int
270 umodem_probe(device_t dev)
271 {
272 	struct usb_attach_arg *uaa = device_get_ivars(dev);
273 	int error;
274 
275 	DPRINTFN(11, "\n");
276 
277 	if (uaa->usb_mode != USB_MODE_HOST) {
278 		return (ENXIO);
279 	}
280 	error = usbd_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
281 	return (error);
282 }
283 
284 static int
285 umodem_attach(device_t dev)
286 {
287 	struct usb_attach_arg *uaa = device_get_ivars(dev);
288 	struct umodem_softc *sc = device_get_softc(dev);
289 	struct usb_cdc_cm_descriptor *cmd;
290 	struct usb_cdc_union_descriptor *cud;
291 	uint8_t i;
292 	int error;
293 
294 	device_set_usb_desc(dev);
295 	mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF);
296 
297 	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
298 	sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
299 	sc->sc_udev = uaa->device;
300 
301 	umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
302 
303 	/* get the data interface number */
304 
305 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
306 
307 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
308 
309 		cud = usbd_find_descriptor(uaa->device, NULL,
310 		    uaa->info.bIfaceIndex, UDESC_CS_INTERFACE,
311 		    0 - 1, UDESCSUB_CDC_UNION, 0 - 1);
312 
313 		if ((cud == NULL) || (cud->bLength < sizeof(*cud))) {
314 			device_printf(dev, "Missing descriptor. "
315 			    "Assuming data interface is next.\n");
316 			if (sc->sc_ctrl_iface_no == 0xFF)
317 				goto detach;
318 			else
319 				sc->sc_data_iface_no =
320 				    sc->sc_ctrl_iface_no + 1;
321 		} else {
322 			sc->sc_data_iface_no = cud->bSlaveInterface[0];
323 		}
324 	} else {
325 		sc->sc_data_iface_no = cmd->bDataInterface;
326 	}
327 
328 	device_printf(dev, "data interface %d, has %sCM over "
329 	    "data, has %sbreak\n",
330 	    sc->sc_data_iface_no,
331 	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
332 	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
333 
334 	/* get the data interface too */
335 
336 	for (i = 0;; i++) {
337 		struct usb_interface *iface;
338 		struct usb_interface_descriptor *id;
339 
340 		iface = usbd_get_iface(uaa->device, i);
341 
342 		if (iface) {
343 
344 			id = usbd_get_interface_descriptor(iface);
345 
346 			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
347 				sc->sc_iface_index[0] = i;
348 				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
349 				break;
350 			}
351 		} else {
352 			device_printf(dev, "no data interface\n");
353 			goto detach;
354 		}
355 	}
356 
357 	if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) {
358 		sc->sc_cm_over_data = 1;
359 	} else {
360 		if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
361 			if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
362 
363 				error = umodem_set_comm_feature
364 				(uaa->device, sc->sc_ctrl_iface_no,
365 				 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
366 
367 				/* ignore any errors */
368 			}
369 			sc->sc_cm_over_data = 1;
370 		}
371 	}
372 	error = usbd_transfer_setup(uaa->device,
373 	    sc->sc_iface_index, sc->sc_xfer,
374 	    umodem_config, UMODEM_N_TRANSFER,
375 	    sc, &sc->sc_mtx);
376 	if (error) {
377 		goto detach;
378 	}
379 
380 	/* clear stall at first run */
381 	mtx_lock(&sc->sc_mtx);
382 	usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
383 	usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
384 	mtx_unlock(&sc->sc_mtx);
385 
386 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
387 	    &umodem_callback, &sc->sc_mtx);
388 	if (error) {
389 		goto detach;
390 	}
391 	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
392 
393 	return (0);
394 
395 detach:
396 	umodem_detach(dev);
397 	return (ENXIO);
398 }
399 
400 static void
401 umodem_start_read(struct ucom_softc *ucom)
402 {
403 	struct umodem_softc *sc = ucom->sc_parent;
404 
405 	/* start interrupt endpoint, if any */
406 	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
407 
408 	/* start read endpoint */
409 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
410 }
411 
412 static void
413 umodem_stop_read(struct ucom_softc *ucom)
414 {
415 	struct umodem_softc *sc = ucom->sc_parent;
416 
417 	/* stop interrupt endpoint, if any */
418 	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
419 
420 	/* stop read endpoint */
421 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
422 }
423 
424 static void
425 umodem_start_write(struct ucom_softc *ucom)
426 {
427 	struct umodem_softc *sc = ucom->sc_parent;
428 
429 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
430 }
431 
432 static void
433 umodem_stop_write(struct ucom_softc *ucom)
434 {
435 	struct umodem_softc *sc = ucom->sc_parent;
436 
437 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
438 }
439 
440 static void
441 umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
442 {
443 	struct usb_cdc_cm_descriptor *cmd;
444 	struct usb_cdc_acm_descriptor *cad;
445 
446 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
447 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
448 		DPRINTF("no CM desc (faking one)\n");
449 		*cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA;
450 	} else
451 		*cm = cmd->bmCapabilities;
452 
453 	cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
454 	if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
455 		DPRINTF("no ACM desc\n");
456 		*acm = 0;
457 	} else
458 		*acm = cad->bmCapabilities;
459 }
460 
461 static void
462 umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
463 {
464 	struct umodem_softc *sc = ucom->sc_parent;
465 
466 	DPRINTF("\n");
467 
468 	*lsr = sc->sc_lsr;
469 	*msr = sc->sc_msr;
470 }
471 
472 static int
473 umodem_pre_param(struct ucom_softc *ucom, struct termios *t)
474 {
475 	return (0);			/* we accept anything */
476 }
477 
478 static void
479 umodem_cfg_param(struct ucom_softc *ucom, struct termios *t)
480 {
481 	struct umodem_softc *sc = ucom->sc_parent;
482 	struct usb_cdc_line_state ls;
483 	struct usb_device_request req;
484 
485 	DPRINTF("sc=%p\n", sc);
486 
487 	bzero(&ls, sizeof(ls));
488 
489 	USETDW(ls.dwDTERate, t->c_ospeed);
490 
491 	ls.bCharFormat = (t->c_cflag & CSTOPB) ?
492 	    UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
493 
494 	ls.bParityType = (t->c_cflag & PARENB) ?
495 	    ((t->c_cflag & PARODD) ?
496 	    UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
497 
498 	switch (t->c_cflag & CSIZE) {
499 	case CS5:
500 		ls.bDataBits = 5;
501 		break;
502 	case CS6:
503 		ls.bDataBits = 6;
504 		break;
505 	case CS7:
506 		ls.bDataBits = 7;
507 		break;
508 	case CS8:
509 		ls.bDataBits = 8;
510 		break;
511 	}
512 
513 	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
514 	    UGETDW(ls.dwDTERate), ls.bCharFormat,
515 	    ls.bParityType, ls.bDataBits);
516 
517 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
518 	req.bRequest = UCDC_SET_LINE_CODING;
519 	USETW(req.wValue, 0);
520 	req.wIndex[0] = sc->sc_ctrl_iface_no;
521 	req.wIndex[1] = 0;
522 	USETW(req.wLength, sizeof(ls));
523 
524 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
525 	    &req, &ls, 0, 1000);
526 }
527 
528 static int
529 umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
530     int flag, struct thread *td)
531 {
532 	struct umodem_softc *sc = ucom->sc_parent;
533 	int error = 0;
534 
535 	DPRINTF("cmd=0x%08x\n", cmd);
536 
537 	switch (cmd) {
538 	case USB_GET_CM_OVER_DATA:
539 		*(int *)data = sc->sc_cm_over_data;
540 		break;
541 
542 	case USB_SET_CM_OVER_DATA:
543 		if (*(int *)data != sc->sc_cm_over_data) {
544 			/* XXX change it */
545 		}
546 		break;
547 
548 	default:
549 		DPRINTF("unknown\n");
550 		error = ENOIOCTL;
551 		break;
552 	}
553 
554 	return (error);
555 }
556 
557 static void
558 umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
559 {
560 	struct umodem_softc *sc = ucom->sc_parent;
561 	struct usb_device_request req;
562 
563 	DPRINTF("onoff=%d\n", onoff);
564 
565 	if (onoff)
566 		sc->sc_line |= UCDC_LINE_DTR;
567 	else
568 		sc->sc_line &= ~UCDC_LINE_DTR;
569 
570 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
571 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
572 	USETW(req.wValue, sc->sc_line);
573 	req.wIndex[0] = sc->sc_ctrl_iface_no;
574 	req.wIndex[1] = 0;
575 	USETW(req.wLength, 0);
576 
577 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
578 	    &req, NULL, 0, 1000);
579 }
580 
581 static void
582 umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
583 {
584 	struct umodem_softc *sc = ucom->sc_parent;
585 	struct usb_device_request req;
586 
587 	DPRINTF("onoff=%d\n", onoff);
588 
589 	if (onoff)
590 		sc->sc_line |= UCDC_LINE_RTS;
591 	else
592 		sc->sc_line &= ~UCDC_LINE_RTS;
593 
594 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
595 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
596 	USETW(req.wValue, sc->sc_line);
597 	req.wIndex[0] = sc->sc_ctrl_iface_no;
598 	req.wIndex[1] = 0;
599 	USETW(req.wLength, 0);
600 
601 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
602 	    &req, NULL, 0, 1000);
603 }
604 
605 static void
606 umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
607 {
608 	struct umodem_softc *sc = ucom->sc_parent;
609 	struct usb_device_request req;
610 	uint16_t temp;
611 
612 	DPRINTF("onoff=%d\n", onoff);
613 
614 	if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
615 
616 		temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
617 
618 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
619 		req.bRequest = UCDC_SEND_BREAK;
620 		USETW(req.wValue, temp);
621 		req.wIndex[0] = sc->sc_ctrl_iface_no;
622 		req.wIndex[1] = 0;
623 		USETW(req.wLength, 0);
624 
625 		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
626 		    &req, NULL, 0, 1000);
627 	}
628 }
629 
630 static void
631 umodem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
632 {
633 	struct usb_cdc_notification pkt;
634 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
635 	struct usb_page_cache *pc;
636 	uint16_t wLen;
637 	int actlen;
638 
639 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
640 
641 	switch (USB_GET_STATE(xfer)) {
642 	case USB_ST_TRANSFERRED:
643 
644 		if (actlen < 8) {
645 			DPRINTF("received short packet, "
646 			    "%d bytes\n", actlen);
647 			goto tr_setup;
648 		}
649 		if (actlen > sizeof(pkt)) {
650 			DPRINTF("truncating message\n");
651 			actlen = sizeof(pkt);
652 		}
653 		pc = usbd_xfer_get_frame(xfer, 0);
654 		usbd_copy_out(pc, 0, &pkt, actlen);
655 
656 		actlen -= 8;
657 
658 		wLen = UGETW(pkt.wLength);
659 		if (actlen > wLen) {
660 			actlen = wLen;
661 		}
662 		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
663 			DPRINTF("unknown message type, "
664 			    "0x%02x, on notify pipe!\n",
665 			    pkt.bmRequestType);
666 			goto tr_setup;
667 		}
668 		switch (pkt.bNotification) {
669 		case UCDC_N_SERIAL_STATE:
670 			/*
671 			 * Set the serial state in ucom driver based on
672 			 * the bits from the notify message
673 			 */
674 			if (actlen < 2) {
675 				DPRINTF("invalid notification "
676 				    "length, %d bytes!\n", actlen);
677 				break;
678 			}
679 			DPRINTF("notify bytes = %02x%02x\n",
680 			    pkt.data[0],
681 			    pkt.data[1]);
682 
683 			/* Currently, lsr is always zero. */
684 			sc->sc_lsr = 0;
685 			sc->sc_msr = 0;
686 
687 			if (pkt.data[0] & UCDC_N_SERIAL_RI) {
688 				sc->sc_msr |= SER_RI;
689 			}
690 			if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
691 				sc->sc_msr |= SER_DSR;
692 			}
693 			if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
694 				sc->sc_msr |= SER_DCD;
695 			}
696 			ucom_status_change(&sc->sc_ucom);
697 			break;
698 
699 		default:
700 			DPRINTF("unknown notify message: 0x%02x\n",
701 			    pkt.bNotification);
702 			break;
703 		}
704 
705 	case USB_ST_SETUP:
706 tr_setup:
707 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
708 		usbd_transfer_submit(xfer);
709 		return;
710 
711 	default:			/* Error */
712 		if (error != USB_ERR_CANCELLED) {
713 			/* try to clear stall first */
714 			usbd_xfer_set_stall(xfer);
715 			goto tr_setup;
716 		}
717 		return;
718 
719 	}
720 }
721 
722 static void
723 umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
724 {
725 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
726 	struct usb_page_cache *pc;
727 	uint32_t actlen;
728 
729 	switch (USB_GET_STATE(xfer)) {
730 	case USB_ST_SETUP:
731 	case USB_ST_TRANSFERRED:
732 tr_setup:
733 		pc = usbd_xfer_get_frame(xfer, 0);
734 		if (ucom_get_data(&sc->sc_ucom, pc, 0,
735 		    UMODEM_BUF_SIZE, &actlen)) {
736 
737 			usbd_xfer_set_frame_len(xfer, 0, actlen);
738 			usbd_transfer_submit(xfer);
739 		}
740 		return;
741 
742 	default:			/* Error */
743 		if (error != USB_ERR_CANCELLED) {
744 			/* try to clear stall first */
745 			usbd_xfer_set_stall(xfer);
746 			goto tr_setup;
747 		}
748 		return;
749 	}
750 }
751 
752 static void
753 umodem_read_callback(struct usb_xfer *xfer, usb_error_t error)
754 {
755 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
756 	struct usb_page_cache *pc;
757 	int actlen;
758 
759 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
760 
761 	switch (USB_GET_STATE(xfer)) {
762 	case USB_ST_TRANSFERRED:
763 
764 		DPRINTF("actlen=%d\n", actlen);
765 
766 		pc = usbd_xfer_get_frame(xfer, 0);
767 		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
768 
769 	case USB_ST_SETUP:
770 tr_setup:
771 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
772 		usbd_transfer_submit(xfer);
773 		return;
774 
775 	default:			/* Error */
776 		if (error != USB_ERR_CANCELLED) {
777 			/* try to clear stall first */
778 			usbd_xfer_set_stall(xfer);
779 			goto tr_setup;
780 		}
781 		return;
782 	}
783 }
784 
785 static void *
786 umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype)
787 {
788 	return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
789 	    type, 0 - 1, subtype, 0 - 1));
790 }
791 
792 static usb_error_t
793 umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no,
794     uint16_t feature, uint16_t state)
795 {
796 	struct usb_device_request req;
797 	struct usb_cdc_abstract_state ast;
798 
799 	DPRINTF("feature=%d state=%d\n",
800 	    feature, state);
801 
802 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
803 	req.bRequest = UCDC_SET_COMM_FEATURE;
804 	USETW(req.wValue, feature);
805 	req.wIndex[0] = iface_no;
806 	req.wIndex[1] = 0;
807 	USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
808 	USETW(ast.wState, state);
809 
810 	return (usbd_do_request(udev, NULL, &req, &ast));
811 }
812 
813 static int
814 umodem_detach(device_t dev)
815 {
816 	struct umodem_softc *sc = device_get_softc(dev);
817 
818 	DPRINTF("sc=%p\n", sc);
819 
820 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
821 	usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
822 	mtx_destroy(&sc->sc_mtx);
823 
824 	return (0);
825 }
826 
827 static void
828 umodem_poll(struct ucom_softc *ucom)
829 {
830 	struct umodem_softc *sc = ucom->sc_parent;
831 	usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
832 }
833