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