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