xref: /freebsd/sys/dev/usb/serial/umodem.c (revision 2e5b60079b7d8c3ca68f1390cd90f305e651f8d3)
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  *
49  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59  * POSSIBILITY OF SUCH DAMAGE.
60  */
61 
62 /*
63  * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
64  *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
65  *                   http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
66  */
67 
68 /*
69  * TODO:
70  * - Add error recovery in various places; the big problem is what
71  *   to do in a callback if there is an error.
72  * - Implement a Call Device for modems without multiplexed commands.
73  *
74  */
75 
76 #include <sys/stdint.h>
77 #include <sys/stddef.h>
78 #include <sys/param.h>
79 #include <sys/queue.h>
80 #include <sys/types.h>
81 #include <sys/systm.h>
82 #include <sys/kernel.h>
83 #include <sys/bus.h>
84 #include <sys/module.h>
85 #include <sys/lock.h>
86 #include <sys/mutex.h>
87 #include <sys/condvar.h>
88 #include <sys/sysctl.h>
89 #include <sys/sx.h>
90 #include <sys/unistd.h>
91 #include <sys/callout.h>
92 #include <sys/malloc.h>
93 #include <sys/priv.h>
94 
95 #include <dev/usb/usb.h>
96 #include <dev/usb/usbdi.h>
97 #include <dev/usb/usbdi_util.h>
98 #include <dev/usb/usbhid.h>
99 #include <dev/usb/usb_cdc.h>
100 #include "usbdevs.h"
101 #include "usb_if.h"
102 
103 #include <dev/usb/usb_ioctl.h>
104 
105 #define	USB_DEBUG_VAR umodem_debug
106 #include <dev/usb/usb_debug.h>
107 #include <dev/usb/usb_process.h>
108 #include <dev/usb/quirk/usb_quirk.h>
109 
110 #include <dev/usb/serial/usb_serial.h>
111 
112 #ifdef USB_DEBUG
113 static int umodem_debug = 0;
114 
115 static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
116 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RWTUN,
117     &umodem_debug, 0, "Debug level");
118 #endif
119 
120 static const STRUCT_USB_DUAL_ID umodem_dual_devs[] = {
121 	/* Generic Modem class match */
122 	{USB_IFACE_CLASS(UICLASS_CDC),
123 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
124 		USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
125 	{USB_IFACE_CLASS(UICLASS_CDC),
126 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
127 		USB_IFACE_PROTOCOL(UIPROTO_CDC_NONE)},
128 };
129 
130 static const STRUCT_USB_HOST_ID umodem_host_devs[] = {
131 	/* Huawei Modem class match */
132 	{USB_VENDOR(USB_VENDOR_HUAWEI),USB_IFACE_CLASS(UICLASS_CDC),
133 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
134 		USB_IFACE_PROTOCOL(0xFF)},
135 	/* Kyocera AH-K3001V */
136 	{USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
137 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
138 	{USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
139 };
140 
141 /*
142  * As speeds for umodem devices increase, these numbers will need to
143  * be increased. They should be good for G3 speeds and below.
144  *
145  * TODO: The TTY buffers should be increased!
146  */
147 #define	UMODEM_BUF_SIZE 1024
148 
149 enum {
150 	UMODEM_BULK_WR,
151 	UMODEM_BULK_RD,
152 	UMODEM_INTR_WR,
153 	UMODEM_INTR_RD,
154 	UMODEM_N_TRANSFER,
155 };
156 
157 #define	UMODEM_MODVER			1	/* module version */
158 
159 struct umodem_softc {
160 	struct ucom_super_softc sc_super_ucom;
161 	struct ucom_softc sc_ucom;
162 
163 	struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER];
164 	struct usb_device *sc_udev;
165 	struct mtx sc_mtx;
166 
167 	uint16_t sc_line;
168 
169 	uint8_t	sc_lsr;			/* local status register */
170 	uint8_t	sc_msr;			/* modem status register */
171 	uint8_t	sc_ctrl_iface_no;
172 	uint8_t	sc_data_iface_no;
173 	uint8_t sc_iface_index[2];
174 	uint8_t	sc_cm_over_data;
175 	uint8_t	sc_cm_cap;		/* CM capabilities */
176 	uint8_t	sc_acm_cap;		/* ACM capabilities */
177 	uint8_t	sc_line_coding[32];	/* used in USB device mode */
178 	uint8_t	sc_abstract_state[32];	/* used in USB device mode */
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 usb_handle_request_t umodem_handle_request;
185 
186 static void umodem_free_softc(struct umodem_softc *);
187 
188 static usb_callback_t umodem_intr_read_callback;
189 static usb_callback_t umodem_intr_write_callback;
190 static usb_callback_t umodem_write_callback;
191 static usb_callback_t umodem_read_callback;
192 
193 static void	umodem_free(struct ucom_softc *);
194 static void	umodem_start_read(struct ucom_softc *);
195 static void	umodem_stop_read(struct ucom_softc *);
196 static void	umodem_start_write(struct ucom_softc *);
197 static void	umodem_stop_write(struct ucom_softc *);
198 static void	umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *);
199 static void	umodem_cfg_get_status(struct ucom_softc *, uint8_t *,
200 		    uint8_t *);
201 static int	umodem_pre_param(struct ucom_softc *, struct termios *);
202 static void	umodem_cfg_param(struct ucom_softc *, struct termios *);
203 static int	umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
204 		    struct thread *);
205 static void	umodem_cfg_set_dtr(struct ucom_softc *, uint8_t);
206 static void	umodem_cfg_set_rts(struct ucom_softc *, uint8_t);
207 static void	umodem_cfg_set_break(struct ucom_softc *, uint8_t);
208 static void	*umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t);
209 static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t,
210 		    uint16_t, uint16_t);
211 static void	umodem_poll(struct ucom_softc *ucom);
212 static void	umodem_find_data_iface(struct usb_attach_arg *uaa,
213 		    uint8_t, uint8_t *, uint8_t *);
214 
215 static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
216 
217 	[UMODEM_BULK_WR] = {
218 		.type = UE_BULK,
219 		.endpoint = UE_ADDR_ANY,
220 		.direction = UE_DIR_TX,
221 		.if_index = 0,
222 		.bufsize = UMODEM_BUF_SIZE,
223 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
224 		.callback = &umodem_write_callback,
225 		.usb_mode = USB_MODE_DUAL,
226 	},
227 
228 	[UMODEM_BULK_RD] = {
229 		.type = UE_BULK,
230 		.endpoint = UE_ADDR_ANY,
231 		.direction = UE_DIR_RX,
232 		.if_index = 0,
233 		.bufsize = UMODEM_BUF_SIZE,
234 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
235 		.callback = &umodem_read_callback,
236 		.usb_mode = USB_MODE_DUAL,
237 	},
238 
239 	[UMODEM_INTR_WR] = {
240 		.type = UE_INTERRUPT,
241 		.endpoint = UE_ADDR_ANY,
242 		.direction = UE_DIR_TX,
243 		.if_index = 1,
244 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
245 		.bufsize = 0,	/* use wMaxPacketSize */
246 		.callback = &umodem_intr_write_callback,
247 		.usb_mode = USB_MODE_DEVICE,
248 	},
249 
250 	[UMODEM_INTR_RD] = {
251 		.type = UE_INTERRUPT,
252 		.endpoint = UE_ADDR_ANY,
253 		.direction = UE_DIR_RX,
254 		.if_index = 1,
255 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
256 		.bufsize = 0,	/* use wMaxPacketSize */
257 		.callback = &umodem_intr_read_callback,
258 		.usb_mode = USB_MODE_HOST,
259 	},
260 };
261 
262 static const struct ucom_callback umodem_callback = {
263 	.ucom_cfg_get_status = &umodem_cfg_get_status,
264 	.ucom_cfg_set_dtr = &umodem_cfg_set_dtr,
265 	.ucom_cfg_set_rts = &umodem_cfg_set_rts,
266 	.ucom_cfg_set_break = &umodem_cfg_set_break,
267 	.ucom_cfg_param = &umodem_cfg_param,
268 	.ucom_pre_param = &umodem_pre_param,
269 	.ucom_ioctl = &umodem_ioctl,
270 	.ucom_start_read = &umodem_start_read,
271 	.ucom_stop_read = &umodem_stop_read,
272 	.ucom_start_write = &umodem_start_write,
273 	.ucom_stop_write = &umodem_stop_write,
274 	.ucom_poll = &umodem_poll,
275 	.ucom_free = &umodem_free,
276 };
277 
278 static device_method_t umodem_methods[] = {
279 	/* USB interface */
280 	DEVMETHOD(usb_handle_request, umodem_handle_request),
281 
282 	/* Device interface */
283 	DEVMETHOD(device_probe, umodem_probe),
284 	DEVMETHOD(device_attach, umodem_attach),
285 	DEVMETHOD(device_detach, umodem_detach),
286 	DEVMETHOD_END
287 };
288 
289 static devclass_t umodem_devclass;
290 
291 static driver_t umodem_driver = {
292 	.name = "umodem",
293 	.methods = umodem_methods,
294 	.size = sizeof(struct umodem_softc),
295 };
296 
297 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0);
298 MODULE_DEPEND(umodem, ucom, 1, 1, 1);
299 MODULE_DEPEND(umodem, usb, 1, 1, 1);
300 MODULE_VERSION(umodem, UMODEM_MODVER);
301 
302 static int
303 umodem_probe(device_t dev)
304 {
305 	struct usb_attach_arg *uaa = device_get_ivars(dev);
306 	int error;
307 
308 	DPRINTFN(11, "\n");
309 
310 	error = usbd_lookup_id_by_uaa(umodem_host_devs,
311 	    sizeof(umodem_host_devs), uaa);
312 	if (error) {
313 		error = usbd_lookup_id_by_uaa(umodem_dual_devs,
314 		    sizeof(umodem_dual_devs), uaa);
315 		if (error)
316 			return (error);
317 	}
318 	return (BUS_PROBE_GENERIC);
319 }
320 
321 static int
322 umodem_attach(device_t dev)
323 {
324 	struct usb_attach_arg *uaa = device_get_ivars(dev);
325 	struct umodem_softc *sc = device_get_softc(dev);
326 	struct usb_cdc_cm_descriptor *cmd;
327 	struct usb_cdc_union_descriptor *cud;
328 	uint8_t i;
329 	int error;
330 
331 	device_set_usb_desc(dev);
332 	mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF);
333 	ucom_ref(&sc->sc_super_ucom);
334 
335 	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
336 	sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
337 	sc->sc_udev = uaa->device;
338 
339 	umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
340 
341 	/* get the data interface number */
342 
343 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
344 
345 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
346 
347 		cud = usbd_find_descriptor(uaa->device, NULL,
348 		    uaa->info.bIfaceIndex, UDESC_CS_INTERFACE,
349 		    0xFF, UDESCSUB_CDC_UNION, 0xFF);
350 
351 		if ((cud == NULL) || (cud->bLength < sizeof(*cud))) {
352 			DPRINTF("Missing descriptor. "
353 			    "Assuming data interface is next.\n");
354 			if (sc->sc_ctrl_iface_no == 0xFF) {
355 				goto detach;
356 			} else {
357 				uint8_t class_match = 0;
358 
359 				/* set default interface number */
360 				sc->sc_data_iface_no = 0xFF;
361 
362 				/* try to find the data interface backwards */
363 				umodem_find_data_iface(uaa,
364 				    uaa->info.bIfaceIndex - 1,
365 				    &sc->sc_data_iface_no, &class_match);
366 
367 				/* try to find the data interface forwards */
368 				umodem_find_data_iface(uaa,
369 				    uaa->info.bIfaceIndex + 1,
370 				    &sc->sc_data_iface_no, &class_match);
371 
372 				/* check if nothing was found */
373 				if (sc->sc_data_iface_no == 0xFF)
374 					goto detach;
375 			}
376 		} else {
377 			sc->sc_data_iface_no = cud->bSlaveInterface[0];
378 		}
379 	} else {
380 		sc->sc_data_iface_no = cmd->bDataInterface;
381 	}
382 
383 	device_printf(dev, "data interface %d, has %sCM over "
384 	    "data, has %sbreak\n",
385 	    sc->sc_data_iface_no,
386 	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
387 	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
388 
389 	/* get the data interface too */
390 
391 	for (i = 0;; i++) {
392 		struct usb_interface *iface;
393 		struct usb_interface_descriptor *id;
394 
395 		iface = usbd_get_iface(uaa->device, i);
396 
397 		if (iface) {
398 
399 			id = usbd_get_interface_descriptor(iface);
400 
401 			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
402 				sc->sc_iface_index[0] = i;
403 				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
404 				break;
405 			}
406 		} else {
407 			device_printf(dev, "no data interface\n");
408 			goto detach;
409 		}
410 	}
411 
412 	if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) {
413 		sc->sc_cm_over_data = 1;
414 	} else {
415 		if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
416 			if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
417 
418 				error = umodem_set_comm_feature
419 				(uaa->device, sc->sc_ctrl_iface_no,
420 				 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
421 
422 				/* ignore any errors */
423 			}
424 			sc->sc_cm_over_data = 1;
425 		}
426 	}
427 	error = usbd_transfer_setup(uaa->device,
428 	    sc->sc_iface_index, sc->sc_xfer,
429 	    umodem_config, UMODEM_N_TRANSFER,
430 	    sc, &sc->sc_mtx);
431 	if (error) {
432 		device_printf(dev, "Can't setup transfer\n");
433 		goto detach;
434 	}
435 
436 	/* clear stall at first run, if USB host mode */
437 	if (uaa->usb_mode == USB_MODE_HOST) {
438 		mtx_lock(&sc->sc_mtx);
439 		usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
440 		usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
441 		mtx_unlock(&sc->sc_mtx);
442 	}
443 
444 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
445 	    &umodem_callback, &sc->sc_mtx);
446 	if (error) {
447 		device_printf(dev, "Can't attach com\n");
448 		goto detach;
449 	}
450 	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
451 
452 	return (0);
453 
454 detach:
455 	umodem_detach(dev);
456 	return (ENXIO);
457 }
458 
459 static void
460 umodem_find_data_iface(struct usb_attach_arg *uaa,
461     uint8_t iface_index, uint8_t *p_data_no, uint8_t *p_match_class)
462 {
463 	struct usb_interface_descriptor *id;
464 	struct usb_interface *iface;
465 
466 	iface = usbd_get_iface(uaa->device, iface_index);
467 
468 	/* check for end of interfaces */
469 	if (iface == NULL)
470 		return;
471 
472 	id = usbd_get_interface_descriptor(iface);
473 
474 	/* check for non-matching interface class */
475 	if (id->bInterfaceClass != UICLASS_CDC_DATA ||
476 	    id->bInterfaceSubClass != UISUBCLASS_DATA) {
477 		/* if we got a class match then return */
478 		if (*p_match_class)
479 			return;
480 	} else {
481 		*p_match_class = 1;
482 	}
483 
484 	DPRINTFN(11, "Match at index %u\n", iface_index);
485 
486 	*p_data_no = id->bInterfaceNumber;
487 }
488 
489 static void
490 umodem_start_read(struct ucom_softc *ucom)
491 {
492 	struct umodem_softc *sc = ucom->sc_parent;
493 
494 	/* start interrupt endpoint, if any */
495 	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
496 
497 	/* start read endpoint */
498 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
499 }
500 
501 static void
502 umodem_stop_read(struct ucom_softc *ucom)
503 {
504 	struct umodem_softc *sc = ucom->sc_parent;
505 
506 	/* stop interrupt endpoint, if any */
507 	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
508 
509 	/* stop read endpoint */
510 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
511 }
512 
513 static void
514 umodem_start_write(struct ucom_softc *ucom)
515 {
516 	struct umodem_softc *sc = ucom->sc_parent;
517 
518 	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_WR]);
519 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
520 }
521 
522 static void
523 umodem_stop_write(struct ucom_softc *ucom)
524 {
525 	struct umodem_softc *sc = ucom->sc_parent;
526 
527 	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_WR]);
528 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
529 }
530 
531 static void
532 umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
533 {
534 	struct usb_cdc_cm_descriptor *cmd;
535 	struct usb_cdc_acm_descriptor *cad;
536 
537 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
538 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
539 		DPRINTF("no CM desc (faking one)\n");
540 		*cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA;
541 	} else
542 		*cm = cmd->bmCapabilities;
543 
544 	cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
545 	if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
546 		DPRINTF("no ACM desc\n");
547 		*acm = 0;
548 	} else
549 		*acm = cad->bmCapabilities;
550 }
551 
552 static void
553 umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
554 {
555 	struct umodem_softc *sc = ucom->sc_parent;
556 
557 	DPRINTF("\n");
558 
559 	*lsr = sc->sc_lsr;
560 	*msr = sc->sc_msr;
561 }
562 
563 static int
564 umodem_pre_param(struct ucom_softc *ucom, struct termios *t)
565 {
566 	return (0);			/* we accept anything */
567 }
568 
569 static void
570 umodem_cfg_param(struct ucom_softc *ucom, struct termios *t)
571 {
572 	struct umodem_softc *sc = ucom->sc_parent;
573 	struct usb_cdc_line_state ls;
574 	struct usb_device_request req;
575 
576 	DPRINTF("sc=%p\n", sc);
577 
578 	memset(&ls, 0, sizeof(ls));
579 
580 	USETDW(ls.dwDTERate, t->c_ospeed);
581 
582 	ls.bCharFormat = (t->c_cflag & CSTOPB) ?
583 	    UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
584 
585 	ls.bParityType = (t->c_cflag & PARENB) ?
586 	    ((t->c_cflag & PARODD) ?
587 	    UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
588 
589 	switch (t->c_cflag & CSIZE) {
590 	case CS5:
591 		ls.bDataBits = 5;
592 		break;
593 	case CS6:
594 		ls.bDataBits = 6;
595 		break;
596 	case CS7:
597 		ls.bDataBits = 7;
598 		break;
599 	case CS8:
600 		ls.bDataBits = 8;
601 		break;
602 	}
603 
604 	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
605 	    UGETDW(ls.dwDTERate), ls.bCharFormat,
606 	    ls.bParityType, ls.bDataBits);
607 
608 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
609 	req.bRequest = UCDC_SET_LINE_CODING;
610 	USETW(req.wValue, 0);
611 	req.wIndex[0] = sc->sc_ctrl_iface_no;
612 	req.wIndex[1] = 0;
613 	USETW(req.wLength, sizeof(ls));
614 
615 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
616 	    &req, &ls, 0, 1000);
617 }
618 
619 static int
620 umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
621     int flag, struct thread *td)
622 {
623 	struct umodem_softc *sc = ucom->sc_parent;
624 	int error = 0;
625 
626 	DPRINTF("cmd=0x%08x\n", cmd);
627 
628 	switch (cmd) {
629 	case USB_GET_CM_OVER_DATA:
630 		*(int *)data = sc->sc_cm_over_data;
631 		break;
632 
633 	case USB_SET_CM_OVER_DATA:
634 		if (*(int *)data != sc->sc_cm_over_data) {
635 			/* XXX change it */
636 		}
637 		break;
638 
639 	default:
640 		DPRINTF("unknown\n");
641 		error = ENOIOCTL;
642 		break;
643 	}
644 
645 	return (error);
646 }
647 
648 static void
649 umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
650 {
651 	struct umodem_softc *sc = ucom->sc_parent;
652 	struct usb_device_request req;
653 
654 	DPRINTF("onoff=%d\n", onoff);
655 
656 	if (onoff)
657 		sc->sc_line |= UCDC_LINE_DTR;
658 	else
659 		sc->sc_line &= ~UCDC_LINE_DTR;
660 
661 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
662 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
663 	USETW(req.wValue, sc->sc_line);
664 	req.wIndex[0] = sc->sc_ctrl_iface_no;
665 	req.wIndex[1] = 0;
666 	USETW(req.wLength, 0);
667 
668 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
669 	    &req, NULL, 0, 1000);
670 }
671 
672 static void
673 umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
674 {
675 	struct umodem_softc *sc = ucom->sc_parent;
676 	struct usb_device_request req;
677 
678 	DPRINTF("onoff=%d\n", onoff);
679 
680 	if (onoff)
681 		sc->sc_line |= UCDC_LINE_RTS;
682 	else
683 		sc->sc_line &= ~UCDC_LINE_RTS;
684 
685 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
686 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
687 	USETW(req.wValue, sc->sc_line);
688 	req.wIndex[0] = sc->sc_ctrl_iface_no;
689 	req.wIndex[1] = 0;
690 	USETW(req.wLength, 0);
691 
692 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
693 	    &req, NULL, 0, 1000);
694 }
695 
696 static void
697 umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
698 {
699 	struct umodem_softc *sc = ucom->sc_parent;
700 	struct usb_device_request req;
701 	uint16_t temp;
702 
703 	DPRINTF("onoff=%d\n", onoff);
704 
705 	if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
706 
707 		temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
708 
709 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
710 		req.bRequest = UCDC_SEND_BREAK;
711 		USETW(req.wValue, temp);
712 		req.wIndex[0] = sc->sc_ctrl_iface_no;
713 		req.wIndex[1] = 0;
714 		USETW(req.wLength, 0);
715 
716 		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
717 		    &req, NULL, 0, 1000);
718 	}
719 }
720 
721 static void
722 umodem_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
723 {
724 	int actlen;
725 
726 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
727 
728 	switch (USB_GET_STATE(xfer)) {
729 	case USB_ST_TRANSFERRED:
730 
731 		DPRINTF("Transferred %d bytes\n", actlen);
732 
733 		/* FALLTHROUGH */
734 	case USB_ST_SETUP:
735 tr_setup:
736 		break;
737 
738 	default:			/* Error */
739 		if (error != USB_ERR_CANCELLED) {
740 			/* start clear stall */
741 			usbd_xfer_set_stall(xfer);
742 			goto tr_setup;
743 		}
744 		break;
745 	}
746 }
747 
748 static void
749 umodem_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
750 {
751 	struct usb_cdc_notification pkt;
752 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
753 	struct usb_page_cache *pc;
754 	uint16_t wLen;
755 	int actlen;
756 
757 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
758 
759 	switch (USB_GET_STATE(xfer)) {
760 	case USB_ST_TRANSFERRED:
761 
762 		if (actlen < 8) {
763 			DPRINTF("received short packet, "
764 			    "%d bytes\n", actlen);
765 			goto tr_setup;
766 		}
767 		if (actlen > (int)sizeof(pkt)) {
768 			DPRINTF("truncating message\n");
769 			actlen = sizeof(pkt);
770 		}
771 		pc = usbd_xfer_get_frame(xfer, 0);
772 		usbd_copy_out(pc, 0, &pkt, actlen);
773 
774 		actlen -= 8;
775 
776 		wLen = UGETW(pkt.wLength);
777 		if (actlen > wLen) {
778 			actlen = wLen;
779 		}
780 		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
781 			DPRINTF("unknown message type, "
782 			    "0x%02x, on notify pipe!\n",
783 			    pkt.bmRequestType);
784 			goto tr_setup;
785 		}
786 		switch (pkt.bNotification) {
787 		case UCDC_N_SERIAL_STATE:
788 			/*
789 			 * Set the serial state in ucom driver based on
790 			 * the bits from the notify message
791 			 */
792 			if (actlen < 2) {
793 				DPRINTF("invalid notification "
794 				    "length, %d bytes!\n", actlen);
795 				break;
796 			}
797 			DPRINTF("notify bytes = %02x%02x\n",
798 			    pkt.data[0],
799 			    pkt.data[1]);
800 
801 			/* Currently, lsr is always zero. */
802 			sc->sc_lsr = 0;
803 			sc->sc_msr = 0;
804 
805 			if (pkt.data[0] & UCDC_N_SERIAL_RI) {
806 				sc->sc_msr |= SER_RI;
807 			}
808 			if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
809 				sc->sc_msr |= SER_DSR;
810 			}
811 			if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
812 				sc->sc_msr |= SER_DCD;
813 			}
814 			ucom_status_change(&sc->sc_ucom);
815 			break;
816 
817 		default:
818 			DPRINTF("unknown notify message: 0x%02x\n",
819 			    pkt.bNotification);
820 			break;
821 		}
822 
823 	case USB_ST_SETUP:
824 tr_setup:
825 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
826 		usbd_transfer_submit(xfer);
827 		return;
828 
829 	default:			/* Error */
830 		if (error != USB_ERR_CANCELLED) {
831 			/* try to clear stall first */
832 			usbd_xfer_set_stall(xfer);
833 			goto tr_setup;
834 		}
835 		return;
836 
837 	}
838 }
839 
840 static void
841 umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
842 {
843 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
844 	struct usb_page_cache *pc;
845 	uint32_t actlen;
846 
847 	switch (USB_GET_STATE(xfer)) {
848 	case USB_ST_SETUP:
849 	case USB_ST_TRANSFERRED:
850 tr_setup:
851 		pc = usbd_xfer_get_frame(xfer, 0);
852 		if (ucom_get_data(&sc->sc_ucom, pc, 0,
853 		    UMODEM_BUF_SIZE, &actlen)) {
854 
855 			usbd_xfer_set_frame_len(xfer, 0, actlen);
856 			usbd_transfer_submit(xfer);
857 		}
858 		return;
859 
860 	default:			/* Error */
861 		if (error != USB_ERR_CANCELLED) {
862 			/* try to clear stall first */
863 			usbd_xfer_set_stall(xfer);
864 			goto tr_setup;
865 		}
866 		return;
867 	}
868 }
869 
870 static void
871 umodem_read_callback(struct usb_xfer *xfer, usb_error_t error)
872 {
873 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
874 	struct usb_page_cache *pc;
875 	int actlen;
876 
877 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
878 
879 	switch (USB_GET_STATE(xfer)) {
880 	case USB_ST_TRANSFERRED:
881 
882 		DPRINTF("actlen=%d\n", actlen);
883 
884 		pc = usbd_xfer_get_frame(xfer, 0);
885 		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
886 
887 	case USB_ST_SETUP:
888 tr_setup:
889 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
890 		usbd_transfer_submit(xfer);
891 		return;
892 
893 	default:			/* Error */
894 		if (error != USB_ERR_CANCELLED) {
895 			/* try to clear stall first */
896 			usbd_xfer_set_stall(xfer);
897 			goto tr_setup;
898 		}
899 		return;
900 	}
901 }
902 
903 static void *
904 umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype)
905 {
906 	return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
907 	    type, 0xFF, subtype, 0xFF));
908 }
909 
910 static usb_error_t
911 umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no,
912     uint16_t feature, uint16_t state)
913 {
914 	struct usb_device_request req;
915 	struct usb_cdc_abstract_state ast;
916 
917 	DPRINTF("feature=%d state=%d\n",
918 	    feature, state);
919 
920 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
921 	req.bRequest = UCDC_SET_COMM_FEATURE;
922 	USETW(req.wValue, feature);
923 	req.wIndex[0] = iface_no;
924 	req.wIndex[1] = 0;
925 	USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
926 	USETW(ast.wState, state);
927 
928 	return (usbd_do_request(udev, NULL, &req, &ast));
929 }
930 
931 static int
932 umodem_detach(device_t dev)
933 {
934 	struct umodem_softc *sc = device_get_softc(dev);
935 
936 	DPRINTF("sc=%p\n", sc);
937 
938 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
939 	usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
940 
941 	device_claim_softc(dev);
942 
943 	umodem_free_softc(sc);
944 
945 	return (0);
946 }
947 
948 UCOM_UNLOAD_DRAIN(umodem);
949 
950 static void
951 umodem_free_softc(struct umodem_softc *sc)
952 {
953 	if (ucom_unref(&sc->sc_super_ucom)) {
954 		mtx_destroy(&sc->sc_mtx);
955 		device_free_softc(sc);
956 	}
957 }
958 
959 static void
960 umodem_free(struct ucom_softc *ucom)
961 {
962 	umodem_free_softc(ucom->sc_parent);
963 }
964 
965 static void
966 umodem_poll(struct ucom_softc *ucom)
967 {
968 	struct umodem_softc *sc = ucom->sc_parent;
969 	usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
970 }
971 
972 static int
973 umodem_handle_request(device_t dev,
974     const void *preq, void **pptr, uint16_t *plen,
975     uint16_t offset, uint8_t *pstate)
976 {
977 	struct umodem_softc *sc = device_get_softc(dev);
978 	const struct usb_device_request *req = preq;
979 	uint8_t is_complete = *pstate;
980 
981 	DPRINTF("sc=%p\n", sc);
982 
983 	if (!is_complete) {
984 		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
985 		    (req->bRequest == UCDC_SET_LINE_CODING) &&
986 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
987 		    (req->wIndex[1] == 0x00) &&
988 		    (req->wValue[0] == 0x00) &&
989 		    (req->wValue[1] == 0x00)) {
990 			if (offset == 0) {
991 				*plen = sizeof(sc->sc_line_coding);
992 				*pptr = &sc->sc_line_coding;
993 			} else {
994 				*plen = 0;
995 			}
996 			return (0);
997 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
998 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
999 		    (req->wIndex[1] == 0x00) &&
1000 		    (req->bRequest == UCDC_SET_COMM_FEATURE)) {
1001 			if (offset == 0) {
1002 				*plen = sizeof(sc->sc_abstract_state);
1003 				*pptr = &sc->sc_abstract_state;
1004 			} else {
1005 				*plen = 0;
1006 			}
1007 			return (0);
1008 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
1009 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
1010 		    (req->wIndex[1] == 0x00) &&
1011 		    (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
1012 			*plen = 0;
1013 			return (0);
1014 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
1015 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
1016 		    (req->wIndex[1] == 0x00) &&
1017 		    (req->bRequest == UCDC_SEND_BREAK)) {
1018 			*plen = 0;
1019 			return (0);
1020 		}
1021 	}
1022 	return (ENXIO);			/* use builtin handler */
1023 }
1024