diff options
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 6 | ||||
-rw-r--r-- | sys/dev/usb/files.usb | 15 | ||||
-rw-r--r-- | sys/dev/usb/ucom.c | 1045 | ||||
-rw-r--r-- | sys/dev/usb/ucomvar.h | 111 | ||||
-rw-r--r-- | sys/dev/usb/umodem.c | 626 | ||||
-rw-r--r-- | sys/dev/usb/usb_port.h | 4 |
6 files changed, 1804 insertions, 3 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index edf2ce6e156..96b4f757182 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -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 ? diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index b0e02f81b8f..a4d1fae5602 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -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 index 00000000000..63c547fde4e --- /dev/null +++ b/sys/dev/usb/ucom.c @@ -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 index 00000000000..6aaff40fdd2 --- /dev/null +++ b/sys/dev/usb/ucomvar.h @@ -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 index 00000000000..f65bd4f645b --- /dev/null +++ b/sys/dev/usb/umodem.c @@ -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); +} diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h index b343ec05933..e58bcbcbb00 100644 --- a/sys/dev/usb/usb_port.h +++ b/sys/dev/usb/usb_port.h @@ -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 |