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