Add USB modem driver stubs; from NetBSD. Not yet tested.
authoraaron <aaron@openbsd.org>
Wed, 5 Apr 2000 00:29:13 +0000 (00:29 +0000)
committeraaron <aaron@openbsd.org>
Wed, 5 Apr 2000 00:29:13 +0000 (00:29 +0000)
sys/arch/i386/conf/GENERIC
sys/dev/usb/files.usb
sys/dev/usb/ucom.c [new file with mode: 0644]
sys/dev/usb/ucomvar.h [new file with mode: 0644]
sys/dev/usb/umodem.c [new file with mode: 0644]
sys/dev/usb/usb_port.h

index edf2ce6..96b4f75 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.172 2000/04/04 22:51:24 aaron Exp $
+#      $OpenBSD: GENERIC,v 1.173 2000/04/05 00:29:13 aaron Exp $
 #      $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $
 #
 #      GENERIC -- everything that's currently supported
@@ -87,6 +87,10 @@ pcmcia*      at pcic? controller ? socket ?
 #atapiscsi* at umass?
 #scsibus* at umass?
 
+# USB Modems
+#umodem*       at uhub? port ? configuration ?
+#ucom* at umodem?
+
 # USB Audio
 #uaudio* at uhub? port ? configuration ?
 
index b0e02f8..a4d1fae 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.usb,v 1.9 2000/04/04 22:51:23 aaron Exp $
+#      $OpenBSD: files.usb,v 1.10 2000/04/05 00:29:14 aaron Exp $
 #      $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
 #
 # Config file and device description for machine-independent USB code.
@@ -23,11 +23,19 @@ file        dev/usb/uhub.c                  usb
 
 attach uhub at uhub with uhub_uhub
 
+# Modem and com serial port "bus"
+define ucombus {[ portno = -1 ]}
+
 # Audio devices
 device uaudio: audio, auconv, mulaw
 attach uaudio at uhub
 file   dev/usb/uaudio.c                uaudio
 
+# Modem and com serial port
+device ucom
+attach ucom at ucombus
+file   dev/usb/ucom.c                  ucom | ucombus  needs-flag
+
 # Generic devices
 device ugen
 attach ugen at uhub
@@ -48,6 +56,11 @@ device       umass: scsi, atapi
 attach umass at uhub
 file   dev/usb/umass.c                 umass
 
+# Modems
+device umodem: ucombus
+attach umodem at uhub
+file   dev/usb/umodem.c                umodem
+
 # Ethernet adapters
 # ADMtek AN986 Pegasus
 device aue: ether, ifnet, mii, ifmedia
