summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorn_hibma <n_hibma@FreeBSD.org>2000-07-17 19:28:29 +0000
committern_hibma <n_hibma@FreeBSD.org>2000-07-17 19:28:29 +0000
commit04ddbba61aed4cc7d53cd30f1a59d296515f3a2a (patch)
tree202f80b569b2cb03f86c0d7d7d0f73037076ed65 /sys/dev
parentd6d5b813b388813775144cf37b0d3f1f1f8de159 (diff)
downloadFreeBSD-src-04ddbba61aed4cc7d53cd30f1a59d296515f3a2a.zip
FreeBSD-src-04ddbba61aed4cc7d53cd30f1a59d296515f3a2a.tar.gz
The modem driver for Communication Device Class, ACM compliant devices.
There is a number of devices that are compliant, of which the 3Com 5605 is has been verified to work. The driver is not perfect yet, but should be able to get you somewhere. The driver was originally written by Lennart Augustsson, but Mike Smith and Mike Meyer <mwm@mired.org> did the porting.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/umodem.c1139
1 files changed, 1114 insertions, 25 deletions
diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c
index 47cbd17..53f3810 100644
--- a/sys/dev/usb/umodem.c
+++ b/sys/dev/usb/umodem.c
@@ -38,17 +38,31 @@
* 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/malloc.h>
-#if defined(__NetBSD__)
-#include <sys/ioctl.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/device.h>
+#include <sys/ioctl.h>
#elif defined(__FreeBSD__)
-#include <sys/module.h>
#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
#endif
+#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/file.h>
#include <sys/select.h>
@@ -64,73 +78,1148 @@
#include <dev/usb/usbdevs.h>
#include <dev/usb/usb_quirks.h>
+#include <dev/usb/usbdevs.h>
+
#ifdef UMODEM_DEBUG
-#define DPRINTF(x) if (umodemdebug) logprintf x
-#define DPRINTFN(n,x) if (umodemdebug>(n)) logprintf x
+#define DPRINTF(x) if(umodemdebug) logprintf x
+#define DPRINTFN(n, x) if(umodemdebug > (n)) logprintf x
int umodemdebug = 1;
#else
#define DPRINTF(x)
-#define DPRINTFN(n,x)
+#define DPRINTFN(n, x)
#endif
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~((unsigned)(f))
+#define ISSET(t, f) ((t) & (f))
+
+#define UMODEMUNIT_MASK 0x3ffff
+#define UMODEMDIALOUT_MASK 0x80000
+#define UMODEMCALLUNIT_MASK 0x40000
+
+#define UMODEMUNIT(x) (minor(x) & UMODEMUNIT_MASK)
+#define UMODEMDIALOUT(x) (minor(x) & UMODEMDIALOUT_MASK)
+#define UMODEMCALLUNIT(x) (minor(x) & UMODEMCALLUNIT_MASK)
+
+#define UMODEMIBUFSIZE 64
+
struct umodem_softc {
- USBBASEDEVICE sc_dev; /* base device */
- usbd_interface_handle sc_ctl; /* control interface */
- usbd_interface_handle sc_data; /* data interface */
- uByte cmCaps;
- uByte acmCaps;
+ 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_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; /* read request */
+
+ int sc_cm_cap; /* CM capabilities */
+ int sc_acm_cap; /* ACM capabilities */
+
+ int sc_cm_over_data;
+
+ struct tty *sc_tty; /* our tty */
+
+ usb_cdc_line_state_t sc_line_state; /* current line state */
+ u_char sc_dtr; /* current DTR state */
+
+ u_char sc_opening; /* lock during open */
+ u_char sc_dying; /* disconnecting */
+
+#if defined(__FreeBSD__)
+ dev_t dev; /* special device node */
+#endif
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+cdev_decl(umodem);
+
+#elif defined(__FreeBSD__)
+d_open_t umodemopen;
+d_close_t umodemclose;
+d_read_t umodemread;
+d_write_t umodemwrite;
+d_ioctl_t umodemioctl;
+
+#define UMODEM_CDEV_MAJOR 124
+
+static struct cdevsw umodem_cdevsw = {
+ /* open */ umodemopen,
+ /* close */ umodemclose,
+ /* read */ umodemread,
+ /* write */ umodemwrite,
+ /* ioctl */ umodemioctl,
+ /* poll */ ttypoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "umodem",
+ /* maj */ UMODEM_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_TTY,
+ /* bmaj */ -1
};
+#endif
-void umodem_intr __P((usbd_xfer_handle, usbd_private_handle, usbd_status));
-void umodem_disco __P((void *));
+void *umodem_get_desc
+ (usbd_device_handle dev, int type, int subtype);
+usbd_status umodem_set_comm_feature
+ (struct umodem_softc *sc, int feature, int state);
+usbd_status umodem_set_line_coding
+ (struct umodem_softc *sc, usb_cdc_line_state_t *state);
+
+void umodem_get_caps (usbd_device_handle, int *, int *);
+void umodem_cleanup (struct umodem_softc *);
+int umodemparam (struct tty *, struct termios *);
+void umodemstart (struct tty *);
+void umodemstop (struct tty *, int);
+struct tty * umodemtty (dev_t dev);
+void umodem_shutdown (struct umodem_softc *);
+void umodem_modem (struct umodem_softc *, int);
+void umodem_break (struct umodem_softc *, int);
+usbd_status umodemstartread (struct umodem_softc *);
+void umodemreadcb (usbd_xfer_handle, usbd_private_handle,
+ usbd_status status);
+void umodemwritecb (usbd_xfer_handle, usbd_private_handle,
+ usbd_status status);
USB_DECLARE_DRIVER(umodem);
USB_MATCH(umodem)
{
USB_MATCH_START(umodem, uaa);
-
usb_interface_descriptor_t *id;
+ int cm, acm;
if (!uaa->iface)
return (UMATCH_NONE);
+
id = usbd_get_interface_descriptor(uaa->iface);
- if (!id ||
+ if (id == 0 ||
id->bInterfaceClass != UCLASS_CDC ||
id->bInterfaceSubClass != USUBCLASS_ABSTRACT_CONTROL_MODEL ||
id->bInterfaceProtocol != UPROTO_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_interface_handle iface = uaa->iface;
+ 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];
-
- sc->sc_ctl = iface;
- id = usbd_get_interface_descriptor(iface);
+ usbd_status err;
+ int data_ifaceno;
+ int i;
+ struct tty *tp;
+
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) {
+ DPRINTF(("%s: no CM desc\n", USBDEVNAME(sc->sc_dev)));
+ goto bad;
+ }
+ sc->sc_data_iface_no = data_ifaceno = cmd->bDataInterface;
+
+ printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
+ USBDEVNAME(sc->sc_dev), data_ifaceno,
+ 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]) {
+ id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+ if (id->bInterfaceNumber == data_ifaceno) {
+ sc->sc_data_iface = uaa->ifaces[i];
+ uaa->ifaces[i] = 0;
+ }
+ }
+ }
+ if (!sc->sc_data_iface) {
+ 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.
+ */
+ sc->sc_bulkin_no = sc->sc_bulkout_no = -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) {
+ 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) {
+ sc->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+
+ if (sc->sc_bulkin_no == -1) {
+ DPRINTF(("%s: Could not find data bulk in\n",
+ USBDEVNAME(sc->sc_dev)));
+ goto bad;
+ }
+ if (sc->sc_bulkout_no == -1) {
+ DPRINTF(("%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)
+ goto bad;
+ sc->sc_cm_over_data = 1;
+ }
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_tty = tp = ttymalloc();
+#elif defined(__FreeBSD__)
+ sc->sc_tty = tp = ttymalloc(sc->sc_tty);
+#endif
+ tp->t_oproc = umodemstart;
+ tp->t_param = umodemparam;
+ tp->t_stop = umodemstop;
+ DPRINTF(("umodem_attach: tty_attach %p\n", tp));
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ tty_attach(tp);
+#endif
+
+#if defined(__FreeBSD__)
+ DPRINTF(("umodem_attach: make_dev: umodem%d\n", device_get_unit(self)));
+ sc->dev = make_dev(&umodem_cdevsw, device_get_unit(self),
+ UID_UUCP, GID_DIALER, 0660,
+ "umodem%d", device_get_unit(self));
+ sc->dev->si_tty = tp;
+#endif
+
+ sc->sc_dtr = -1;
USB_ATTACH_SUCCESS_RETURN;
+
+ bad:
+ DPRINTF(("umodem_attach: BAD -> DYING\n"));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
}
-#if defined(__FreeBSD__)
-Static int
-umodem_detach(device_t self)
+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) {
+ 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) {
+ DPRINTF(("umodem_get_desc: no ACM desc\n"));
+ return;
+ }
+ *acm = cad->bmCapabilities;
+}
+
+void
+umodemstart(tp)
+ struct tty *tp;
+{
+ struct umodem_softc *sc;
+ int s;
+ u_char *data;
+ int cnt;
+
+ USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
+
+ if (sc->sc_dying)
+ return;
+
+ s = spltty();
+ if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
+ ttwwakeup(tp);
+ DPRINTFN(4,("umodemstart: stopped\n"));
+ goto out;
+ }
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ 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;
+ }
+#elif defined(__FreeBSD__)
+ if (tp->t_outq.c_cc <= tp->t_olowat) {
+ if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
+ CLR(tp->t_state, TS_SO_OLOWAT);
+ wakeup(TSA_OLOWAT(tp));
+ }
+ selwakeup(&tp->t_wsel);
+ if (tp->t_outq.c_cc == 0) {
+ if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
+ TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
+ CLR(tp->t_state, TS_SO_OCOMPLETE);
+ wakeup(TSA_OCOMPLETE(tp));
+ }
+ goto out;
+ }
+ }
+#endif
+
+ /* Grab the first contiguous region of buffer space. */
+ data = tp->t_outq.c_cf;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ cnt = ndqb(&tp->t_outq, 0);
+#elif defined(__FreeBSD__)
+ cnt = tp->t_outq.c_cc;
+#endif
+
+ if (cnt == 0) {
+ DPRINTF(("umodemstart: cnt==0\n"));
+ splx(s);
+ return;
+ }
+
+ SET(tp->t_state, TS_BUSY);
+
+ DPRINTFN(4,("umodemstart: %d chars\n", cnt));
+ /* XXX what can we do on error? */
+ usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
+ (usbd_private_handle)sc, data, cnt,
+ 0, USBD_NO_TIMEOUT, umodemwritecb);
+ (void)usbd_transfer(sc->sc_oxfer);
+
+ ttwwakeup(tp);
+out:
+ splx(s);
+}
+
+void
+umodemwritecb(xfer, priv, status)
+ usbd_xfer_handle xfer;
+ usbd_private_handle priv;
+ usbd_status status;
{
- DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
+ struct umodem_softc *sc = (struct umodem_softc *)priv;
+ struct tty *tp = sc->sc_tty;
+ u_int32_t cc;
+ int s;
- return 0;
+ DPRINTFN(5,("umodemwritecb: status=%d\n", status));
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("umodemwritecb: 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, 0, 0, &cc, 0);
+ DPRINTFN(5,("umodemwritecb: 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);
}
+
+int
+umodemparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ struct umodem_softc *sc;
+ usb_cdc_line_state_t ls;
+
+ USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ /* Check requested parameters. */
+ if (t->c_ospeed < 0)
+ return (EINVAL);
+ if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /*
+ * 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);
+
+ /* And copy to tty. */
+ tp->t_ispeed = 0;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = t->c_cflag;
+
+ 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;
+ }
+ /* XXX what can we if it fails? */
+ (void)umodem_set_line_coding(sc, &ls);
+
+ /*
+ * 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.
+ */
+ (void) (*linesw[tp->t_line].l_modem)(tp, 1 /* XXX carrier */ );
+
+ return (0);
+}
+
+int
+umodemopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ int unit = UMODEMUNIT(dev);
+ struct umodem_softc *sc;
+ usbd_status err;
+ struct tty *tp;
+ int s;
+ int error;
+
+ USB_GET_SC_OPEN(umodem, unit, sc);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+#if defined(__NetBSD__) || defined(__OpenBBSD__)
+ if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
+ return (ENXIO);
+#endif
+
+ tp = sc->sc_tty;
+
+ DPRINTF(("%s: umodemopen: tp=%p\n", USBDEVNAME(sc->sc_dev), tp));
+
+ if (ISSET(tp->t_state, TS_ISOPEN) &&
+ ISSET(tp->t_state, TS_XCLUDE) &&
+ p->p_ucred->cr_uid != 0)
+ return (EBUSY);
+
+ /*
+ * Do the following iff this is a first open.
+ */
+ s = spltty();
+ while (sc->sc_opening)
+ tsleep(&sc->sc_opening, PRIBIO, "umdmop", 0);
+ sc->sc_opening = 1;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+#elif defined(__FreeBSD__)
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+#endif
+ struct termios t;
+
+ tp->t_dev = dev;
+
+ /*
+ * 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;
+ /* Make sure umodemparam() will do something. */
+ tp->t_ospeed = 0;
+ (void) umodemparam(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.
+ */
+ umodem_modem(sc, 1);
+
+ DPRINTF(("umodemopen: open pipes\n"));
+
+ /* Open the bulk pipes */
+ err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkin_no, 0,
+ &sc->sc_bulkin_pipe);
+ if (err) {
+ DPRINTF(("%s: cannot open bulk out pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no));
+ return (EIO);
+ }
+ err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ DPRINTF(("%s: cannot open bulk in pipe (addr %d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no));
+ 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 == 0) {
+ 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 == 0) {
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ usbd_free_xfer(sc->sc_ixfer);
+ return (ENOMEM);
+ }
+ sc->sc_ibuf = malloc(UMODEMIBUFSIZE, M_USBDEV, M_WAITOK);
+ umodemstartread(sc);
+ }
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+ splx(s);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ error = ttyopen(tp, UMODEMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
+ if (error)
+ goto bad;
+#elif defined(__FreeBSD__)
+ error = ttyopen(dev, tp);
+ if (error)
+ goto bad;
+#endif
+
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ if (error)
+ goto bad;
+
+ return (0);
+
+bad:
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+#elif defined(__FreeBSD__)
+ 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.
+ */
+ umodem_cleanup(sc);
+ }
+
+ return (error);
+}
+
+usbd_status
+umodemstartread(sc)
+ struct umodem_softc *sc;
+{
+ usbd_status err;
+
+ DPRINTFN(5,("umodemstartread: start\n"));
+ usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
+ (usbd_private_handle)sc,
+ sc->sc_ibuf, UMODEMIBUFSIZE, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, umodemreadcb);
+
+ err = usbd_transfer(sc->sc_ixfer);
+ if (err != USBD_IN_PROGRESS)
+ return (err);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+umodemreadcb(xfer, p, status)
+ usbd_xfer_handle xfer;
+ usbd_private_handle p;
+ usbd_status status;
+{
+ struct umodem_softc *sc = (struct umodem_softc *)p;
+ struct tty *tp = sc->sc_tty;
+ int (*rint) (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 != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("umodemreadcb: 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, 0, (void **)&cp, &cc, 0);
+ DPRINTFN(5,("umodemreadcb: got %d chars, tp=%p\n", cc, tp));
+ s = spltty();
+ /* Give characters to tty layer. */
+ while (cc-- > 0) {
+ DPRINTFN(7,("umodemreadcb: char=0x%02x\n", *cp));
+ if ((*rint)(*cp++, tp) == -1) {
+ /* XXX what should we do? */
+ break;
+ }
+ }
+ splx(s);
+
+ err = umodemstartread(sc);
+ if (err) {
+ printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev));
+ /* XXX what should we dow now? */
+ }
+}
+
+int
+umodemclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct umodem_softc *sc;
+ struct tty *tp;
+ int s;
+
+ USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
+
+ tp = sc->sc_tty;
+
+ DPRINTF(("%s: umodemclose sc_tty=%p\n", USBDEVNAME(sc->sc_dev), tp));
+
+ if (!ISSET(tp->t_state, TS_ISOPEN))
+ return (0);
+
+ DPRINTF(("%s: umodemclose lclose(%p,%d)\n", USBDEVNAME(sc->sc_dev), tp,flag));
+
+ s=spltty();
+ DPRINTF(("%s: umodemclose lclose=%p\n", USBDEVNAME(sc->sc_dev), linesw[tp->t_line].l_close));
+ (*linesw[tp->t_line].l_close)(tp, flag);
+
+ DPRINTF(("%s: umodemclose ttyclose(%p)\n", USBDEVNAME(sc->sc_dev), tp));
+ ttyclose(tp);
+ splx(s);
+
+ DPRINTF(("%s: umodemclose sc->sc_dying=%d\n", USBDEVNAME(sc->sc_dev), sc->sc_dying));
+ if (sc->sc_dying)
+ return (0);
+
+ DPRINTF(("%s: umodemclose tp->t_state=%d\n", USBDEVNAME(sc->sc_dev), tp->t_state));
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+#elif defined(__FreeBSD__)
+ 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.
+ */
+ DPRINTF(("%s: umodemclose umodem_cleanup(%p)\n", USBDEVNAME(sc->sc_dev), sc));
+ umodem_cleanup(sc);
+ }
+ DPRINTF(("%s: umodemclose return\n", USBDEVNAME(sc->sc_dev)));
+
+ return (0);
+}
+
+void
+umodem_cleanup(sc)
+ struct umodem_softc *sc;
+{
+ umodem_shutdown(sc);
+
+ DPRINTFN(5, ("%s: umodem_cleanup: closing pipes\n",
+ USBDEVNAME(sc->sc_dev)));
+
+ 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);
+ free(sc->sc_ibuf, M_USBDEV);
+}
+
+int
+umodemread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct umodem_softc *sc;
+ struct tty *tp;
+
+ USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
+
+ tp = sc->sc_tty;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+int
+umodemwrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct umodem_softc *sc;
+ struct tty *tp;
+
+ USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
+
+ tp = sc->sc_tty;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+void
+umodemstop(tp, flag)
+ struct tty *tp;
+ int flag;
+{
+ struct umodem_softc *sc;
+ int s;
+
+ USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc);
+
+ DPRINTF(("umodemstop: %d\n", flag));
+ s = spltty();
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ DPRINTF(("umodemstop: XXX\n"));
+ /* XXX do what? */
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
+ }
+ splx(s);
+}
+
+struct tty *
+umodemtty(dev)
+ dev_t dev;
+{
+ struct umodem_softc *sc;
+ struct tty *tp;
+
+ USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
+
+ tp = sc->sc_tty;
+
+ return (tp);
+}
+
+int
+umodemioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct umodem_softc *sc;
+ struct tty *tp;
+ int error;
+ int s;
+ int bits;
+
+ USB_GET_SC(umodem, UMODEMUNIT(dev), sc);
+
+ tp = sc->sc_tty;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd));
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ error = ttioctl(tp, cmd, data, flag, p);
+#elif defined(__FreeBSD__)
+ error = ttioctl(tp, cmd, data, flag);
+#endif
+ if (error >= 0)
+ return (error);
+
+ DPRINTF(("umodemioctl: our cmd=0x%08lx\n", cmd));
+ s = spltty();
+
+ switch (cmd) {
+ case TIOCSBRK:
+ umodem_break(sc, 1);
+ break;
+
+ case TIOCCBRK:
+ umodem_break(sc, 0);
+ break;
+
+ case TIOCSDTR:
+ umodem_modem(sc, 1);
+ break;
+
+ case TIOCCDTR:
+ umodem_modem(sc, 0);
+ break;
+
+ case TIOCMGET:
+ bits = TIOCM_LE;
+ if(sc->sc_dtr)
+ bits |= TIOCM_DTR;
+ *(int *)data = bits;
+ break;
+
+ case TIOCMSET:
+ break;
+
+ 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;
+ splx(s);
+ return (error);
+ }
+
+ splx(s);
+ return(0);
+}
+
+void
+umodem_shutdown(sc)
+ struct umodem_softc *sc;
+{
+ struct tty *tp = sc->sc_tty;
+
+ DPRINTF(("%s: umodem_shutdown\n", USBDEVNAME(sc->sc_dev)));
+ /*
+ * 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)) {
+ umodem_modem(sc, 0);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ (void) tsleep(sc, TTIPRI, ttclos, hz);
+#elif defined(__FreeBSD__)
+ (void) tsleep(sc, TTIPRI, "umdmsd", hz);
+#endif
+ }
+}
+
+void
+umodem_modem(sc, onoff)
+ struct umodem_softc *sc;
+ int onoff;
+{
+ usb_device_request_t req;
+
+ DPRINTF(("%s: umodem_modem: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, onoff ? UCDC_LINE_DTR : 0);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ (void)usbd_do_request(sc->sc_udev, &req, 0);
+
+ sc->sc_dtr = onoff;
+}
+
+void
+umodem_break(sc, onoff)
+ struct umodem_softc *sc;
+ int onoff;
+{
+ usb_device_request_t req;
+
+ DPRINTF(("%s: umodem_break: onoff=%d\n", USBDEVNAME(sc->sc_dev),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_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;
+
+ 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(("%s: umodem_set_comm_feat: feature=%d failed, err=%d\n",
+ USBDEVNAME(sc->sc_dev), feature, err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+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(("%s: umodem_set_line_cod: rate=%d fmt=%d parity=%d bits=%d\n",
+ USBDEVNAME(sc->sc_dev), UGETDW(state->dwDTERate),
+ state->bCharFormat, state->bParityType, state->bDataBits));
+
+ if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
+ DPRINTF(("%s: umodem_set_line_coding: already set\n",
+ USBDEVNAME(sc->sc_dev)));
+ 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(("%s: umodem_set_line_coding: failed, err=%d\n",
+ USBDEVNAME(sc->sc_dev), err));
+ return (err);
+ }
+
+ sc->sc_line_state = *state;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+umodem_activate(self, act)
+ device_ptr_t self;
+ enum devact act;
+{
+ struct umodem_softc *sc = (struct umodem_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+USB_DETACH(umodem)
+{
+ USB_DETACH_START(umodem, sc);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ int maj, mn;
+
+ DPRINTF(("umodem_detach: sc=%p flags=%d tp=%p\n",
+ sc, flags, sc->sc_tty));
+#elif defined(__FreeBSD__)
+ DPRINTF(("umodem_detach: sc=%p flags=%d, tp=%p\n",
+ sc, 0, sc->sc_tty));
+#endif
+
+ sc->sc_dying = 1;
+
+#ifdef DIAGNOSTIC
+ if (sc->sc_tty == 0) {
+ DPRINTF(("umodem_detach: no tty\n"));
+ return (0);
+ }
+#endif
+
+ /* use refernce count? XXX */
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == umodemopen)
+ break;
+
+ /* Nuke the vnodes for any open instances. */
+ mn = self->dv_unit;
+ vdevgone(maj, mn, mn, VCHR);
+ vdevgone(maj, mn, mn | UMODEMDIALOUT_MASK, VCHR);
+ vdevgone(maj, mn, mn | UMODEMCALLUNIT_MASK, VCHR);
+#elif defined(__FreeBSD__)
+ /* XXX not yet implemented */
#endif
#if defined(__FreeBSD__)
-DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0);
+ destroy_dev(sc->dev);
+#endif
+
+ /* Detach and free the tty. */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ tty_detach(sc->sc_tty);
+ ttyfree(sc->sc_tty);
+ sc->sc_tty = 0;
#endif
+ return (0);
+}
+
+#if defined(__FreeBSD__)
+DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0);
+#endif
OpenPOWER on IntegriCloud