proc->thread stage 2: MAJOR revamping of system calls, ucred, jail API,
[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.3 2003/06/23 17:55:36 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 td)
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         struct proc *p = td->td_proc;
577
578         KKASSERT(p != NULL);
579  
580         USB_GET_SC_OPEN(umodem, unit, sc);
581
582         if (sc->sc_dying)
583                 return (EIO);
584
585 #if defined(__NetBSD__) || defined(__OpenBBSD__)
586         if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
587                 return (ENXIO);
588 #endif
589
590         tp = sc->sc_tty;
591
592         DPRINTF(("%s: umodemopen: tp=%p\n", USBDEVNAME(sc->sc_dev), tp));
593
594         if (ISSET(tp->t_state, TS_ISOPEN) &&
595             ISSET(tp->t_state, TS_XCLUDE) &&
596             p->p_ucred->cr_uid != 0)
597                 return (EBUSY);
598
599         /*
600          * Do the following iff this is a first open.
601          */
602         s = spltty();
603         while (sc->sc_opening)
604                 tsleep(&sc->sc_opening, PRIBIO, "umdmop", 0);
605         sc->sc_opening = 1;
606         
607 #if defined(__NetBSD__) || defined(__OpenBSD__)
608         if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
609 #elif defined(__FreeBSD__)
610         if (!ISSET(tp->t_state, TS_ISOPEN)) {
611 #endif
612                 struct termios t;
613
614                 tp->t_dev = dev;
615
616                 /*
617                  * Initialize the termios status to the defaults.  Add in the
618                  * sticky bits from TIOCSFLAGS.
619                  */
620                 t.c_ispeed = 0;
621                 t.c_ospeed = TTYDEF_SPEED;
622                 t.c_cflag = TTYDEF_CFLAG;
623                 /* Make sure umodemparam() will do something. */
624                 tp->t_ospeed = 0;
625                 (void) umodemparam(tp, &t);
626                 tp->t_iflag = TTYDEF_IFLAG;
627                 tp->t_oflag = TTYDEF_OFLAG;
628                 tp->t_lflag = TTYDEF_LFLAG;
629                 ttychars(tp);
630                 ttsetwater(tp);
631
632                 /*
633                  * Turn on DTR.  We must always do this, even if carrier is not
634                  * present, because otherwise we'd have to use TIOCSDTR
635                  * immediately after setting CLOCAL, which applications do not
636                  * expect.  We always assert DTR while the device is open
637                  * unless explicitly requested to deassert it.
638                  */
639                 umodem_modem(sc, 1);
640
641                 DPRINTF(("umodemopen: open pipes\n"));
642
643                 /* Open the bulk pipes */
644                 err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkin_no, 0,
645                                    &sc->sc_bulkin_pipe);
646                 if (err) {
647                         DPRINTF(("%s: cannot open bulk out pipe (addr %d)\n",
648                                  USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no));
649                         return (EIO);
650                 }
651                 err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkout_no,
652                                    USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
653                 if (err) {
654                         DPRINTF(("%s: cannot open bulk in pipe (addr %d)\n",
655                                  USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no));
656                         usbd_close_pipe(sc->sc_bulkin_pipe);
657                         return (EIO);
658                 }
659                 
660                 /* Allocate a request and an input buffer and start reading. */
661                 sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
662                 if (sc->sc_ixfer == 0) {
663                         usbd_close_pipe(sc->sc_bulkin_pipe);
664                         usbd_close_pipe(sc->sc_bulkout_pipe);
665                         return (ENOMEM);
666                 }
667                 sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
668                 if (sc->sc_oxfer == 0) {
669                         usbd_close_pipe(sc->sc_bulkin_pipe);
670                         usbd_close_pipe(sc->sc_bulkout_pipe);
671                         usbd_free_xfer(sc->sc_ixfer);
672                         return (ENOMEM);
673                 }
674                 sc->sc_ibuf = malloc(UMODEMIBUFSIZE, M_USBDEV, M_WAITOK);
675                 umodemstartread(sc);
676         }
677         sc->sc_opening = 0;
678         wakeup(&sc->sc_opening);
679         splx(s);
680
681 #if defined(__NetBSD__) || defined(__OpenBSD__)
682         error = ttyopen(tp, UMODEMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
683         if (error)
684                 goto bad;
685 #elif defined(__FreeBSD__)
686         error = ttyopen(dev, tp);
687         if (error)
688                 goto bad;
689 #endif
690
691         error = (*linesw[tp->t_line].l_open)(dev, tp);
692         if (error)
693                 goto bad;
694
695         return (0);
696
697 bad:
698 #if defined(__NetBSD__) || defined(__OpenBSD__)
699         if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
700 #elif defined(__FreeBSD__)
701         if (!ISSET(tp->t_state, TS_ISOPEN)) {
702 #endif
703                 /*
704                  * We failed to open the device, and nobody else had it opened.
705                  * Clean up the state as appropriate.
706                  */
707                 umodem_cleanup(sc);
708         }
709
710         return (error);
711 }
712
713 usbd_status
714 umodemstartread(struct umodem_softc *sc)
715 {
716         usbd_status err;
717
718         DPRINTFN(5,("umodemstartread: start\n"));
719         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, 
720                            (usbd_private_handle)sc, 
721                            sc->sc_ibuf,  UMODEMIBUFSIZE, USBD_SHORT_XFER_OK, 
722                            USBD_NO_TIMEOUT, umodemreadcb);
723
724         err = usbd_transfer(sc->sc_ixfer);
725         if (err != USBD_IN_PROGRESS)
726                 return (err);
727
728         return (USBD_NORMAL_COMPLETION);
729 }
730  
731 void
732 umodemreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
733 {
734         struct umodem_softc *sc = (struct umodem_softc *)p;
735         struct tty *tp = sc->sc_tty;
736         int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint;
737         usbd_status err;
738         u_int32_t cc;
739         u_char *cp;
740         int s;
741
742         if (status == USBD_CANCELLED)
743                 return;
744
745         if (status != USBD_NORMAL_COMPLETION) {
746                 DPRINTF(("umodemreadcb: status=%d\n", status));
747                 usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
748                 /* XXX we should restart after some delay. */
749                 return;
750         }
751
752         usbd_get_xfer_status(xfer, 0, (void **)&cp, &cc, 0);
753         DPRINTFN(5,("umodemreadcb: got %d chars, tp=%p\n", cc, tp));
754         s = spltty();
755         /* Give characters to tty layer. */
756         while (cc-- > 0) {
757                 DPRINTFN(7,("umodemreadcb: char=0x%02x\n", *cp));
758                 if ((*rint)(*cp++, tp) == -1) {
759                         /* XXX what should we do? */
760                         break;
761                 }
762         }
763         splx(s);
764
765         err = umodemstartread(sc);
766         if (err) {
767                 printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
768                 /* XXX what should we dow now? */
769         }
770 }
771
772 int
773 umodemclose(dev_t dev, int flag, int mode, usb_proc_ptr p)
774 {
775         struct umodem_softc *sc;
776         struct tty *tp;
777         int s;
778
779         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
780
781         tp = sc->sc_tty;
782
783         DPRINTF(("%s: umodemclose sc_tty=%p\n", USBDEVNAME(sc->sc_dev), tp));
784
785         if (!ISSET(tp->t_state, TS_ISOPEN))
786                 return (0);
787
788         DPRINTF(("%s: umodemclose lclose(%p,%d)\n", USBDEVNAME(sc->sc_dev), tp,flag));
789
790         s=spltty();
791         DPRINTF(("%s: umodemclose lclose=%p\n", USBDEVNAME(sc->sc_dev), linesw[tp->t_line].l_close));
792         (*linesw[tp->t_line].l_close)(tp, flag);
793
794         DPRINTF(("%s: umodemclose ttyclose(%p)\n", USBDEVNAME(sc->sc_dev), tp));
795         ttyclose(tp);
796         splx(s);
797
798         DPRINTF(("%s: umodemclose sc->sc_dying=%d\n", USBDEVNAME(sc->sc_dev), sc->sc_dying));
799         if (sc->sc_dying)
800                 return (0);
801
802         DPRINTF(("%s: umodemclose tp->t_state=%d\n", USBDEVNAME(sc->sc_dev), tp->t_state));
803 #if defined(__NetBSD__) || defined(__OpenBSD__)
804         if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
805 #elif defined(__FreeBSD__)
806         if (!ISSET(tp->t_state, TS_ISOPEN)) {
807 #endif
808                 /*
809                  * Although we got a last close, the device may still be in
810                  * use; e.g. if this was the dialout node, and there are still
811                  * processes waiting for carrier on the non-dialout node.
812                  */
813                 DPRINTF(("%s: umodemclose umodem_cleanup(%p)\n", USBDEVNAME(sc->sc_dev), sc));
814                 umodem_cleanup(sc);
815         }
816         DPRINTF(("%s: umodemclose return\n", USBDEVNAME(sc->sc_dev)));
817
818         return (0);
819 }
820  
821 void
822 umodem_cleanup(struct umodem_softc *sc)
823 {
824         umodem_shutdown(sc);
825
826         DPRINTFN(5, ("%s: umodem_cleanup: closing pipes\n",
827                 USBDEVNAME(sc->sc_dev)));
828
829         usbd_abort_pipe(sc->sc_bulkin_pipe);
830         usbd_close_pipe(sc->sc_bulkin_pipe);
831         usbd_abort_pipe(sc->sc_bulkout_pipe);
832         usbd_close_pipe(sc->sc_bulkout_pipe);
833         usbd_free_xfer(sc->sc_ixfer);
834         usbd_free_xfer(sc->sc_oxfer);
835         free(sc->sc_ibuf, M_USBDEV);
836 }
837
838 int
839 umodemread(dev_t dev, struct uio *uio, int flag)
840 {
841         struct umodem_softc *sc;
842         struct tty *tp;
843
844         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
845
846         tp = sc->sc_tty;
847         
848         if (sc->sc_dying)
849                 return (EIO);
850  
851         return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
852 }
853  
854 int
855 umodemwrite(dev_t dev, struct uio *uio, int flag)
856 {
857         struct umodem_softc *sc;
858         struct tty *tp;
859
860         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
861
862         tp = sc->sc_tty;
863
864         if (sc->sc_dying)
865                 return (EIO);
866  
867         return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
868 }
869
870 void
871 umodemstop(struct tty *tp, int flag)
872 {
873         struct umodem_softc *sc;
874         int s;
875
876         USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
877
878         DPRINTF(("umodemstop: %d\n", flag));
879         s = spltty();
880         if (ISSET(tp->t_state, TS_BUSY)) {
881                 DPRINTF(("umodemstop: XXX\n"));
882                 /* XXX do what? */
883                 if (!ISSET(tp->t_state, TS_TTSTOP))
884                         SET(tp->t_state, TS_FLUSH);
885         }
886         splx(s);
887 }
888
889 struct tty *
890 umodemtty(dev_t dev)
891 {
892         struct umodem_softc *sc;
893         struct tty *tp;
894
895         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
896
897         tp = sc->sc_tty;
898
899         return (tp);
900 }
901
902 int
903 umodemioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
904 {
905         struct umodem_softc *sc;
906         struct tty *tp;
907         int error;
908         int s;
909         int bits;
910
911         USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
912
913         tp = sc->sc_tty;
914
915         if (sc->sc_dying)
916                 return (EIO);
917  
918         DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd));
919
920         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
921         if (error >= 0)
922                 return (error);
923
924 #if defined(__NetBSD__) || defined(__OpenBSD__)
925         error = ttioctl(tp, cmd, data, flag, p);
926 #elif defined(__FreeBSD__)
927         error = ttioctl(tp, cmd, data, flag);
928 #endif
929         if (error >= 0)
930                 return (error);
931
932         DPRINTF(("umodemioctl: our cmd=0x%08lx\n", cmd));
933         s = spltty();
934
935         switch (cmd) {
936         case TIOCSBRK:
937                 umodem_break(sc, 1);
938                 break;
939
940         case TIOCCBRK:
941                 umodem_break(sc, 0);
942                 break;
943
944         case TIOCSDTR:
945                 umodem_modem(sc, 1);
946                 break;
947
948         case TIOCCDTR:
949                 umodem_modem(sc, 0);
950                 break;
951
952         case TIOCMGET:
953                 bits = TIOCM_LE;
954                 if(sc->sc_dtr)
955                         bits |= TIOCM_DTR;
956                 *(int *)data = bits;
957                 break;
958
959         case TIOCMSET:
960                 break;
961
962         case USB_GET_CM_OVER_DATA:
963                 *(int *)data = sc->sc_cm_over_data;
964                 break;
965
966         case USB_SET_CM_OVER_DATA:
967                 if (*(int *)data != sc->sc_cm_over_data) {
968                         /* XXX change it */
969                 }
970                 break;
971
972         default:
973                 DPRINTF(("umodemioctl: unknown\n"));
974                 error = ENOTTY;
975                 splx(s);
976                 return (error);
977         }
978
979         splx(s);
980         return(0);
981 }
982
983 void
984 umodem_shutdown(struct umodem_softc *sc)
985 {
986         struct tty *tp = sc->sc_tty;
987
988         DPRINTF(("%s: umodem_shutdown\n", USBDEVNAME(sc->sc_dev)));
989         /*
990          * Hang up if necessary.  Wait a bit, so the other side has time to
991          * notice even if we immediately open the port again.
992          */
993         if (ISSET(tp->t_cflag, HUPCL)) {
994                 umodem_modem(sc, 0);
995 #if defined(__NetBSD__) || defined(__OpenBSD__)
996                 (void) tsleep(sc, TTIPRI, ttclos, hz);
997 #elif defined(__FreeBSD__)
998                 (void) tsleep(sc, TTIPRI, "umdmsd", hz);
999 #endif
1000         }
1001 }
1002
1003 void
1004 umodem_modem(struct umodem_softc *sc, int onoff)
1005 {
1006         usb_device_request_t req;
1007
1008         DPRINTF(("%s: umodem_modem: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff));
1009
1010         if (sc->sc_dtr == onoff)
1011                 return;
1012
1013         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1014         req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
1015         USETW(req.wValue, onoff ? UCDC_LINE_DTR : 0);
1016         USETW(req.wIndex, sc->sc_ctl_iface_no);
1017         USETW(req.wLength, 0);
1018
1019         (void)usbd_do_request(sc->sc_udev, &req, 0);
1020
1021         sc->sc_dtr = onoff;
1022 }
1023
1024 void
1025 umodem_break(struct umodem_softc *sc, int onoff)
1026 {
1027         usb_device_request_t req;
1028
1029         DPRINTF(("%s: umodem_break: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff));
1030
1031         if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
1032                 return;
1033
1034         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1035         req.bRequest = UCDC_SEND_BREAK;
1036         USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
1037         USETW(req.wIndex, sc->sc_ctl_iface_no);
1038         USETW(req.wLength, 0);
1039
1040         (void)usbd_do_request(sc->sc_udev, &req, 0);
1041 }
1042
1043 void *
1044 umodem_get_desc(usbd_device_handle dev, int type, int subtype)
1045 {
1046         usb_descriptor_t *desc;
1047         usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
1048         uByte *p = (uByte *)cd;
1049         uByte *end = p + UGETW(cd->wTotalLength);
1050
1051         while (p < end) {
1052                 desc = (usb_descriptor_t *)p;
1053                 if (desc->bDescriptorType == type &&
1054                     desc->bDescriptorSubtype == subtype)
1055                         return (desc);
1056                 p += desc->bLength;
1057         }
1058
1059         return (0);
1060 }
1061
1062 usbd_status
1063 umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state)
1064 {
1065         usb_device_request_t req;
1066         usbd_status err;
1067         usb_cdc_abstract_state_t ast;
1068
1069         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1070         req.bRequest = UCDC_SET_COMM_FEATURE;
1071         USETW(req.wValue, feature);
1072         USETW(req.wIndex, sc->sc_ctl_iface_no);
1073         USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
1074         USETW(ast.wState, state);
1075
1076         err = usbd_do_request(sc->sc_udev, &req, &ast);
1077         if (err) {
1078                 DPRINTF(("%s: umodem_set_comm_feat: feature=%d failed, err=%d\n",
1079                          USBDEVNAME(sc->sc_dev), feature, err));
1080                 return (err);
1081         }
1082
1083         return (USBD_NORMAL_COMPLETION);
1084 }
1085
1086 usbd_status
1087 umodem_set_line_coding(struct umodem_softc *sc, usb_cdc_line_state_t *state)
1088 {
1089         usb_device_request_t req;
1090         usbd_status err;
1091
1092         DPRINTF(("%s: umodem_set_line_cod: rate=%d fmt=%d parity=%d bits=%d\n",
1093                  USBDEVNAME(sc->sc_dev), UGETDW(state->dwDTERate),
1094                  state->bCharFormat, state->bParityType, state->bDataBits));
1095
1096         if (bcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
1097                 DPRINTF(("%s: umodem_set_line_coding: already set\n",
1098                         USBDEVNAME(sc->sc_dev)));
1099                 return (USBD_NORMAL_COMPLETION);
1100         }
1101
1102         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1103         req.bRequest = UCDC_SET_LINE_CODING;
1104         USETW(req.wValue, 0);
1105         USETW(req.wIndex, sc->sc_ctl_iface_no);
1106         USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
1107
1108         err = usbd_do_request(sc->sc_udev, &req, state);
1109         if (err) {
1110                 DPRINTF(("%s: umodem_set_line_coding: failed, err=%d\n",
1111                         USBDEVNAME(sc->sc_dev), err));
1112                 return (err);
1113         }
1114
1115         sc->sc_line_state = *state;
1116
1117         return (USBD_NORMAL_COMPLETION);
1118 }
1119
1120 #if defined(__NetBSD__) || defined(__OpenBSD__)
1121 int
1122 umodem_activate(device_ptr_t self, enum devact act)
1123 {
1124         struct umodem_softc *sc = (struct umodem_softc *)self;
1125
1126         switch (act) {
1127         case DVACT_ACTIVATE:
1128                 return (EOPNOTSUPP);
1129                 break;
1130
1131         case DVACT_DEACTIVATE:
1132                 sc->sc_dying = 1;
1133                 break;
1134         }
1135         return (0);
1136 }
1137 #endif
1138
1139 USB_DETACH(umodem)
1140 {
1141         USB_DETACH_START(umodem, sc);
1142 #if defined(__NetBSD__) || defined(__OpenBSD__)
1143         int maj, mn;
1144
1145         DPRINTF(("umodem_detach: sc=%p flags=%d tp=%p\n", 
1146                  sc, flags, sc->sc_tty));
1147 #elif defined(__FreeBSD__)
1148         DPRINTF(("umodem_detach: sc=%p flags=%d, tp=%p\n", 
1149                  sc, 0,  sc->sc_tty));
1150 #endif
1151
1152         sc->sc_dying = 1;
1153
1154 #ifdef DIAGNOSTIC
1155         if (sc->sc_tty == 0) {
1156                 DPRINTF(("umodem_detach: no tty\n"));
1157                 return (0);
1158         }
1159 #endif
1160
1161         /* use refernce count? XXX */
1162
1163 #if defined(__NetBSD__) || defined(__OpenBSD__)
1164         /* locate the major number */
1165         for (maj = 0; maj < nchrdev; maj++)
1166                 if (cdevsw[maj].d_open == umodemopen)
1167                         break;
1168
1169         /* Nuke the vnodes for any open instances. */
1170         mn = self->dv_unit;
1171         vdevgone(maj, mn, mn, VCHR);
1172         vdevgone(maj, mn, mn | UMODEMDIALOUT_MASK, VCHR);
1173         vdevgone(maj, mn, mn | UMODEMCALLUNIT_MASK, VCHR);
1174 #elif defined(__FreeBSD__)
1175         /* XXX not yet implemented */
1176 #endif
1177
1178 #if defined(__FreeBSD__)
1179         destroy_dev(sc->dev);
1180 #endif
1181
1182         /* Detach and free the tty. */
1183 #if defined(__NetBSD__) || defined(__OpenBSD__)
1184         tty_detach(sc->sc_tty);
1185         ttyfree(sc->sc_tty);
1186         sc->sc_tty = 0;
1187 #endif
1188
1189         return (0);
1190 }
1191
1192 #if defined(__FreeBSD__)
1193 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0);
1194 #endif