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