]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/umodem.c
This commit was generated by cvs2svn to compensate for changes in r104912,
[FreeBSD/FreeBSD.git] / sys / dev / usb / umodem.c
1 /*      $NetBSD: umodem.c,v 1.5 1999/01/08 11:58:25 augustss Exp $      */
2 /*      $FreeBSD$       */
3
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (lennart@augustsson.net) at
10  * Carlstedt Research & Technology.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *        This product includes software developed by the NetBSD
23  *        Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40
41 /*
42  * Comm Class spec: http://www.usb.org/developers/data/devclass/usbcdc10.pdf
43  *                  http://www.usb.org/developers/data/devclass/usbcdc11.pdf
44  */
45
46 /*
47  * TODO:
48  * - Add error recovery in various places; the big problem is what
49  *   to do in a callback if there is an error.
50  * - Implement a Call Device for modems without multiplexed commands.
51  *
52  */
53
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/kernel.h>
57 #include <sys/malloc.h>
58 #if defined(__NetBSD__) || defined(__OpenBSD__)
59 #include <sys/device.h>
60 #include <sys/ioctl.h>
61 #elif defined(__FreeBSD__)
62 #include <sys/bus.h>
63 #include <sys/ioccom.h>
64 #include <sys/fcntl.h>
65 #endif
66 #include <sys/conf.h>
67 #include <sys/tty.h>
68 #include <sys/clist.h>
69 #include <sys/file.h>
70 #if __FreeBSD_version >= 500014
71 #include <sys/selinfo.h>
72 #else
73 #include <sys/select.h>
74 #endif
75 #include <sys/proc.h>
76 #include <sys/vnode.h>
77 #include <sys/poll.h>
78 #include <sys/sysctl.h>
79
80 #include <dev/usb/usb.h>
81 #include <dev/usb/usbcdc.h>
82
83 #include <dev/usb/usbdi.h>
84 #include <dev/usb/usbdi_util.h>
85 #include <dev/usb/usbdevs.h>
86 #include <dev/usb/usb_quirks.h>
87
88 #include <dev/usb/usbdevs.h>
89
90 #ifdef USB_DEBUG
91 #define DPRINTF(x) if(umodemdebug) logprintf x
92 #define DPRINTFN(n, x) if(umodemdebug > (n)) logprintf x
93 int     umodemdebug = 0;
94 SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
95 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW,
96            &umodemdebug, 0, "umodem debug level");
97 #else
98 #define DPRINTF(x)
99 #define DPRINTFN(n, x)
100 #endif
101
102 /* Macros to clear/set/test flags. */
103 #define SET(t, f)       (t) |= (f)
104 #define CLR(t, f)       (t) &= ~((unsigned)(f))
105 #define ISSET(t, f)     ((t) & (f))
106
107 #define UMODEMUNIT_MASK         0x3ffff
108 #define UMODEMDIALOUT_MASK      0x80000
109 #define UMODEMCALLUNIT_MASK     0x40000
110
111 #define UMODEMUNIT(x)           (minor(x) & UMODEMUNIT_MASK)
112 #define UMODEMDIALOUT(x)        (minor(x) & UMODEMDIALOUT_MASK)
113 #define UMODEMCALLUNIT(x)       (minor(x) & UMODEMCALLUNIT_MASK)
114
115 #define UMODEMIBUFSIZE 64
116
117 struct umodem_softc {
118         USBBASEDEVICE           sc_dev;         /* base device */
119
120         usbd_device_handle      sc_udev;        /* USB device */
121
122         int                     sc_ctl_iface_no;
123         usbd_interface_handle   sc_ctl_iface;   /* control interface */
124         int                     sc_data_iface_no;
125         usbd_interface_handle   sc_data_iface;  /* data interface */
126
127         int                     sc_bulkin_no;   /* bulk in endpoint address */
128         usbd_pipe_handle        sc_bulkin_pipe; /* bulk in pipe */
129         usbd_xfer_handle        sc_ixfer;       /* read request */
130         u_char                  *sc_ibuf;       /* read buffer */
131
132         int                     sc_bulkout_no;  /* bulk out endpoint address */
133         usbd_pipe_handle        sc_bulkout_pipe;/* bulk out pipe */
134         usbd_xfer_handle        sc_oxfer;       /* read request */
135
136         int                     sc_cm_cap;      /* CM capabilities */
137         int                     sc_acm_cap;     /* ACM capabilities */
138
139         int                     sc_cm_over_data;
140
141         struct tty              *sc_tty;        /* our tty */
142
143         usb_cdc_line_state_t    sc_line_state;  /* current line state */
144         u_char                  sc_dtr;         /* current DTR state */
145
146         u_char                  sc_opening;     /* lock during open */
147         u_char                  sc_dying;       /* disconnecting */
148
149 #if defined(__FreeBSD__)
150         dev_t                   dev;            /* special device node */
151 #endif
152 };
153
154 #if defined(__NetBSD__) || defined(__OpenBSD__)
155 cdev_decl(umodem);
156
157 #elif defined(__FreeBSD__)
158 d_open_t  umodemopen;
159 d_close_t umodemclose;
160 d_read_t  umodemread;
161 d_write_t umodemwrite;
162 d_ioctl_t umodemioctl;
163
164 #define UMODEM_CDEV_MAJOR  124
165
166 static struct cdevsw umodem_cdevsw = {
167         /* open */      umodemopen,
168         /* close */     umodemclose,
169         /* read */      umodemread,
170         /* write */     umodemwrite,
171         /* ioctl */     umodemioctl,
172         /* poll */      ttypoll,
173         /* mmap */      nommap,
174         /* strategy */  nostrategy,
175         /* name */      "umodem",
176         /* maj */       UMODEM_CDEV_MAJOR,
177         /* dump */      nodump,
178         /* psize */     nopsize,
179         /* flags */     D_TTY | D_KQFILTER,
180 #if __FreeBSD_version < 500014
181         /* bmaj */      -1,
182 #endif
183         /* kqfilter */  ttykqfilter,
184 };
185 #endif
186
187 void *umodem_get_desc
188         (usbd_device_handle dev, int type, int subtype);
189 usbd_status umodem_set_comm_feature
190         (struct umodem_softc *sc, int feature, int state);
191 usbd_status umodem_set_line_coding
192         (struct umodem_softc *sc, usb_cdc_line_state_t *state);
193
194 void    umodem_get_caps (usbd_device_handle, int *, int *);
195 void    umodem_cleanup  (struct umodem_softc *);
196 int     umodemparam     (struct tty *, struct termios *);
197 void    umodemstart     (struct tty *);
198 void    umodemstop      (struct tty *, int);
199 struct tty * umodemtty  (dev_t dev);
200 void    umodem_shutdown (struct umodem_softc *);
201 void    umodem_modem    (struct umodem_softc *, int);
202 void    umodem_break    (struct umodem_softc *, int);
203 usbd_status umodemstartread (struct umodem_softc *);
204 void    umodemreadcb    (usbd_xfer_handle, usbd_private_handle, 
205                              usbd_status status);
206 void    umodemwritecb   (usbd_xfer_handle, usbd_private_handle, 
207                              usbd_status status);
208
209 USB_DECLARE_DRIVER(umodem);
210
211 USB_MATCH(umodem)
212 {
213         USB_MATCH_START(umodem, uaa);
214         usb_interface_descriptor_t *id;
215         int cm, acm;
216         
217         if (!uaa->iface)
218                 return (UMATCH_NONE);
219
220         id = usbd_get_interface_descriptor(uaa->iface);
221         if (id == 0 ||
222             id->bInterfaceClass != UICLASS_CDC ||
223             id->bInterfaceSubClass != UISUBCLASS_ABSTRACT_CONTROL_MODEL ||
224             id->bInterfaceProtocol != UIPROTO_CDC_AT)
225                 return (UMATCH_NONE);
226         
227         umodem_get_caps(uaa->device, &cm, &acm);
228         if (!(cm & USB_CDC_CM_DOES_CM) ||
229             !(cm & USB_CDC_CM_OVER_DATA) ||
230             !(acm & USB_CDC_ACM_HAS_LINE))
231                 return (UMATCH_NONE);
232
233         return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
234 }
235
236 USB_ATTACH(umodem)
237 {
238         USB_ATTACH_START(umodem, sc, uaa);
239         usbd_device_handle dev = uaa->device;
240         usb_interface_descriptor_t *id;
241         usb_endpoint_descriptor_t *ed;
242         usb_cdc_cm_descriptor_t *cmd;
243         char devinfo[1024];
244         usbd_status err;
245         int data_ifaceno;
246         int i;
247         struct tty *tp;
248
249         usbd_devinfo(uaa->device, 0, devinfo);
250         USB_ATTACH_SETUP;
251
252         sc->sc_udev = dev;
253
254         sc->sc_ctl_iface = uaa->iface;
255         id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
256         printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
257                devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
258         sc->sc_ctl_iface_no = id->bInterfaceNumber;
259
260         umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap);
261
262         /* Get the data interface no. */
263         cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
264         if (!cmd) {
265                 DPRINTF(("%s: no CM desc\n", USBDEVNAME(sc->sc_dev)));
266                 goto bad;
267         }
268         sc->sc_data_iface_no = data_ifaceno = cmd->bDataInterface;
269
270         printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
271                USBDEVNAME(sc->sc_dev), data_ifaceno,
272                sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
273                sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
274
275
276         /* Get the data interface too. */
277         for (i = 0; i < uaa->nifaces; i++) {
278                 if (uaa->ifaces[i]) {
279                         id = usbd_get_interface_descriptor(uaa->ifaces[i]);
280                         if (id->bInterfaceNumber == data_ifaceno) {
281                                 sc->sc_data_iface = uaa->ifaces[i];
282                                 uaa->ifaces[i] = 0;
283                         }
284                 }
285         }
286         if (!sc->sc_data_iface) {
287                 printf("%s: no data interface\n", USBDEVNAME(sc->sc_dev));
288                 goto bad;
289         }
290
291         /* 
292          * Find the bulk endpoints. 
293          * Iterate over all endpoints in the data interface and take note.
294          */
295         sc->sc_bulkin_no = sc->sc_bulkout_no = -1;
296
297         id = usbd_get_interface_descriptor(sc->sc_data_iface);
298         for (i = 0; i < id->bNumEndpoints; i++) {
299                 ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
300                 if (!ed) {
301                         printf("%s: no endpoint descriptor for %d\n",
302                                 USBDEVNAME(sc->sc_dev), i);
303                         goto bad;
304                 }
305                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
306                     (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
307                         sc->sc_bulkin_no = ed->bEndpointAddress;
308                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
309                            (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
310                         sc->sc_bulkout_no = ed->bEndpointAddress;
311                 }
312         }
313
314         if (sc->sc_bulkin_no == -1) {
315                 DPRINTF(("%s: Could not find data bulk in\n",
316                         USBDEVNAME(sc->sc_dev)));
317                 goto bad;
318         }
319         if (sc->sc_bulkout_no == -1) {
320                 DPRINTF(("%s: Could not find data bulk out\n",
321                         USBDEVNAME(sc->sc_dev)));
322                 goto bad;
323         }
324
325         if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_ASSUME_CM_OVER_DATA) {
326                 sc->sc_cm_over_data = 1;
327         } else {
328             if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
329                     err = umodem_set_comm_feature(sc, UCDC_ABSTRACT_STATE,
330                                                 UCDC_DATA_MULTIPLEXED);
331                     if (err)
332                                 goto bad;
333                     sc->sc_cm_over_data = 1;
334                 }
335         }
336
337 #if defined(__NetBSD__) || defined(__OpenBSD__)
338         sc->sc_tty = tp = ttymalloc();
339 #elif defined(__FreeBSD__)
340         sc->sc_tty = tp = ttymalloc(sc->sc_tty);
341 #endif
342         tp->t_oproc = umodemstart;
343         tp->t_param = umodemparam;
344         tp->t_stop = umodemstop;
345         DPRINTF(("umodem_attach: tty_attach %p\n", tp));
346 #if defined(__NetBSD__) || defined(__OpenBSD__)
347         tty_attach(tp);
348 #endif
349
350 #if defined(__FreeBSD__)
351         DPRINTF(("umodem_attach: make_dev: umodem%d\n", device_get_unit(self)));
352         sc->dev = make_dev(&umodem_cdevsw, device_get_unit(self),
353                         UID_UUCP, GID_DIALER, 0660,
354                         "umodem%d", device_get_unit(self));
355         sc->dev->si_tty = tp;
356 #endif
357
358         sc->sc_dtr = -1;
359
360         USB_ATTACH_SUCCESS_RETURN;
361
362  bad:
363         DPRINTF(("umodem_attach: BAD -> DYING\n"));
364         sc->sc_dying = 1;
365         USB_ATTACH_ERROR_RETURN;
366 }
367
368 void
369 umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
370 {
371         usb_cdc_cm_descriptor_t *cmd;
372         usb_cdc_acm_descriptor_t *cad;
373
374         *cm = *acm = 0;
375
376         cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
377         if (!cmd) {
378                 DPRINTF(("umodem_get_desc: no CM desc\n"));
379                 return;
380         }
381         *cm = cmd->bmCapabilities;
382
383         cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
384         if (!cad) {
385                 DPRINTF(("umodem_get_desc: no ACM desc\n"));
386                 return;
387         }
388         *acm = cad->bmCapabilities;
389
390
391 void
392 umodemstart(struct tty *tp)
393 {
394         struct umodem_softc *sc;
395         struct cblock *cbp;
396         int s;
397         u_char *data;
398         int cnt;
399
400         USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
401
402         if (sc->sc_dying)
403                 return;
404
405         s = spltty();
406         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
407                 ttwwakeup(tp);
408                 DPRINTFN(4,("umodemstart: stopped\n"));
409                 goto out;
410         }
411
412 #if defined(__NetBSD__) || defined(__OpenBSD__)
413         if (tp->t_outq.c_cc <= tp->t_lowat) {
414                 if (ISSET(tp->t_state, TS_ASLEEP)) {
415                         CLR(tp->t_state, TS_ASLEEP);
416                         wakeup(&tp->t_outq);
417                 }
418                 selwakeup(&tp->t_wsel);
419                 if (tp->t_outq.c_cc == 0)
420                         goto out;
421         }
422 #elif defined(__FreeBSD__)
423         if (tp->t_outq.c_cc <= tp->t_olowat) {
424                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
425                         CLR(tp->t_state, TS_SO_OLOWAT);
426                         wakeup(TSA_OLOWAT(tp));
427                 }
428                 selwakeup(&tp->t_wsel);
429                 if (tp->t_outq.c_cc == 0) {
430                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
431                             TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
432                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
433                                 wakeup(TSA_OCOMPLETE(tp));
434                         } 
435                         goto out;
436                 }
437         }
438 #endif
439
440         /* Grab the first contiguous region of buffer space. */
441         data = tp->t_outq.c_cf;
442 #if defined(__NetBSD__) || defined(__OpenBSD__)
443         cnt = ndqb(&tp->t_outq, 0);
444 #elif defined(__FreeBSD__)
445         cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
446         cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
447 #endif
448
449         if (cnt == 0) {
450                 DPRINTF(("umodemstart: cnt==0\n"));
451                 splx(s);
452                 return;
453         }
454
455         SET(tp->t_state, TS_BUSY);
456
457         DPRINTFN(4,("umodemstart: %d chars\n", cnt));
458         /* XXX what can we do on error? */
459         usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, 
460                            (usbd_private_handle)sc, data, cnt,
461                            0, USBD_NO_TIMEOUT, umodemwritecb);
462         (void)usbd_transfer(sc->sc_oxfer);
463
464         ttwwakeup(tp);
465 out:
466         splx(s);
467 }
468
469 void
470 umodemwritecb(usbd_xfer_handle xfer, usbd_private_handle priv,
471               usbd_status status)
472 {
473         struct umodem_softc *sc = (struct umodem_softc *)priv;
474         struct tty *tp = sc->sc_tty;
475         u_int32_t cc;
476         int s;
477
478         DPRINTFN(5,("umodemwritecb: status=%d\n", status));
479
480         if (status == USBD_CANCELLED)
481                 return;
482
483         if (status != USBD_NORMAL_COMPLETION) {
484                 DPRINTF(("umodemwritecb: status=%d\n", status));
485                 if (status == USBD_STALLED)
486                     usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
487                 /* XXX we should restart after some delay. */
488                 return;
489         }
490
491         usbd_get_xfer_status(xfer, 0, 0, &cc, 0);
492         DPRINTFN(5,("umodemwritecb: cc=%d\n", cc));
493
494         s = spltty();
495         CLR(tp->t_state, TS_BUSY);
496         if (ISSET(tp->t_state, TS_FLUSH))
497                 CLR(tp->t_state, TS_FLUSH);
498         else
499                 ndflush(&tp->t_outq, cc);
500         (*linesw[tp->t_line].l_start)(tp);
501         splx(s);
502 }
503
504 int
505 umodemparam(struct tty *tp, struct termios *t)
506 {
507         struct umodem_softc *sc;
508         usb_cdc_line_state_t ls;
509
510         USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
511
512         if (sc->sc_dying)
513                 return (EIO);
514
515         /* Check requested parameters. */
516         if (t->c_ospeed < 0)
517                 return (EINVAL);
518         if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
519                 return (EINVAL);
520
521         /*
522          * If there were no changes, don't do anything.  This avoids dropping
523          * input and improves performance when all we did was frob things like
524          * VMIN and VTIME.
525          */
526         if (tp->t_ospeed == t->c_ospeed &&
527             tp->t_cflag == t->c_cflag)
528                 return (0);
529
530         /* And copy to tty. */
531         tp->t_ispeed = 0;
532         tp->t_ospeed = t->c_ospeed;
533         tp->t_cflag = t->c_cflag;
534
535         USETDW(ls.dwDTERate, t->c_ospeed);
536         if (ISSET(t->c_cflag, CSTOPB))
537                 ls.bCharFormat = UCDC_STOP_BIT_2;
538         else
539                 ls.bCharFormat = UCDC_STOP_BIT_1;
540         if (ISSET(t->c_cflag, PARENB)) {
541                 if (ISSET(t->c_cflag, PARODD))
542                         ls.bParityType = UCDC_PARITY_ODD;
543                 else
544                         ls.bParityType = UCDC_PARITY_EVEN;
545         } else
546                 ls.bParityType = UCDC_PARITY_NONE;
547         switch (ISSET(t->c_cflag, CSIZE)) {
548         case CS5:
549                 ls.bDataBits = 5;
550                 break;
551         case CS6:
552                 ls.bDataBits = 6;
553                 break;
554         case CS7:
555                 ls.bDataBits = 7;
556                 break;
557         case CS8:
558                 ls.bDataBits = 8;
559                 break;
560         }
561         /* XXX what can we if it fails? */
562         (void)umodem_set_line_coding(sc, &ls);
563
564         /*
565          * Update the tty layer's idea of the carrier bit, in case we changed
566          * CLOCAL or MDMBUF.  We don't hang up here; we only do that by
567          * explicit request.
568          */
569         (void) (*linesw[tp->t_line].l_modem)(tp, 1 /* XXX carrier */ );
570
571         return (0);
572 }
573
574 int
575 umodemopen(dev_t dev, int flag, int mode, usb_proc_ptr p)
576 {
577         int unit = UMODEMUNIT(dev);
578         struct umodem_softc *sc;
579         usbd_status err;
580         struct tty *tp;
581         int s;
582         int error;
583  
584         USB_GET_SC_OPEN(umodem, unit, sc);
585
586         if (sc->sc_dying)
587                 return (EIO);
588
589 #if defined(__NetBSD__) || defined(__OpenBBSD__)
590         if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
591                 return (ENXIO);
592 #endif
593
594         tp = sc->sc_tty;
595
596         DPRINTF(("%s: umodemopen: tp=%p\n", USBDEVNAME(sc->sc_dev), tp));
597
598         if (ISSET(tp->t_state, TS_ISOPEN) &&
599             ISSET(tp->t_state, TS_XCLUDE) &&
600             suser(p))
601                 return (EBUSY);
602
603         /*
604          * Do the following iff this is a first open.
605          */
606         s = spltty();
607         while (sc->sc_opening)
608                 tsleep(&sc->sc_opening, PRIBIO, "umdmop", 0);
609         sc->sc_opening = 1;
610         
611 #if defined(__NetBSD__) || defined(__OpenBSD__)
612         if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
613 #elif defined(__FreeBSD__)
614         if (!ISSET(tp->t_state, TS_ISOPEN)) {
615 #endif
616                 struct termios t;
617
618                 tp->t_dev = dev;
619
620                 /*
621                  * Initialize the termios status to the defaults.  Add in the
622                  * sticky bits from TIOCSFLAGS.
623                  */
624                 t.c_ispeed = 0;
625                 t.c_ospeed = TTYDEF_SPEED;
626                 t.c_cflag = TTYDEF_CFLAG;
627                 /* Make sure umodemparam() will do something. */
628                 tp->t_ospeed = 0;
629                 (void) umodemparam(tp, &t);
630                 tp->t_iflag = TTYDEF_IFLAG;
631                 tp->t_oflag = TTYDEF_OFLAG;
632                 tp->t_lflag = TTYDEF_LFLAG;
633                 ttychars(tp);
634                 ttsetwater(tp);
635
636                 /*
637                  * Turn on DTR.  We must always do this, even if carrier is not
638                  * present, because otherwise we'd have to use TIOCSDTR
639                  * immediately after setting CLOCAL, which applications do not
640                  * expect.  We always assert DTR while the device is open
641                  * unless explicitly requested to deassert it.
642                  */
643                 umodem_modem(sc, 1);
644
645                 DPRINTF(("umodemopen: open pipes\n"));
646
647                 /* Open the bulk pipes */
648                 err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkin_no, 0,
649                                    &sc->sc_bulkin_pipe);
650                 if (err) {
651                         DPRINTF(("%s: cannot open bulk out pipe (addr %d)\n",
652                                  USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no));
653                         return (EIO);
654                 }
655                 err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkout_no,
656                                    USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
657                 if (err) {
658                         DPRINTF(("%s: cannot open bulk in pipe (addr %d)\n",
659                                  USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no));
660                         usbd_close_pipe(sc->sc_bulkin_pipe);
661                         return (EIO);
662                 }
663                 
664                 /* Allocate a request and an input buffer and start reading. */
665                 sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
666                 if (sc->sc_ixfer == 0) {
667                         usbd_close_pipe(sc->sc_bulkin_pipe);
668                         usbd_close_pipe(sc->sc_bulkout_pipe);
669                         return (ENOMEM);
670                 }
671                 sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
672                 if (sc->sc_oxfer == 0) {
673                         usbd_close_pipe(sc->sc_bulkin_pipe);
674                         usbd_close_pipe(sc->sc_bulkout_pipe);
675                         usbd_free_xfer(sc->sc_ixfer);
676                         return (ENOMEM);
677                 }
678                 sc->sc_ibuf = malloc(UMODEMIBUFSIZE, M_USBDEV, M_WAITOK);
679                 umodemstartread(sc);
680         }
681         sc->sc_opening = 0;
682         wakeup(&sc->sc_opening);
683         splx(s);
684
685 #if defined(__NetBSD__) || defined(__OpenBSD__)
686         error = ttyopen(tp, UMODEMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
687         if (error)
688                 goto bad;
689 #elif defined(__FreeBSD__)
690         error = ttyopen(dev, tp);
691         if (error)
692                 goto bad;
693 #endif
694
695         error = (*linesw[tp->t_line].l_open)(dev, tp);
696         if (error)
697                 goto bad;
698
699         return (0);
700
701 bad:
702 #if defined(__NetBSD__) || defined(__OpenBSD__)
703         if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
704 #elif defined(__FreeBSD__)
705         if (!ISSET(tp->t_state, TS_ISOPEN)) {
706 #endif
707                 /*
708                  * We failed to open the device, and nobody else had it opened.
709                  * Clean up the state as appropriate.
710                  */
711                 umodem_cleanup(sc);
712         }
713
714         return (error);
715 }
716
717 usbd_status
718 umodemstartread(struct umodem_softc *sc)
719 {
720         usbd_status err;
721
722         DPRINTFN(5,("umodemstartread: start\n"));
723         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, 
724                            (usbd_private_handle)sc, 
725                            sc->sc_ibuf,  UMODEMIBUFSIZE, USBD_SHORT_XFER_OK, 
726                            USBD_NO_TIMEOUT, umodemreadcb);
727
728         err = usbd_transfer(sc->sc_ixfer);
729         if (err != USBD_IN_PROGRESS)
730                 return (err);
731
732         return (USBD_NORMAL_COMPLETION);
733 }
734  
735 void
736 umodemreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
737 {
738         struct umodem_softc *sc = (struct umodem_softc *)p;
739         struct tty *tp = sc->sc_tty;
740         int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint;
741         usbd_status err;
742         u_int32_t cc;
743         u_char *cp;
744         int s;
745
746         if (status == USBD_CANCELLED)
747                 return;
748
749         if (status != USBD_NORMAL_COMPLETION) {
750                 DPRINTF(("umodemreadcb: status=%d\n", status));
751                 if (status == USBD_STALLED)
752                     usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
753                 /* XXX we should restart after some delay. */
754                 return;
755         }
756
757         usbd_get_xfer_status(xfer, 0, (void **)&cp, &cc, 0);
758         DPRINTFN(5,("umodemreadcb: got %d chars, tp=%p\n", cc, tp));
759         s = spltty();
760         /* Give characters to tty layer. */
761         while (cc-- > 0) {
762                 DPRINTFN(7,("umodemreadcb: char=0x%02x\n", *cp));
763                 if ((*rint)(*cp++, tp) == -1) {
764                         /* XXX what should we do? */
765                         break;
766                 }
767         }
768         splx(s);
769
770         err = umodemstartread(sc);
771         if (err) {
772                 printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
773                 /* XXX what should we dow now? */
774         }
775 }
776
777 int
778 umodemclose(dev_t dev, int flag, int mode, usb_proc_ptr p)
779 {
780         struct umodem_softc *sc;
781         struct tty *tp;
782         int s;
783
784         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
785
786         tp = sc->sc_tty;
787
788         DPRINTF(("%s: umodemclose sc_tty=%p\n", USBDEVNAME(sc->sc_dev), tp));
789
790         if (!ISSET(tp->t_state, TS_ISOPEN))
791                 return (0);
792
793         DPRINTF(("%s: umodemclose lclose(%p,%d)\n", USBDEVNAME(sc->sc_dev), tp,flag));
794
795         s=spltty();
796         DPRINTF(("%s: umodemclose lclose=%p\n", USBDEVNAME(sc->sc_dev), linesw[tp->t_line].l_close));
797         (*linesw[tp->t_line].l_close)(tp, flag);
798
799         DPRINTF(("%s: umodemclose ttyclose(%p)\n", USBDEVNAME(sc->sc_dev), tp));
800         ttyclose(tp);
801         splx(s);
802
803         DPRINTF(("%s: umodemclose sc->sc_dying=%d\n", USBDEVNAME(sc->sc_dev), sc->sc_dying));
804         if (sc->sc_dying)
805                 return (0);
806
807         DPRINTF(("%s: umodemclose tp->t_state=%d\n", USBDEVNAME(sc->sc_dev), tp->t_state));
808 #if defined(__NetBSD__) || defined(__OpenBSD__)
809         if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
810 #elif defined(__FreeBSD__)
811         if (!ISSET(tp->t_state, TS_ISOPEN)) {
812 #endif
813                 /*
814                  * Although we got a last close, the device may still be in
815                  * use; e.g. if this was the dialout node, and there are still
816                  * processes waiting for carrier on the non-dialout node.
817                  */
818                 DPRINTF(("%s: umodemclose umodem_cleanup(%p)\n", USBDEVNAME(sc->sc_dev), sc));
819                 umodem_cleanup(sc);
820         }
821         DPRINTF(("%s: umodemclose return\n", USBDEVNAME(sc->sc_dev)));
822
823         return (0);
824 }
825  
826 void
827 umodem_cleanup(struct umodem_softc *sc)
828 {
829         umodem_shutdown(sc);
830
831         DPRINTFN(5, ("%s: umodem_cleanup: closing pipes\n",
832                 USBDEVNAME(sc->sc_dev)));
833
834         usbd_abort_pipe(sc->sc_bulkin_pipe);
835         usbd_close_pipe(sc->sc_bulkin_pipe);
836         usbd_abort_pipe(sc->sc_bulkout_pipe);
837         usbd_close_pipe(sc->sc_bulkout_pipe);
838         usbd_free_xfer(sc->sc_ixfer);
839         usbd_free_xfer(sc->sc_oxfer);
840         free(sc->sc_ibuf, M_USBDEV);
841 }
842
843 int
844 umodemread(dev_t dev, struct uio *uio, int flag)
845 {
846         struct umodem_softc *sc;
847         struct tty *tp;
848
849         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
850
851         tp = sc->sc_tty;
852         
853         if (sc->sc_dying)
854                 return (EIO);
855  
856         return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
857 }
858  
859 int
860 umodemwrite(dev_t dev, struct uio *uio, int flag)
861 {
862         struct umodem_softc *sc;
863         struct tty *tp;
864
865         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
866
867         tp = sc->sc_tty;
868
869         if (sc->sc_dying)
870                 return (EIO);
871  
872         return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
873 }
874
875 void
876 umodemstop(struct tty *tp, int flag)
877 {
878         struct umodem_softc *sc;
879         int s;
880
881         USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
882
883         DPRINTF(("umodemstop: %d\n", flag));
884         s = spltty();
885         if (ISSET(tp->t_state, TS_BUSY)) {
886                 DPRINTF(("umodemstop: XXX\n"));
887                 /* XXX do what? */
888                 if (!ISSET(tp->t_state, TS_TTSTOP))
889                         SET(tp->t_state, TS_FLUSH);
890         }
891         splx(s);
892 }
893
894 struct tty *
895 umodemtty(dev_t dev)
896 {
897         struct umodem_softc *sc;
898         struct tty *tp;
899
900         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
901
902         tp = sc->sc_tty;
903
904         return (tp);
905 }
906
907 int
908 umodemioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
909 {
910         struct umodem_softc *sc;
911         struct tty *tp;
912         int error;
913         int s;
914         int bits;
915
916         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
917
918         tp = sc->sc_tty;
919
920         if (sc->sc_dying)
921                 return (EIO);
922  
923         DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd));
924
925         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
926         if (error >= 0)
927                 return (error);
928
929 #if defined(__NetBSD__) || defined(__OpenBSD__)
930         error = ttioctl(tp, cmd, data, flag, p);
931 #elif defined(__FreeBSD__)
932         error = ttioctl(tp, cmd, data, flag);
933 #endif
934         if (error >= 0)
935                 return (error);
936
937         DPRINTF(("umodemioctl: our cmd=0x%08lx\n", cmd));
938         s = spltty();
939
940         switch (cmd) {
941         case TIOCSBRK:
942                 umodem_break(sc, 1);
943                 break;
944
945         case TIOCCBRK:
946                 umodem_break(sc, 0);
947                 break;
948
949         case TIOCSDTR:
950                 umodem_modem(sc, 1);
951                 break;
952
953         case TIOCCDTR:
954                 umodem_modem(sc, 0);
955                 break;
956
957         case TIOCMGET:
958                 bits = TIOCM_LE;
959                 if(sc->sc_dtr)
960                         bits |= TIOCM_DTR;
961                 *(int *)data = bits;
962                 break;
963
964         case TIOCMSET:
965                 break;
966
967         case USB_GET_CM_OVER_DATA:
968                 *(int *)data = sc->sc_cm_over_data;
969                 break;
970
971         case USB_SET_CM_OVER_DATA:
972                 if (*(int *)data != sc->sc_cm_over_data) {
973                         /* XXX change it */
974                 }
975                 break;
976
977         default:
978                 DPRINTF(("umodemioctl: unknown\n"));
979                 error = ENOTTY;
980                 splx(s);
981                 return (error);
982         }
983
984         splx(s);
985         return(0);
986 }
987
988 void
989 umodem_shutdown(struct umodem_softc *sc)
990 {
991         struct tty *tp = sc->sc_tty;
992
993         DPRINTF(("%s: umodem_shutdown\n", USBDEVNAME(sc->sc_dev)));
994         /*
995          * Hang up if necessary.  Wait a bit, so the other side has time to
996          * notice even if we immediately open the port again.
997          */
998         if (ISSET(tp->t_cflag, HUPCL)) {
999                 umodem_modem(sc, 0);
1000 #if defined(__NetBSD__) || defined(__OpenBSD__)
1001                 (void) tsleep(sc, TTIPRI, ttclos, hz);
1002 #elif defined(__FreeBSD__)
1003                 (void) tsleep(sc, TTIPRI, "umdmsd", hz);
1004 #endif
1005         }
1006 }
1007
1008 void
1009 umodem_modem(struct umodem_softc *sc, int onoff)
1010 {
1011         usb_device_request_t req;
1012
1013         DPRINTF(("%s: umodem_modem: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff));
1014
1015         if (sc->sc_dtr == onoff)
1016                 return;
1017
1018         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1019         req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
1020         USETW(req.wValue, onoff ? UCDC_LINE_DTR : 0);
1021         USETW(req.wIndex, sc->sc_ctl_iface_no);
1022         USETW(req.wLength, 0);
1023
1024         (void)usbd_do_request(sc->sc_udev, &req, 0);
1025
1026         sc->sc_dtr = onoff;
1027 }
1028
1029 void
1030 umodem_break(struct umodem_softc *sc, int onoff)
1031 {
1032         usb_device_request_t req;
1033
1034         DPRINTF(("%s: umodem_break: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff));
1035
1036         if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
1037                 return;
1038
1039         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1040         req.bRequest = UCDC_SEND_BREAK;
1041         USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
1042         USETW(req.wIndex, sc->sc_ctl_iface_no);
1043         USETW(req.wLength, 0);
1044
1045         (void)usbd_do_request(sc->sc_udev, &req, 0);
1046 }
1047
1048 void *
1049 umodem_get_desc(usbd_device_handle dev, int type, int subtype)
1050 {
1051         usb_descriptor_t *desc;
1052         usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
1053         uByte *p = (uByte *)cd;
1054         uByte *end = p + UGETW(cd->wTotalLength);
1055
1056         while (p < end) {
1057                 desc = (usb_descriptor_t *)p;
1058                 if (desc->bDescriptorType == type &&
1059                     desc->bDescriptorSubtype == subtype)
1060                         return (desc);
1061                 p += desc->bLength;
1062         }
1063
1064         return (0);
1065 }
1066
1067 usbd_status
1068 umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state)
1069 {
1070         usb_device_request_t req;
1071         usbd_status err;
1072         usb_cdc_abstract_state_t ast;
1073
1074         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1075         req.bRequest = UCDC_SET_COMM_FEATURE;
1076         USETW(req.wValue, feature);
1077         USETW(req.wIndex, sc->sc_ctl_iface_no);
1078         USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
1079         USETW(ast.wState, state);
1080
1081         err = usbd_do_request(sc->sc_udev, &req, &ast);
1082         if (err) {
1083                 DPRINTF(("%s: umodem_set_comm_feat: feature=%d failed, err=%d\n",
1084                          USBDEVNAME(sc->sc_dev), feature, err));
1085                 return (err);
1086         }
1087
1088         return (USBD_NORMAL_COMPLETION);
1089 }
1090
1091 usbd_status
1092 umodem_set_line_coding(struct umodem_softc *sc, usb_cdc_line_state_t *state)
1093 {
1094         usb_device_request_t req;
1095         usbd_status err;
1096
1097         DPRINTF(("%s: umodem_set_line_cod: rate=%d fmt=%d parity=%d bits=%d\n",
1098                  USBDEVNAME(sc->sc_dev), UGETDW(state->dwDTERate),
1099                  state->bCharFormat, state->bParityType, state->bDataBits));
1100
1101         if (bcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
1102                 DPRINTF(("%s: umodem_set_line_coding: already set\n",
1103                         USBDEVNAME(sc->sc_dev)));
1104                 return (USBD_NORMAL_COMPLETION);
1105         }
1106
1107         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1108         req.bRequest = UCDC_SET_LINE_CODING;
1109         USETW(req.wValue, 0);
1110         USETW(req.wIndex, sc->sc_ctl_iface_no);
1111         USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
1112
1113         err = usbd_do_request(sc->sc_udev, &req, state);
1114         if (err) {
1115                 DPRINTF(("%s: umodem_set_line_coding: failed, err=%d\n",
1116                         USBDEVNAME(sc->sc_dev), err));
1117                 return (err);
1118         }
1119
1120         sc->sc_line_state = *state;
1121
1122         return (USBD_NORMAL_COMPLETION);
1123 }
1124
1125 #if defined(__NetBSD__) || defined(__OpenBSD__)
1126 int
1127 umodem_activate(device_ptr_t self, enum devact act)
1128 {
1129         struct umodem_softc *sc = (struct umodem_softc *)self;
1130
1131         switch (act) {
1132         case DVACT_ACTIVATE:
1133                 return (EOPNOTSUPP);
1134                 break;
1135
1136         case DVACT_DEACTIVATE:
1137                 sc->sc_dying = 1;
1138                 break;
1139         }
1140         return (0);
1141 }
1142 #endif
1143
1144 USB_DETACH(umodem)
1145 {
1146         USB_DETACH_START(umodem, sc);
1147 #if defined(__NetBSD__) || defined(__OpenBSD__)
1148         int maj, mn;
1149
1150         DPRINTF(("umodem_detach: sc=%p flags=%d tp=%p\n", 
1151                  sc, flags, sc->sc_tty));
1152 #elif defined(__FreeBSD__)
1153         DPRINTF(("umodem_detach: sc=%p flags=%d, tp=%p\n", 
1154                  sc, 0,  sc->sc_tty));
1155 #endif
1156
1157         sc->sc_dying = 1;
1158
1159 #ifdef DIAGNOSTIC
1160         if (sc->sc_tty == 0) {
1161                 DPRINTF(("umodem_detach: no tty\n"));
1162                 return (0);
1163         }
1164 #endif
1165
1166         /* use refernce count? XXX */
1167
1168 #if defined(__NetBSD__) || defined(__OpenBSD__)
1169         /* locate the major number */
1170         for (maj = 0; maj < nchrdev; maj++)
1171                 if (cdevsw[maj].d_open == umodemopen)
1172                         break;
1173
1174         /* Nuke the vnodes for any open instances. */
1175         mn = self->dv_unit;
1176         vdevgone(maj, mn, mn, VCHR);
1177         vdevgone(maj, mn, mn | UMODEMDIALOUT_MASK, VCHR);
1178         vdevgone(maj, mn, mn | UMODEMCALLUNIT_MASK, VCHR);
1179 #elif defined(__FreeBSD__)
1180         /* XXX not yet implemented */
1181 #endif
1182
1183 #if defined(__FreeBSD__)
1184         destroy_dev(sc->dev);
1185 #endif
1186
1187         /* Detach and free the tty. */
1188 #if defined(__NetBSD__) || defined(__OpenBSD__)
1189         tty_detach(sc->sc_tty);
1190         ttyfree(sc->sc_tty);
1191         sc->sc_tty = 0;
1192 #endif
1193
1194         return (0);
1195 }
1196
1197 #if defined(__FreeBSD__)
1198 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0);
1199 #endif