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