diff --git a/sys/dev/usb/ucom.c b/sys/dev/usb/ucom.c
new file mode 100644 (file)
index 0000000..63c547f
--- /dev/null
@@ -0,0 +1,1045 @@
+/*     $NetBSD: ucom.c,v 1.16 2000/03/27 12:33:55 augustss Exp $       */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#include "ucom.h"
+
+#if NUCOM > 0
+
+#ifdef UCOM_DEBUG
+#define DPRINTFN(n, x) if (ucomdebug > (n)) logprintf x
+int ucomdebug = 0;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define        UCOMUNIT_MASK           0x3ffff
+#define        UCOMDIALOUT_MASK        0x80000
+#define        UCOMCALLUNIT_MASK       0x40000
+
+#define        UCOMUNIT(x)             (minor(x) & UCOMUNIT_MASK)
+#define        UCOMDIALOUT(x)          (minor(x) & UCOMDIALOUT_MASK)
+#define        UCOMCALLUNIT(x)         (minor(x) & UCOMCALLUNIT_MASK)
+
+/* 
+ * These are the maximum number of bytes transferred per frame.
+ * If some really high speed devices should use this driver they
+ * may need to be increased, but this is good enough for modems.
+ */
+#define UCOMIBUFSIZE 64
+#define UCOMOBUFSIZE 256
+
+struct ucom_softc {
+       USBBASEDEVICE           sc_dev;         /* base device */
+
+       usbd_device_handle      sc_udev;        /* USB device */
+
+       usbd_interface_handle   sc_iface;       /* data interface */
+
+       int                     sc_bulkin_no;   /* bulk in endpoint address */
+       usbd_pipe_handle        sc_bulkin_pipe; /* bulk in pipe */
+       usbd_xfer_handle        sc_ixfer;       /* read request */
+       u_char                  *sc_ibuf;       /* read buffer */
+
+       int                     sc_bulkout_no;  /* bulk out endpoint address */
+       usbd_pipe_handle        sc_bulkout_pipe;/* bulk out pipe */
+       usbd_xfer_handle        sc_oxfer;       /* write request */
+       u_char                  *sc_obuf;       /* write buffer */
+
+       struct ucom_methods     *sc_methods;
+       void                    *sc_parent;
+       int                     sc_portno;
+
+       struct tty              *sc_tty;        /* our tty */
+       u_char                  sc_lsr;
+       u_char                  sc_msr;
+       u_char                  sc_mcr;
+       u_char                  sc_tx_stopped;
+       int                     sc_swflags;
+
+       u_char                  sc_opening;     /* lock during open */
+       u_char                  sc_dying;       /* disconnecting */
+};
+
+cdev_decl(ucom);
+
+Static void    ucom_cleanup    __P((struct ucom_softc *));
+Static void    ucom_hwiflow    __P((struct ucom_softc *));
+Static int     ucomparam       __P((struct tty *, struct termios *));
+Static void    ucomstart       __P((struct tty *));
+Static void    ucom_shutdown   __P((struct ucom_softc *));
+Static void    ucom_dtr        __P((struct ucom_softc *, int));
+Static void    ucom_rts        __P((struct ucom_softc *, int));
+Static void    ucom_break      __P((struct ucom_softc *, int));
+Static usbd_status ucomstartread __P((struct ucom_softc *));
+Static void    ucomreadcb      __P((usbd_xfer_handle, usbd_private_handle, 
+                                    usbd_status status));
+Static void    ucomwritecb     __P((usbd_xfer_handle, usbd_private_handle, 
+                                    usbd_status status));
+Static void    tiocm_to_ucom   __P((struct ucom_softc *, int, int));
+Static int     ucom_to_tiocm   __P((struct ucom_softc *));
+
+USB_DECLARE_DRIVER(ucom);
+
+USB_MATCH(ucom)
+{
+       return (1);
+}
+
+USB_ATTACH(ucom)
+{
+       struct ucom_softc *sc = (struct ucom_softc *)self;
+       struct ucom_attach_args *uca = aux;
+       struct tty *tp;
+
+       if (uca->portno != UCOM_UNK_PORTNO)
+               printf(": portno %d", uca->portno);
+       printf("\n");
+
+       sc->sc_udev = uca->device;
+       sc->sc_iface = uca->iface;
+       sc->sc_bulkout_no = uca->bulkout;
+       sc->sc_bulkin_no = uca->bulkin;
+       sc->sc_methods = uca->methods;
+       sc->sc_parent = uca->arg;
+       sc->sc_portno = uca->portno;
+
+       tp = ttymalloc();
+       tp->t_oproc = ucomstart;
+       tp->t_param = ucomparam;
+       sc->sc_tty = tp;
+
+       DPRINTF(("ucom_attach: tty_attach %p\n", tp));
+       tty_attach(tp);
+
+       USB_ATTACH_SUCCESS_RETURN;
+}
+
+USB_DETACH(ucom)
+{
+       struct ucom_softc *sc = (struct ucom_softc *)self;
+       int maj, mn;
+
+       DPRINTF(("ucom_detach: sc=%p flags=%d tp=%p\n", 
+                sc, flags, sc->sc_tty));
+
+       sc->sc_dying = 1;
+
+#ifdef DIAGNOSTIC
+       if (sc->sc_tty == NULL) {
+               DPRINTF(("ucom_detach: no tty\n"));
+               return (0);
+       }
+#endif
+
+       /* XXX  Use reference count? */
+
+       /* locate the major number */
+       for (maj = 0; maj < nchrdev; maj++)
+               if (cdevsw[maj].d_open == ucomopen)
+                       break;
+
+       /* Nuke the vnodes for any open instances. */
+       mn = self->dv_unit;
+       vdevgone(maj, mn, mn, VCHR);
+       vdevgone(maj, mn, mn | UCOMDIALOUT_MASK, VCHR);
+       vdevgone(maj, mn, mn | UCOMCALLUNIT_MASK, VCHR);
+
+       /* Detach and free the tty. */
+       tty_detach(sc->sc_tty);
+       ttyfree(sc->sc_tty);
+       sc->sc_tty = 0;
+
+       return (0);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+ucom_activate(self, act)
+       device_ptr_t self;
+       enum devact act;
+{
+       struct ucom_softc *sc = (struct ucom_softc *)self;
+
+       switch (act) {
+       case DVACT_ACTIVATE:
+               return (EOPNOTSUPP);
+               break;
+
+       case DVACT_DEACTIVATE:
+               sc->sc_dying = 1;
+               break;
+       }
+       return (0);
+}
+#endif
+
+void
+ucom_shutdown(sc)
+       struct ucom_softc *sc;
+{
+       struct tty *tp = sc->sc_tty;
+
+       DPRINTF(("ucom_shutdown\n"));
+       /*
+        * Hang up if necessary.  Wait a bit, so the other side has time to
+        * notice even if we immediately open the port again.
+        */
+       if (ISSET(tp->t_cflag, HUPCL)) {
+               ucom_dtr(sc, 0);
+               (void)tsleep(sc, TTIPRI, ttclos, hz);
+       }
+}
+
+int
+ucomopen(dev, flag, mode, p)
+       dev_t dev;
+       int flag, mode;
+       struct proc *p;
+{
+       int unit = UCOMUNIT(dev);
+       usbd_status err;
+       struct ucom_softc *sc;
+       struct tty *tp;
+       int s;
+       int error;
+       if (unit >= ucom_cd.cd_ndevs)
+               return (ENXIO);
+       sc = ucom_cd.cd_devs[unit];
+       if (sc == NULL)
+               return (ENXIO);
+
+       if (sc->sc_dying)
+               return (EIO);
+
+       if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
+               return (ENXIO);
+
+       tp = sc->sc_tty;
+
+       DPRINTF(("ucomopen: unit=%d, tp=%p\n", unit, tp));
+
+       if (ISSET(tp->t_state, TS_ISOPEN) &&
+           ISSET(tp->t_state, TS_XCLUDE) &&
+           p->p_ucred->cr_uid != 0)
+               return (EBUSY);
+
+       s = spltty();
+
+       /*
+        * Do the following iff this is a first open.
+        */
+       while (sc->sc_opening)
+               tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0);
+       sc->sc_opening = 1;
+       
+#if defined(__NetBSD__)
+       if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+#else
+       if (!ISSET(tp->t_state, TS_ISOPEN)) {
+#endif
+               struct termios t;
+
+               tp->t_dev = dev;
+
+               ucom_status_change(sc);
+
+               /*
+                * Initialize the termios status to the defaults.  Add in the
+                * sticky bits from TIOCSFLAGS.
+                */
+               t.c_ispeed = 0;
+               t.c_ospeed = TTYDEF_SPEED;
+               t.c_cflag = TTYDEF_CFLAG;
+               if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
+                       SET(t.c_cflag, CLOCAL);
+               if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
+                       SET(t.c_cflag, CRTSCTS);
+               if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
+                       SET(t.c_cflag, MDMBUF);
+               /* Make sure ucomparam() will do something. */
+               tp->t_ospeed = 0;
+               (void) ucomparam(tp, &t);
+               tp->t_iflag = TTYDEF_IFLAG;
+               tp->t_oflag = TTYDEF_OFLAG;
+               tp->t_lflag = TTYDEF_LFLAG;
+               ttychars(tp);
+               ttsetwater(tp);
+
+               /*
+                * Turn on DTR.  We must always do this, even if carrier is not
+                * present, because otherwise we'd have to use TIOCSDTR
+                * immediately after setting CLOCAL, which applications do not
+                * expect.  We always assert DTR while the device is open
+                * unless explicitly requested to deassert it.
+                */
+               ucom_dtr(sc, 1);
+
+               // XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
+               ucom_hwiflow(sc);
+
+               DPRINTF(("ucomopen: open pipes in=%d out=%d\n",
+                        sc->sc_bulkin_no, sc->sc_bulkout_no));
+
+               /* Open the bulk pipes */
+               err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
+                                    &sc->sc_bulkin_pipe);
+               if (err) {
+                       DPRINTF(("%s: open bulk out error (addr %d), err=%s\n",
+                                USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no, 
+                                usbd_errstr(err)));
+                       return (EIO);
+               }
+               err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
+                                    USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+               if (err) {
+                       DPRINTF(("%s: open bulk in error (addr %d), err=%s\n",
+                                USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no,
+                                usbd_errstr(err)));
+                       usbd_close_pipe(sc->sc_bulkin_pipe);
+                       return (EIO);
+               }
+               
+               /* Allocate a request and an input buffer and start reading. */
+               sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
+               if (sc->sc_ixfer == NULL) {
+                       usbd_close_pipe(sc->sc_bulkin_pipe);
+                       usbd_close_pipe(sc->sc_bulkout_pipe);
+                       return (ENOMEM);
+               }
+               sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer, UCOMIBUFSIZE);
+               if (sc->sc_ibuf == NULL) {
+                       usbd_free_xfer(sc->sc_ixfer);
+                       usbd_close_pipe(sc->sc_bulkin_pipe);
+                       usbd_close_pipe(sc->sc_bulkout_pipe);
+                       return (ENOMEM);
+               }
+
+               sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
+               if (sc->sc_oxfer == NULL) {
+                       usbd_free_xfer(sc->sc_ixfer);
+                       usbd_close_pipe(sc->sc_bulkin_pipe);
+                       usbd_close_pipe(sc->sc_bulkout_pipe);
+                       return (ENOMEM);
+               }
+               sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer, UCOMOBUFSIZE);
+               if (sc->sc_obuf == NULL) {
+                       usbd_free_xfer(sc->sc_oxfer);
+                       usbd_free_xfer(sc->sc_ixfer);
+                       usbd_close_pipe(sc->sc_bulkin_pipe);
+                       usbd_close_pipe(sc->sc_bulkout_pipe);
+                       return (ENOMEM);
+               }
+
+               if (sc->sc_methods->ucom_open != NULL)
+                       sc->sc_methods->ucom_open(sc->sc_parent, sc->sc_portno);
+
+               ucomstartread(sc);
+       }
+       sc->sc_opening = 0;
+       wakeup(&sc->sc_opening);
+       splx(s);
+
+#if defined(__NetBSD__)
+       error = ttyopen(tp, UCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
+#else
+       error = ttyopen(UCOMDIALOUT(dev), tp);
+#endif
+       if (error)
+               goto bad;
+
+       error = (*linesw[tp->t_line].l_open)(dev, tp);
+       if (error)
+               goto bad;
+
+       return (0);
+
+bad:
+#if defined(__NetBSD__)
+       if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+#else
+       if (!ISSET(tp->t_state, TS_ISOPEN)) {
+#endif
+               /*
+                * We failed to open the device, and nobody else had it opened.
+                * Clean up the state as appropriate.
+                */
+               ucom_cleanup(sc);
+       }
+
+       return (error);
+}
+
+int
+ucomclose(dev, flag, mode, p)
+       dev_t dev;
+       int flag, mode;
+       struct proc *p;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+       struct tty *tp = sc->sc_tty;
+
+       DPRINTF(("ucomclose: unit=%d\n", UCOMUNIT(dev)));
+       if (!ISSET(tp->t_state, TS_ISOPEN))
+               return (0);
+
+       (*linesw[tp->t_line].l_close)(tp, flag);
+       ttyclose(tp);
+
+       if (sc->sc_dying)
+               return (0);
+
+#if defined(__NetBSD__)
+       if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+#else
+       if (!ISSET(tp->t_state, TS_ISOPEN)) {
+#endif
+               /*
+                * Although we got a last close, the device may still be in
+                * use; e.g. if this was the dialout node, and there are still
+                * processes waiting for carrier on the non-dialout node.
+                */
+               ucom_cleanup(sc);
+       }
+
+       if (sc->sc_methods->ucom_close != NULL)
+               sc->sc_methods->ucom_close(sc->sc_parent, sc->sc_portno);
+
+       return (0);
+}
+int
+ucomread(dev, uio, flag)
+       dev_t dev;
+       struct uio *uio;
+       int flag;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+       struct tty *tp = sc->sc_tty;
+
+       if (sc->sc_dying)
+               return (EIO);
+       return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+int
+ucomwrite(dev, uio, flag)
+       dev_t dev;
+       struct uio *uio;
+       int flag;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+       struct tty *tp = sc->sc_tty;
+
+       if (sc->sc_dying)
+               return (EIO);
+       return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+struct tty *
+ucomtty(dev)
+       dev_t dev;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+       struct tty *tp = sc->sc_tty;
+
+       return (tp);
+}
+
+int
+ucomioctl(dev, cmd, data, flag, p)
+       dev_t dev;
+       u_long cmd;
+       caddr_t data;
+       int flag;
+       struct proc *p;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
+       struct tty *tp = sc->sc_tty;
+       int error;
+       int s;
+
+       if (sc->sc_dying)
+               return (EIO);
+       DPRINTF(("ucomioctl: cmd=0x%08lx\n", cmd));
+
+       error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+       if (error >= 0)
+               return (error);
+
+       error = ttioctl(tp, cmd, data, flag, p);
+       if (error >= 0)
+               return (error);
+
+       if (sc->sc_methods->ucom_ioctl != NULL) {
+               error = sc->sc_methods->ucom_ioctl(sc->sc_parent,
+                           sc->sc_portno, cmd, data, flag, p);
+               if (error >= 0)
+                       return (error);
+       }
+
+       error = 0;
+
+       DPRINTF(("ucomioctl: our cmd=0x%08lx\n", cmd));
+       s = spltty();
+
+       switch (cmd) {
+       case TIOCSBRK:
+               ucom_break(sc, 1);
+               break;
+
+       case TIOCCBRK:
+               ucom_break(sc, 0);
+               break;
+
+       case TIOCSDTR:
+               ucom_dtr(sc, 1);
+               break;
+
+       case TIOCCDTR:
+               ucom_dtr(sc, 0);
+               break;
+
+       case TIOCGFLAGS:
+               *(int *)data = sc->sc_swflags;
+               break;
+
+       case TIOCSFLAGS:
+               error = suser(p->p_ucred, &p->p_acflag); 
+               if (error)
+                       break;
+               sc->sc_swflags = *(int *)data;
+               break;
+
+       case TIOCMSET:
+       case TIOCMBIS:
+       case TIOCMBIC:
+               tiocm_to_ucom(sc, cmd, *(int *)data);
+               break;
+
+       case TIOCMGET:
+               *(int *)data = ucom_to_tiocm(sc);
+               break;
+
+       default:
+               error = ENOTTY;
+               break;
+       }
+
+       splx(s);
+
+       return (error);
+}
+
+void
+tiocm_to_ucom(sc, how, ttybits)
+       struct ucom_softc *sc;
+       int how, ttybits;
+{
+       u_char combits;
+
+       combits = 0;
+       if (ISSET(ttybits, TIOCM_DTR))
+               SET(combits, UMCR_DTR);
+       if (ISSET(ttybits, TIOCM_RTS))
+               SET(combits, UMCR_RTS);
+       switch (how) {
+       case TIOCMBIC:
+               CLR(sc->sc_mcr, combits);
+               break;
+
+       case TIOCMBIS:
+               SET(sc->sc_mcr, combits);
+               break;
+
+       case TIOCMSET:
+               CLR(sc->sc_mcr, UMCR_DTR | UMCR_RTS);
+               SET(sc->sc_mcr, combits);
+               break;
+       }
+
+       ucom_dtr(sc, (sc->sc_mcr & UMCR_DTR) != 0);
+       ucom_rts(sc, (sc->sc_mcr & UMCR_RTS) != 0);
+}
+
+int
+ucom_to_tiocm(sc)
+       struct ucom_softc *sc;
+{
+       u_char combits;
+       int ttybits = 0;
+
+       combits = sc->sc_mcr;
+       if (ISSET(combits, UMCR_DTR))
+               SET(ttybits, TIOCM_DTR);
+       if (ISSET(combits, UMCR_RTS))
+               SET(ttybits, TIOCM_RTS);
+
+       combits = sc->sc_msr;
+       if (ISSET(combits, UMSR_DCD))
+               SET(ttybits, TIOCM_CD);
+       if (ISSET(combits, UMSR_CTS))
+               SET(ttybits, TIOCM_CTS);
+       if (ISSET(combits, UMSR_DSR))
+               SET(ttybits, TIOCM_DSR);
+       if (ISSET(combits, UMSR_RI | UMSR_TERI))
+               SET(ttybits, TIOCM_RI);
+
+#if 0
+XXX;
+       if (sc->sc_ier != 0)
+               SET(ttybits, TIOCM_LE);
+#endif
+
+       return (ttybits);
+}
+
+void
+ucom_break(sc, onoff)
+       struct ucom_softc *sc;
+       int onoff;
+{
+       DPRINTF(("ucom_break: onoff=%d\n", onoff));
+
+       if (sc->sc_methods->ucom_set != NULL)
+               sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
+                   UCOM_SET_BREAK, onoff);
+}
+
+void
+ucom_dtr(sc, onoff)
+       struct ucom_softc *sc;
+       int onoff;
+{
+       DPRINTF(("ucom_dtr: onoff=%d\n", onoff));
+
+       if (sc->sc_methods->ucom_set != NULL)
+               sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno, 
+                   UCOM_SET_DTR, onoff);
+}
+
+void
+ucom_rts(sc, onoff)
+       struct ucom_softc *sc;
+       int onoff;
+{
+       DPRINTF(("ucom_rts: onoff=%d\n", onoff));
+
+       if (sc->sc_methods->ucom_set != NULL)
+               sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno, 
+                   UCOM_SET_RTS, onoff);
+}
+
+void
+ucom_status_change(sc)
+       struct ucom_softc *sc;
+{
+       if (sc->sc_methods->ucom_get_status != NULL) {
+               sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno,
+                   &sc->sc_lsr, &sc->sc_msr);
+       } else {
+               sc->sc_lsr = 0;
+               sc->sc_msr = 0;
+       }
+}
+
+int
+ucomparam(tp, t)
+       struct tty *tp;
+       struct termios *t;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
+       int error;
+
+       if (sc->sc_dying)
+               return (EIO);
+
+       /* Check requested parameters. */
+       if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
+               return (EINVAL);
+
+       /*
+        * For the console, always force CLOCAL and !HUPCL, so that the port
+        * is always active.
+        */
+       if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) {
+               SET(t->c_cflag, CLOCAL);
+               CLR(t->c_cflag, HUPCL);
+       }
+
+       /*
+        * If there were no changes, don't do anything.  This avoids dropping
+        * input and improves performance when all we did was frob things like
+        * VMIN and VTIME.
+        */
+       if (tp->t_ospeed == t->c_ospeed &&
+           tp->t_cflag == t->c_cflag)
+               return (0);
+
+       //XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag);
+
+       /* And copy to tty. */
+       tp->t_ispeed = 0;
+       tp->t_ospeed = t->c_ospeed;
+       tp->t_cflag = t->c_cflag;
+
+       if (sc->sc_methods->ucom_param != NULL) {
+               error = sc->sc_methods->ucom_param(sc->sc_parent, sc->sc_portno,
+                           t);
+               if (error)
+                       return (error);
+       }
+
+       // XXX worry about CHWFLOW
+
+       /*
+        * Update the tty layer's idea of the carrier bit, in case we changed
+        * CLOCAL or MDMBUF.  We don't hang up here; we only do that by
+        * explicit request.
+        */
+       DPRINTF(("ucomparam: l_modem\n"));
+       (void) (*linesw[tp->t_line].l_modem)(tp, 1 /* XXX carrier */ );
+
+#if 0
+XXX what if the hardware is not open
+       if (!ISSET(t->c_cflag, CHWFLOW)) {
+               if (sc->sc_tx_stopped) {
+                       sc->sc_tx_stopped = 0;
+                       ucomstart(tp);
+               }
+       }
+#endif
+
+       return (0);
+}
+
+/*
+ * (un)block input via hw flowcontrol
+ */
+void
+ucom_hwiflow(sc)
+       struct ucom_softc *sc;
+{
+#if 0
+XXX
+       bus_space_tag_t iot = sc->sc_iot;
+       bus_space_handle_t ioh = sc->sc_ioh;
+
+       if (sc->sc_mcr_rts == 0)
+               return;
+
+       if (ISSET(sc->sc_rx_flags, RX_ANY_BLOCK)) {
+               CLR(sc->sc_mcr, sc->sc_mcr_rts);
+               CLR(sc->sc_mcr_active, sc->sc_mcr_rts);
+       } else {
+               SET(sc->sc_mcr, sc->sc_mcr_rts);
+               SET(sc->sc_mcr_active, sc->sc_mcr_rts);
+       }
+       bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active);
+#endif
+}
+
+void
+ucomstart(tp)
+       struct tty *tp;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
+       usbd_status err;
+       int s;
+       u_char *data;
+       int cnt;
+
+       if (sc->sc_dying)
+               return;
+
+       s = spltty();
+       if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
+               DPRINTFN(4,("ucomstart: stopped\n"));
+               goto out;
+       }
+       if (sc->sc_tx_stopped)
+               goto out;
+
+       if (tp->t_outq.c_cc <= tp->t_lowat) {
+               if (ISSET(tp->t_state, TS_ASLEEP)) {
+                       CLR(tp->t_state, TS_ASLEEP);
+                       wakeup(&tp->t_outq);
+               }
+               selwakeup(&tp->t_wsel);
+               if (tp->t_outq.c_cc == 0)
+                       goto out;
+       }
+
+       /* Grab the first contiguous region of buffer space. */
+       data = tp->t_outq.c_cf;
+       cnt = ndqb(&tp->t_outq, 0);
+
+       if (cnt == 0) {
+               DPRINTF(("ucomstart: cnt==0\n"));
+               goto out;
+       }
+
+       SET(tp->t_state, TS_BUSY);
+
+       if (cnt > UCOMOBUFSIZE) {
+               DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
+               cnt = UCOMOBUFSIZE;
+       }
+       memcpy(sc->sc_obuf, data, cnt);
+
+       DPRINTFN(4,("ucomstart: %d chars\n", cnt));
+       usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, 
+                       (usbd_private_handle)sc, sc->sc_obuf, cnt,
+                       USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
+       /* What can we do on error? */
+       err = usbd_transfer(sc->sc_oxfer);
+#ifdef DIAGNOSTIC
+       if (err != USBD_IN_PROGRESS)
+               printf("ucomstart: err=%s\n", usbd_errstr(err));
+#endif
+
+out:
+       splx(s);
+}
+
+#if defined(__NetBSD__)
+void
+#else
+int
+#endif
+ucomstop(tp, flag)
+       struct tty *tp;
+       int flag;
+{
+       struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
+       int s;
+
+       DPRINTF(("ucomstop: %d\n", flag));
+       s = spltty();
+       if (ISSET(tp->t_state, TS_BUSY)) {
+               DPRINTF(("ucomstop: XXX\n"));
+               sc->sc_tx_stopped = 1;
+               if (!ISSET(tp->t_state, TS_TTSTOP))
+                       SET(tp->t_state, TS_FLUSH);
+       }
+       splx(s);
+
+#if !defined(__NetBSD__)
+       return 0;
+#endif
+}
+
+void
+ucomwritecb(xfer, p, status)
+       usbd_xfer_handle xfer;
+       usbd_private_handle p;
+       usbd_status status;
+{
+       struct ucom_softc *sc = (struct ucom_softc *)p;
+       struct tty *tp = sc->sc_tty;
+       u_int32_t cc;
+       int s;
+
+       DPRINTFN(5,("ucomwritecb: status=%d\n", status));
+
+       if (status == USBD_CANCELLED)
+               return;
+
+       if (status) {
+               DPRINTF(("ucomwritecb: status=%d\n", status));
+               usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+               /* XXX we should restart after some delay. */
+               return;
+       }
+
+       usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+       DPRINTFN(5,("ucomwritecb: cc=%d\n", cc));
+
+       s = spltty();
+       CLR(tp->t_state, TS_BUSY);
+       if (ISSET(tp->t_state, TS_FLUSH))
+               CLR(tp->t_state, TS_FLUSH);
+       else
+               ndflush(&tp->t_outq, cc);
+       (*linesw[tp->t_line].l_start)(tp);
+       splx(s);
+}
+
+usbd_status
+ucomstartread(sc)
+       struct ucom_softc *sc;
+{
+       usbd_status err;
+
+       DPRINTFN(5,("ucomstartread: start\n"));
+       usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, 
+                       (usbd_private_handle)sc, 
+                       sc->sc_ibuf,  UCOMIBUFSIZE,
+                       USBD_SHORT_XFER_OK | USBD_NO_COPY,
+                       USBD_NO_TIMEOUT, ucomreadcb);
+       err = usbd_transfer(sc->sc_ixfer);
+       if (err != USBD_IN_PROGRESS) {
+               DPRINTF(("ucomstartread: err=%s\n", usbd_errstr(err)));
+               return (err);
+       }
+       return (USBD_NORMAL_COMPLETION);
+}
+void
+ucomreadcb(xfer, p, status)
+       usbd_xfer_handle xfer;
+       usbd_private_handle p;
+       usbd_status status;
+{
+       struct ucom_softc *sc = (struct ucom_softc *)p;
+       struct tty *tp = sc->sc_tty;
+       int (*rint) __P((int c, struct tty *tp)) = linesw[tp->t_line].l_rint;
+       usbd_status err;
+       u_int32_t cc;
+       u_char *cp;
+       int s;
+
+       if (status == USBD_CANCELLED)
+               return;
+
+       if (status) {
+               DPRINTF(("ucomreadcb: status=%d\n", status));
+               usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+               /* XXX we should restart after some delay. */
+               return;
+       }
+
+       usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
+       DPRINTFN(5,("ucomreadcb: got %d chars, tp=%p\n", cc, tp));
+       s = spltty();
+       /* Give characters to tty layer. */
+       while (cc-- > 0) {
+               DPRINTFN(7,("ucomreadcb: char=0x%02x\n", *cp));
+               if ((*rint)(*cp++, tp) == -1) {
+                       /* XXX what should we do? */
+                       break;
+               }
+       }
+       splx(s);
+
+       err = ucomstartread(sc);
+       if (err) {
+               printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
+               /* XXX what should we dow now? */
+       }
+}
+
+void
+ucom_cleanup(sc)
+       struct ucom_softc *sc;
+{
+       DPRINTF(("ucom_cleanup: closing pipes\n"));
+
+       ucom_shutdown(sc);
+       usbd_abort_pipe(sc->sc_bulkin_pipe);
+       usbd_close_pipe(sc->sc_bulkin_pipe);
+       usbd_abort_pipe(sc->sc_bulkout_pipe);
+       usbd_close_pipe(sc->sc_bulkout_pipe);
+       usbd_free_xfer(sc->sc_ixfer);
+       usbd_free_xfer(sc->sc_oxfer);
+}
+
+#endif /* NUCOM > 0 */
+
+int
+ucomprint(aux, pnp)
+       void *aux;
+       const char *pnp;
+{
+
+       if (pnp)
+               printf("ucom at %s\n", pnp);
+       return (UNCONF);
+}
+
+int
+#if defined(__OpenBSD__)
+ucomsubmatch(parent, match, aux)
+       struct device *parent;
+       void *match;
+       void *aux;
+{
+       struct cfdata *cf = match;
+#else
+ucomsubmatch(parent, cf, aux)
+       struct device *parent;
+       struct cfdata *cf;
+       void *aux;
+{
+#endif
+       struct ucom_attach_args *uca = aux;
+
+       if (uca->portno != UCOM_UNK_PORTNO &&
+           cf->ucomcf_portno != UCOM_UNK_PORTNO &&
+           cf->ucomcf_portno != uca->portno)
+               return (0);
+       return ((*cf->cf_attach->ca_match)(parent, cf, aux));
+}
diff --git a/sys/dev/usb/ucomvar.h b/sys/dev/usb/ucomvar.h
new file mode 100644 (file)
index 0000000..6aaff40
--- /dev/null
@@ -0,0 +1,111 @@
+/*     $NetBSD: ucomvar.h,v 1.2 2000/02/08 09:18:02 augustss Exp $     */
+
+/*
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f)       (t) |= (f)
+#define CLR(t, f)       (t) &= ~(f)
+#define ISSET(t, f)     ((t) & (f))
+
+#if !defined(__OpenBSD__)
+#include "locators.h"
+#endif
+
+#define ucomcf_portno cf_loc[UCOMBUSCF_PORTNO]
+#define UCOM_UNK_PORTNO UCOMBUSCF_PORTNO_DEFAULT
+
+struct ucom_softc;
+
+struct ucom_methods {
+       void (*ucom_get_status)__P((void *sc, int portno, 
+                                   u_char *lsr, u_char *msr));
+       void (*ucom_set)__P((void *sc, int portno, int reg, int onoff));
+#define UCOM_SET_DTR 1
+#define UCOM_SET_RTS 2
+#define UCOM_SET_BREAK 3
+       int (*ucom_param)__P((void *sc, int portno, struct termios *));
+       int (*ucom_ioctl)__P((void *sc, int portno, u_long cmd, 
+                             caddr_t data, int flag, struct proc *p));
+       void (*ucom_open)__P((void *sc, int portno));
+       void (*ucom_close)__P((void *sc, int portno));
+};
+
+/* modem control register */
+#define        UMCR_RTS        0x02    /* Request To Send */
+#define        UMCR_DTR        0x01    /* Data Terminal Ready */
+
+/* line status register */
+#define        ULSR_RCV_FIFO   0x80
+#define        ULSR_TSRE       0x40    /* Transmitter empty: byte sent */
+#define        ULSR_TXRDY      0x20    /* Transmitter buffer empty */
+#define        ULSR_BI         0x10    /* Break detected */
+#define        ULSR_FE         0x08    /* Framing error: bad stop bit */
+#define        ULSR_PE         0x04    /* Parity error */
+#define        ULSR_OE         0x02    /* Overrun, lost incoming byte */
+#define        ULSR_RXRDY      0x01    /* Byte ready in Receive Buffer */
+#define        ULSR_RCV_MASK   0x1f    /* Mask for incoming data or error */
+
+/* modem status register */
+/* All deltas are from the last read of the MSR. */
+#define        UMSR_DCD        0x80    /* Current Data Carrier Detect */
+#define        UMSR_RI         0x40    /* Current Ring Indicator */
+#define        UMSR_DSR        0x20    /* Current Data Set Ready */
+#define        UMSR_CTS        0x10    /* Current Clear to Send */
+#define        UMSR_DDCD       0x08    /* DCD has changed state */
+#define        UMSR_TERI       0x04    /* RI has toggled low to high */
+#define        UMSR_DDSR       0x02    /* DSR has changed state */
+#define        UMSR_DCTS       0x01    /* CTS has changed state */
+
+struct ucom_attach_args {
+       int portno;
+       int bulkin;
+       int bulkout;
+       usbd_device_handle device;
+       usbd_interface_handle iface;
+       struct ucom_methods *methods;
+       void *arg;
+};
+
+int ucomprint __P((void *aux, const char *pnp));
+#if defined(__OpenBSD__)
+int ucomsubmatch __P((struct device *parent, void *cf, void *aux));
+#else
+int ucomsubmatch __P((struct device *parent, struct cfdata *cf, void *aux));
+#endif
+void ucom_status_change __P((struct ucom_softc *sc));
diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c
new file mode 100644 (file)
index 0000000..f65bd4f
--- /dev/null
@@ -0,0 +1,626 @@
+/*     $NetBSD: umodem.c,v 1.25 2000/03/27 12:33:57 augustss Exp $     */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Comm Class spec: http://www.usb.org/developers/data/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Add error recovery in various places; the big problem is what
+ *   to do in a callback if there is an error.
+ * - Implement a Call Device for modems without multiplexed commands.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/ucomvar.h>
+
+#ifdef UMODEM_DEBUG
+#define DPRINTFN(n, x) if (umodemdebug > (n)) logprintf x
+int    umodemdebug = 0;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+struct umodem_softc {
+       USBBASEDEVICE           sc_dev;         /* base device */
+
+       usbd_device_handle      sc_udev;        /* USB device */
+
+       int                     sc_ctl_iface_no;
+       usbd_interface_handle   sc_ctl_iface;   /* control interface */
+       int                     sc_data_iface_no;
+       usbd_interface_handle   sc_data_iface;  /* data interface */
+
+       int                     sc_cm_cap;      /* CM capabilities */
+       int                     sc_acm_cap;     /* ACM capabilities */
+
+       int                     sc_cm_over_data;
+
+       usb_cdc_line_state_t    sc_line_state;  /* current line state */
+       u_char                  sc_dtr;         /* current DTR state */
+       u_char                  sc_rts;         /* current RTS state */
+
+       device_ptr_t            sc_subdev;      /* ucom device */
+
+       u_char                  sc_opening;     /* lock during open */
+       u_char                  sc_dying;       /* disconnecting */
+};
+
+Static void    *umodem_get_desc
+               __P((usbd_device_handle dev, int type, int subtype));
+Static usbd_status umodem_set_comm_feature
+               __P((struct umodem_softc *sc, int feature, int state));
+Static usbd_status umodem_set_line_coding
+               __P((struct umodem_softc *sc, usb_cdc_line_state_t *state));
+
+Static void    umodem_get_caps __P((usbd_device_handle, int *, int *));
+
+Static void    umodem_get_status
+               __P((void *, int portno, u_char *lsr, u_char *msr));
+Static void    umodem_set      __P((void *, int, int, int));
+Static void    umodem_dtr      __P((struct umodem_softc *, int));
+Static void    umodem_rts      __P((struct umodem_softc *, int));
+Static void    umodem_break    __P((struct umodem_softc *, int));
+Static void    umodem_set_line_state __P((struct umodem_softc *));
+Static int     umodem_param    __P((void *, int, struct termios *));
+Static int     umodem_ioctl    __P((void *, int, u_long, caddr_t, int,
+                                    struct proc *));
+
+Static struct ucom_methods umodem_methods = {
+       umodem_get_status,
+       umodem_set,
+       umodem_param,
+       umodem_ioctl,
+       NULL,
+       NULL,
+};
+
+USB_DECLARE_DRIVER(umodem);
+
+USB_MATCH(umodem)
+{
+       USB_MATCH_START(umodem, uaa);
+       usb_interface_descriptor_t *id;
+       int cm, acm;
+       
+       if (uaa->iface == NULL)
+               return (UMATCH_NONE);
+
+       id = usbd_get_interface_descriptor(uaa->iface);
+       if (id == NULL ||
+           id->bInterfaceClass != UICLASS_CDC ||
+           id->bInterfaceSubClass != UISUBCLASS_ABSTRACT_CONTROL_MODEL ||
+           id->bInterfaceProtocol != UIPROTO_CDC_AT)
+               return (UMATCH_NONE);
+       
+       umodem_get_caps(uaa->device, &cm, &acm);
+       if (!(cm & USB_CDC_CM_DOES_CM) ||
+           !(cm & USB_CDC_CM_OVER_DATA) ||
+           !(acm & USB_CDC_ACM_HAS_LINE))
+               return (UMATCH_NONE);
+
+       return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+}
+
+USB_ATTACH(umodem)
+{
+       USB_ATTACH_START(umodem, sc, uaa);
+       usbd_device_handle dev = uaa->device;
+       usb_interface_descriptor_t *id;
+       usb_endpoint_descriptor_t *ed;
+       usb_cdc_cm_descriptor_t *cmd;
+       char devinfo[1024];
+       usbd_status err;
+       int data_ifcno;
+       int i;
+       struct ucom_attach_args uca;
+
+       usbd_devinfo(uaa->device, 0, devinfo);
+       USB_ATTACH_SETUP;
+
+       sc->sc_udev = dev;
+       sc->sc_ctl_iface = uaa->iface;
+
+       id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+       printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
+              devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
+       sc->sc_ctl_iface_no = id->bInterfaceNumber;
+
+       umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap);
+
+       /* Get the data interface no. */
+       cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+       if (cmd == NULL) {
+               printf("%s: no CM descriptor\n", USBDEVNAME(sc->sc_dev));
+               goto bad;
+       }
+       sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface;
+
+       printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
+              USBDEVNAME(sc->sc_dev), data_ifcno,
+              sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+              sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+
+       /* Get the data interface too. */
+       for (i = 0; i < uaa->nifaces; i++) {
+               if (uaa->ifaces[i] != NULL) {
+                       id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+                       if (id != NULL && id->bInterfaceNumber == data_ifcno) {
+                               sc->sc_data_iface = uaa->ifaces[i];
+                               uaa->ifaces[i] = NULL;
+                       }
+               }
+       }
+       if (sc->sc_data_iface == NULL) {
+               printf("%s: no data interface\n", USBDEVNAME(sc->sc_dev));
+               goto bad;
+       }
+
+       /* 
+        * Find the bulk endpoints. 
+        * Iterate over all endpoints in the data interface and take note.
+        */
+       uca.bulkin = uca.bulkout = -1;
+
+       id = usbd_get_interface_descriptor(sc->sc_data_iface);
+       for (i = 0; i < id->bNumEndpoints; i++) {
+               ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
+               if (ed == NULL) {
+                       printf("%s: no endpoint descriptor for %d\n",
+                               USBDEVNAME(sc->sc_dev), i);
+                       goto bad;
+               }
+               if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+                   (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+                        uca.bulkin = ed->bEndpointAddress;
+                } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+                          (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+                        uca.bulkout = ed->bEndpointAddress;
+                }
+        }
+
+       if (uca.bulkin == -1) {
+               printf("%s: Could not find data bulk in\n",
+                      USBDEVNAME(sc->sc_dev));
+               goto bad;
+       }
+       if (uca.bulkout == -1) {
+               printf("%s: Could not find data bulk out\n",
+                       USBDEVNAME(sc->sc_dev));
+               goto bad;
+       }
+
+       if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
+               err = umodem_set_comm_feature(sc, UCDC_ABSTRACT_STATE,
+                                             UCDC_DATA_MULTIPLEXED);
+               if (err) {
+                       printf("%s: could not set data multiplex mode\n",
+                              USBDEVNAME(sc->sc_dev));
+                       goto bad;
+               }
+               sc->sc_cm_over_data = 1;
+       }
+
+       sc->sc_dtr = -1;
+
+       uca.portno = UCOM_UNK_PORTNO;
+       /* bulkin, bulkout set above */
+       uca.device = sc->sc_udev;
+       uca.iface = sc->sc_data_iface;
+       uca.methods = &umodem_methods;
+       uca.arg = sc;
+
+       DPRINTF(("umodem_attach: sc=%p\n", sc));
+       sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
+
+       usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+                          USBDEV(sc->sc_dev));
+
+       USB_ATTACH_SUCCESS_RETURN;
+
+ bad:
+       sc->sc_dying = 1;
+       USB_ATTACH_ERROR_RETURN;
+}
+
+void
+umodem_get_caps(dev, cm, acm)
+       usbd_device_handle dev;
+       int *cm, *acm;
+{
+       usb_cdc_cm_descriptor_t *cmd;
+       usb_cdc_acm_descriptor_t *cad;
+
+       *cm = *acm = 0;
+
+       cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+       if (cmd == NULL) {
+               DPRINTF(("umodem_get_desc: no CM desc\n"));
+               return;
+       }
+       *cm = cmd->bmCapabilities;
+
+       cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+       if (cad == NULL) {
+               DPRINTF(("umodem_get_desc: no ACM desc\n"));
+               return;
+       }
+       *acm = cad->bmCapabilities;
+} 
+
+void
+umodem_get_status(addr, portno, lsr, msr)
+       void *addr;
+       int portno;
+       u_char *lsr, *msr;
+{
+       DPRINTF(("umodem_get_status:\n"));
+
+       if (lsr != NULL)
+               *lsr = 0;       /* XXX */
+       if (msr != NULL)
+               *msr = 0;       /* XXX */
+}
+
+int
+umodem_param(addr, portno, t)
+       void *addr;
+       int portno;
+       struct termios *t;
+{
+       struct umodem_softc *sc = addr;
+       usbd_status err;
+       usb_cdc_line_state_t ls;
+
+       DPRINTF(("umodem_param: sc=%p\n", sc));
+
+       USETDW(ls.dwDTERate, t->c_ospeed);
+       if (ISSET(t->c_cflag, CSTOPB))
+               ls.bCharFormat = UCDC_STOP_BIT_2;
+       else
+               ls.bCharFormat = UCDC_STOP_BIT_1;
+       if (ISSET(t->c_cflag, PARENB)) {
+               if (ISSET(t->c_cflag, PARODD))
+                       ls.bParityType = UCDC_PARITY_ODD;
+               else
+                       ls.bParityType = UCDC_PARITY_EVEN;
+       } else
+               ls.bParityType = UCDC_PARITY_NONE;
+       switch (ISSET(t->c_cflag, CSIZE)) {
+       case CS5:
+               ls.bDataBits = 5;
+               break;
+       case CS6:
+               ls.bDataBits = 6;
+               break;
+       case CS7:
+               ls.bDataBits = 7;
+               break;
+       case CS8:
+               ls.bDataBits = 8;
+               break;
+       }
+
+       err = umodem_set_line_coding(sc, &ls);
+       if (err) {
+               DPRINTF(("umodem_param: err=%s\n", usbd_errstr(err)));
+               return (1);
+       }
+       return (0);
+}
+
+int
+umodem_ioctl(addr, portno, cmd, data, flag, p)
+       void *addr;
+       int portno;
+       u_long cmd;
+       caddr_t data;
+       int flag;
+       struct proc *p;
+{
+       struct umodem_softc *sc = addr;
+       int error = 0;
+
+       if (sc->sc_dying)
+               return (EIO);
+       DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd));
+
+       switch (cmd) {
+       case USB_GET_CM_OVER_DATA:
+               *(int *)data = sc->sc_cm_over_data;
+               break;
+
+       case USB_SET_CM_OVER_DATA:
+               if (*(int *)data != sc->sc_cm_over_data) {
+                       /* XXX change it */
+               }
+               break;
+
+       default:
+               DPRINTF(("umodemioctl: unknown\n"));
+               error = ENOTTY;
+               break;
+       }
+
+       return (error);
+}
+
+void
+umodem_dtr(sc, onoff)
+       struct umodem_softc *sc;
+       int onoff;
+{
+       DPRINTF(("umodem_modem: onoff=%d\n", onoff));
+
+       if (sc->sc_dtr == onoff)
+               return;
+       sc->sc_dtr = onoff;
+
+       umodem_set_line_state(sc);
+}
+
+void
+umodem_rts(sc, onoff)
+       struct umodem_softc *sc;
+       int onoff;
+{
+       DPRINTF(("umodem_modem: onoff=%d\n", onoff));
+
+       if (sc->sc_rts == onoff)
+               return;
+       sc->sc_rts = onoff;
+
+       umodem_set_line_state(sc);
+}
+
+void
+umodem_set_line_state(sc)
+       struct umodem_softc *sc;
+{
+       usb_device_request_t req;
+       int ls;
+
+       ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
+            (sc->sc_rts ? UCDC_LINE_RTS : 0);
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+       USETW(req.wValue, ls);
+       USETW(req.wIndex, sc->sc_ctl_iface_no);
+       USETW(req.wLength, 0);
+
+       (void)usbd_do_request(sc->sc_udev, &req, 0);
+
+}
+
+void
+umodem_break(sc, onoff)
+       struct umodem_softc *sc;
+       int onoff;
+{
+       usb_device_request_t req;
+
+       DPRINTF(("umodem_break: onoff=%d\n", onoff));
+
+       if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
+               return;
+
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UCDC_SEND_BREAK;
+       USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+       USETW(req.wIndex, sc->sc_ctl_iface_no);
+       USETW(req.wLength, 0);
+
+       (void)usbd_do_request(sc->sc_udev, &req, 0);
+}
+
+void
+umodem_set(addr, portno, reg, onoff)
+       void *addr;
+       int portno;
+       int onoff;
+{
+       struct umodem_softc *sc = addr;
+
+       switch (reg) {
+       case UCOM_SET_DTR:
+               umodem_dtr(sc, onoff);
+               break;
+       case UCOM_SET_RTS:
+               umodem_rts(sc, onoff);
+               break;
+       case UCOM_SET_BREAK:
+               umodem_break(sc, onoff);
+               break;
+       default:
+               break;
+       }
+}
+
+usbd_status
+umodem_set_line_coding(sc, state)
+       struct umodem_softc *sc;
+       usb_cdc_line_state_t *state;
+{
+       usb_device_request_t req;
+       usbd_status err;
+
+       DPRINTF(("umodem_set_line_coding: rate=%d fmt=%d parity=%d bits=%d\n",
+                UGETDW(state->dwDTERate), state->bCharFormat,
+                state->bParityType, state->bDataBits));
+
+       if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
+               DPRINTF(("umodem_set_line_coding: already set\n"));
+               return (USBD_NORMAL_COMPLETION);
+       }
+
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UCDC_SET_LINE_CODING;
+       USETW(req.wValue, 0);
+       USETW(req.wIndex, sc->sc_ctl_iface_no);
+       USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+       err = usbd_do_request(sc->sc_udev, &req, state);
+       if (err) {
+               DPRINTF(("umodem_set_line_coding: failed, err=%s\n", 
+                        usbd_errstr(err)));
+               return (err);
+       }
+
+       sc->sc_line_state = *state;
+
+       return (USBD_NORMAL_COMPLETION);
+}
+
+void *
+umodem_get_desc(dev, type, subtype)
+       usbd_device_handle dev;
+       int type;
+       int subtype;
+{
+       usb_descriptor_t *desc;
+       usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+        uByte *p = (uByte *)cd;
+        uByte *end = p + UGETW(cd->wTotalLength);
+
+       while (p < end) {
+               desc = (usb_descriptor_t *)p;
+               if (desc->bDescriptorType == type &&
+                   desc->bDescriptorSubtype == subtype)
+                       return (desc);
+               p += desc->bLength;
+       }
+
+       return (0);
+}
+
+usbd_status
+umodem_set_comm_feature(sc, feature, state)
+       struct umodem_softc *sc;
+       int feature;
+       int state;
+{
+       usb_device_request_t req;
+       usbd_status err;
+       usb_cdc_abstract_state_t ast;
+
+       DPRINTF(("umodem_set_comm_feature: feature=%d state=%d\n", feature,
+                state));
+
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UCDC_SET_COMM_FEATURE;
+       USETW(req.wValue, feature);
+       USETW(req.wIndex, sc->sc_ctl_iface_no);
+       USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
+       USETW(ast.wState, state);
+
+       err = usbd_do_request(sc->sc_udev, &req, &ast);
+       if (err) {
+               DPRINTF(("umodem_set_comm_feature: feature=%d, err=%s\n",
+                        feature, usbd_errstr(err)));
+               return (err);
+       }
+
+       return (USBD_NORMAL_COMPLETION);
+}
+
+int
+umodem_activate(self, act)
+       device_ptr_t self;
+       enum devact act;
+{
+       struct umodem_softc *sc = (struct umodem_softc *)self;
+       int rv = 0;
+
+       switch (act) {
+       case DVACT_ACTIVATE:
+               return (EOPNOTSUPP);
+               break;
+
+       case DVACT_DEACTIVATE:
+               sc->sc_dying = 1;
+               if (sc->sc_subdev)
+                       rv = config_deactivate(sc->sc_subdev);
+               break;
+       }
+       return (rv);
+}
+
+USB_DETACH(umodem)
+{
+       USB_DETACH_START(umodem, sc);
+       int rv = 0;
+
+       DPRINTF(("umodem_detach: sc=%p flags=%d\n", sc, flags));
+
+       sc->sc_dying = 1;
+
+       if (sc->sc_subdev != NULL)
+               rv = config_detach(sc->sc_subdev, flags);
+
+       usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+                          USBDEV(sc->sc_dev));
+
+       return (rv);
+}
index b343ec0..e58bcbc 100644 (file)
@@ -1,4 +1,3 @@
-/*     $OpenBSD: usb_port.h,v 1.11 2000/04/04 22:51:23 aaron Exp $ */
 /*     $NetBSD: usb_port.h,v 1.28 2000/03/30 08:53:31 augustss Exp $   */
 /*     $FreeBSD: src/sys/dev/usb/usb_port.h,v 1.21 1999/11/17 22:33:47 n_hibma Exp $   */
 
@@ -189,6 +188,9 @@ __CONCAT(dname,_detach)(self, flags) \
 
 #define Static
 
+#define UCOMBUSCF_PORTNO               -1
+#define UCOMBUSCF_PORTNO_DEFAULT       -1
+
 #define SCSI_MODE_SENSE                MODE_SENSE
 #define XS_STS_DONE            ITSDONE
 #define XS_CTL_POLL            SCSI_POLL