diff options
| author | ichiro <ichiro@NetBSD.org> | 2008-01-21 11:36:46 +0000 |
|---|---|---|
| committer | ichiro <ichiro@NetBSD.org> | 2008-01-21 11:36:46 +0000 |
| commit | 817e003a02975f634cd5aa20e8b6dd2c7b3c8fb5 (patch) | |
| tree | d5f5d2af3ee304d46e915b00f3694bd69c11cd49 /sys/dev | |
| parent | 654775cc67f536428e949bad24e7661f748a2cef (diff) | |
add full support device driver for Huawei E220 wireless modem
PR/37692 from Yojiro UO
---
uhmodem: device driver for huawei 3G wireless modem
* what it is?
A device driver for huawei 3G wireless modem, E220 and its valiations.
The devices are very simuler to ubsa device, but they need special care
to use as modem device.
This patch introduce "uhmodem (USB Huawei modem)" for the devices.
A uhmodem device has two com devices and one USB mass strage device.
The driver enable to use all of them.
* dmesg:
uhmodem0 at uhub0 port 1 configuration 1 interface 0
uhmodem0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
uhmodem0: mass storage only mode, reattach to enable modem
uhmodem0: at uhub0 port 1 (addr 2) disconnected
uhmodem0 detached
uhmodem0 at uhub0 port 1 configuration 1 interface 0
uhmodem0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ucom0 at uhmodem0 portno 0: modem
ucom1 at uhmodem0 portno 1: monitor
umass0 at uhub0 port 1 configuration 1 interface 2
umass0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
umass0: using SCSI over Bulk-Only
scsibus0 at umass0: 2 targets, 1 lun per target
cd0 at scsibus0 target 0 lun 0: <HUAWEI, Mass Storage, 2.31> cdrom removable
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/DEVNAMES | 3 | ||||
| -rw-r--r-- | sys/dev/usb/files.usb | 14 | ||||
| -rw-r--r-- | sys/dev/usb/ubsa.c | 508 | ||||
| -rw-r--r-- | sys/dev/usb/ubsa_common.c | 458 | ||||
| -rw-r--r-- | sys/dev/usb/ubsavar.h | 167 | ||||
| -rw-r--r-- | sys/dev/usb/uhmodem.c | 589 | ||||
| -rw-r--r-- | sys/dev/usb/umass_quirks.c | 33 |
7 files changed, 1296 insertions, 476 deletions
diff --git a/sys/dev/DEVNAMES b/sys/dev/DEVNAMES index e00105e4012..9aa4ddd1d20 100644 --- a/sys/dev/DEVNAMES +++ b/sys/dev/DEVNAMES @@ -1,4 +1,4 @@ -# $NetBSD: DEVNAMES,v 1.236 2008/01/09 14:44:51 xtraeme Exp $ +# $NetBSD: DEVNAMES,v 1.237 2008/01/21 11:36:46 ichiro Exp $ # # This file contains all used device names and defined attributes in # alphabetical order. New devices added to the system somewhere should first @@ -1328,6 +1328,7 @@ uhci MI uhid MI uhidbus MI Attribute uhidev MI +uhmodem MI uhub MI uipaq MI uirda MI diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 83b1892132b..6a34ca483cf 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $NetBSD: files.usb,v 1.81 2007/09/07 20:17:38 plunky Exp $ +# $NetBSD: files.usb,v 1.82 2008/01/21 11:36:46 ichiro Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -227,10 +227,18 @@ attach uvscom at usbdevif file dev/usb/uvscom.c uvscom # Belkin & other serial driver -device ubsa: ucombus -attach ubsa at usbdevif +define ubsa_common +file dev/usb/ubsa_common.c ubsa_common +device ubsa: ucombus, ubsa_common +#attach ubsa at usbdevif +attach ubsa at usbifif file dev/usb/ubsa.c ubsa +# Huawei E220 3G/HSDPA modem (ubsa) +device uhmodem: ucombus, ubsa_common +attach uhmodem at usbifif +file dev/usb/uhmodem.c uhmodem + # iPAQ PDAs device uipaq: ucombus attach uipaq at usbdevif diff --git a/sys/dev/usb/ubsa.c b/sys/dev/usb/ubsa.c index 4e95a5d6e44..3e7980b95bc 100644 --- a/sys/dev/usb/ubsa.c +++ b/sys/dev/usb/ubsa.c @@ -1,4 +1,4 @@ -/* $NetBSD: ubsa.c,v 1.19 2007/03/13 13:51:55 drochner Exp $ */ +/* $NetBSD: ubsa.c,v 1.20 2008/01/21 11:36:47 ichiro Exp $ */ /*- * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>. * All rights reserved. @@ -61,7 +61,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ubsa.c,v 1.19 2007/03/13 13:51:55 drochner Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ubsa.c,v 1.20 2008/01/21 11:36:47 ichiro Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -94,6 +94,7 @@ __KERNEL_RCSID(0, "$NetBSD: ubsa.c,v 1.19 2007/03/13 13:51:55 drochner Exp $"); #include <dev/usb/usb_quirks.h> #include <dev/usb/ucomvar.h> +#include <dev/usb/ubsavar.h> #ifdef UBSA_DEBUG Static int ubsadebug = 0; @@ -112,107 +113,6 @@ SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RW, #endif #define DPRINTF(x) DPRINTFN(0, x) -#define UBSA_MODVER 1 /* module version */ - -#define UBSA_CONFIG_INDEX 1 -#define UBSA_IFACE_INDEX 0 - -#define UBSA_INTR_INTERVAL 100 /* ms */ - -#define UBSA_SET_BAUDRATE 0x00 -#define UBSA_SET_STOP_BITS 0x01 -#define UBSA_SET_DATA_BITS 0x02 -#define UBSA_SET_PARITY 0x03 -#define UBSA_SET_DTR 0x0A -#define UBSA_SET_RTS 0x0B -#define UBSA_SET_BREAK 0x0C -#define UBSA_SET_FLOW_CTRL 0x10 - -#define UBSA_QUADUMTS_SET_PIN 0x22 - -#define UBSA_PARITY_NONE 0x00 -#define UBSA_PARITY_EVEN 0x01 -#define UBSA_PARITY_ODD 0x02 -#define UBSA_PARITY_MARK 0x03 -#define UBSA_PARITY_SPACE 0x04 - -#define UBSA_FLOW_NONE 0x0000 -#define UBSA_FLOW_OCTS 0x0001 -#define UBSA_FLOW_ODSR 0x0002 -#define UBSA_FLOW_IDSR 0x0004 -#define UBSA_FLOW_IDTR 0x0008 -#define UBSA_FLOW_IRTS 0x0010 -#define UBSA_FLOW_ORTS 0x0020 -#define UBSA_FLOW_UNKNOWN 0x0040 -#define UBSA_FLOW_OXON 0x0080 -#define UBSA_FLOW_IXON 0x0100 - -/* line status register */ -#define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */ -#define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */ -#define UBSA_LSR_BI 0x10 /* Break detected */ -#define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */ -#define UBSA_LSR_PE 0x04 /* Parity error */ -#define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */ -#define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */ -#define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */ - -/* modem status register */ -/* All deltas are from the last read of the MSR. */ -#define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */ -#define UBSA_MSR_RI 0x40 /* Current Ring Indicator */ -#define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */ -#define UBSA_MSR_CTS 0x10 /* Current Clear to Send */ -#define UBSA_MSR_DDCD 0x08 /* DCD has changed state */ -#define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */ -#define UBSA_MSR_DDSR 0x02 /* DSR has changed state */ -#define UBSA_MSR_DCTS 0x01 /* CTS has changed state */ - -struct ubsa_softc { - USBBASEDEVICE sc_dev; /* base device */ - usbd_device_handle sc_udev; /* USB device */ - usbd_interface_handle sc_iface; /* interface */ - - int sc_iface_number; /* interface number */ - - int sc_intr_number; /* interrupt number */ - usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */ - u_char *sc_intr_buf; /* interrupt buffer */ - int sc_isize; - - u_char sc_dtr; /* current DTR state */ - u_char sc_rts; /* current RTS state */ - - u_char sc_lsr; /* Local status register */ - u_char sc_msr; /* ubsa status register */ - - device_ptr_t sc_subdev; /* ucom device */ - - u_char sc_dying; /* disconnecting */ - u_char sc_quadumts; - -}; - -Static void ubsa_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); - -Static void ubsa_get_status(void *, int, u_char *, u_char *); -Static void ubsa_set(void *, int, int, int); -Static int ubsa_param(void *, int, struct termios *); -Static int ubsa_open(void *, int); -Static void ubsa_close(void *, int); - -Static void ubsa_break(struct ubsa_softc *sc, int onoff); -Static int ubsa_request(struct ubsa_softc *, u_int8_t, u_int16_t); -Static void ubsa_dtr(struct ubsa_softc *, int); -Static void ubsa_quadumts_dtr(struct ubsa_softc *, int); -Static void ubsa_rts(struct ubsa_softc *, int); -Static void ubsa_quadumts_rts(struct ubsa_softc *, int); -Static void ubsa_baudrate(struct ubsa_softc *, speed_t); -Static void ubsa_parity(struct ubsa_softc *, tcflag_t); -Static void ubsa_databits(struct ubsa_softc *, tcflag_t); -Static void ubsa_stopbits(struct ubsa_softc *, tcflag_t); -Static void ubsa_flow(struct ubsa_softc *, tcflag_t, tcflag_t); - struct ucom_methods ubsa_methods = { ubsa_get_status, ubsa_set, @@ -273,6 +173,8 @@ USB_ATTACH(ubsa) usbd_devinfo_free(devinfop); sc->sc_udev = dev; + sc->sc_config_index = UBSA_DEFAULT_CONFIG_INDEX; + sc->sc_numif = 1; /* default device has one interface */ /* * initialize rts, dtr variables to something @@ -297,13 +199,8 @@ USB_ATTACH(ubsa) DPRINTF(("ubsa attach: sc = %p\n", sc)); - /* initialize endpoints */ - uca.bulkin = uca.bulkout = -1; - sc->sc_intr_number = -1; - sc->sc_intr_pipe = NULL; - /* Move the device into the configured state. */ - err = usbd_set_config_index(dev, UBSA_CONFIG_INDEX, 1); + err = usbd_set_config_index(dev, sc->sc_config_index, 1); if (err) { printf("%s: failed to set configuration: %s\n", devname, usbd_errstr(err)); @@ -321,28 +218,31 @@ USB_ATTACH(ubsa) goto error; } - /* get the first interface */ - err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX, - &sc->sc_iface); + sc->sc_intr_number = -1; + sc->sc_intr_pipe = NULL; + + /* get the interfaces */ + err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX_OFFSET, + &sc->sc_iface[0]); if (err) { - printf("%s: failed to get interface: %s\n", - devname, usbd_errstr(err)); + /* can not get main interface */ sc->sc_dying = 1; goto error; } /* Find the endpoints */ + id = usbd_get_interface_descriptor(sc->sc_iface[0]); + sc->sc_iface_number[0] = id->bInterfaceNumber; - id = usbd_get_interface_descriptor(sc->sc_iface); - sc->sc_iface_number = id->bInterfaceNumber; + /* initialize endpoints */ + uca.bulkin = uca.bulkout = -1; for (i = 0; i < id->bNumEndpoints; i++) { - ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + ed = usbd_interface2endpoint_descriptor(sc->sc_iface[0], i); if (ed == NULL) { printf("%s: no endpoint descriptor for %d\n", - USBDEVNAME(sc->sc_dev), i); - sc->sc_dying = 1; - goto error; + USBDEVNAME(sc->sc_dev), i); + break; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && @@ -358,7 +258,7 @@ USB_ATTACH(ubsa) uca.bulkout = ed->bEndpointAddress; uca.obufsize = UGETW(ed->wMaxPacketSize); } - } + } /* end of Endpoint loop */ if (sc->sc_intr_number == -1) { printf("%s: Could not find interrupt in\n", devname); @@ -378,25 +278,23 @@ USB_ATTACH(ubsa) goto error; } - uca.portno = UCOM_UNK_PORTNO; + uca.portno = 0; /* bulkin, bulkout set above */ uca.ibufsizepad = uca.ibufsize; uca.opkthdrlen = 0; uca.device = dev; - uca.iface = sc->sc_iface; + uca.iface = sc->sc_iface[0]; uca.methods = &ubsa_methods; uca.arg = sc; uca.info = NULL; + DPRINTF(("ubsa: int#=%d, in = 0x%x, out = 0x%x, intr = 0x%x\n", + i, uca.bulkin, uca.bulkout, sc->sc_intr_number)); + sc->sc_subdevs[0] = config_found_sm_loc(self, "ucombus", NULL, &uca, + ucomprint, ucomsubmatch); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); - DPRINTF(("ubsa: in = 0x%x, out = 0x%x, intr = 0x%x\n", - uca.bulkin, uca.bulkout, sc->sc_intr_number)); - - sc->sc_subdev = config_found_sm_loc(self, "ucombus", NULL, &uca, - ucomprint, ucomsubmatch); - USB_ATTACH_SUCCESS_RETURN; error: @@ -406,6 +304,7 @@ error: USB_DETACH(ubsa) { USB_DETACH_START(ubsa, sc); + int i; int rv = 0; @@ -419,9 +318,11 @@ USB_DETACH(ubsa) } sc->sc_dying = 1; - if (sc->sc_subdev != NULL) { - rv = config_detach(sc->sc_subdev, flags); - sc->sc_subdev = NULL; + for (i = 0; i < sc->sc_numif; i++) { + if (sc->sc_subdevs[i] != NULL) { + rv |= config_detach(sc->sc_subdevs[i], flags); + sc->sc_subdevs[i] = NULL; + } } usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, @@ -435,353 +336,20 @@ ubsa_activate(device_ptr_t self, enum devact act) { struct ubsa_softc *sc = (struct ubsa_softc *)self; int rv = 0; + int i; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); case DVACT_DEACTIVATE: - if (sc->sc_subdev != NULL) - rv = config_deactivate(sc->sc_subdev); + for (i = 0; i < sc->sc_numif; i++) { + if (sc->sc_subdevs[i] != NULL) + rv |= config_deactivate(sc->sc_subdevs[i]); + } sc->sc_dying = 1; break; } return (rv); } -Static int -ubsa_request(struct ubsa_softc *sc, u_int8_t request, u_int16_t value) -{ - usb_device_request_t req; - usbd_status err; - - if (sc->sc_quadumts) - req.bmRequestType = UT_WRITE_CLASS_INTERFACE; - else - req.bmRequestType = UT_WRITE_VENDOR_DEVICE; - - req.bRequest = request; - USETW(req.wValue, value); - USETW(req.wIndex, sc->sc_iface_number); - USETW(req.wLength, 0); - - err = usbd_do_request(sc->sc_udev, &req, 0); - if (err) - printf("%s: ubsa_request: %s\n", - USBDEVNAME(sc->sc_dev), usbd_errstr(err)); - return (err); -} - -Static void -ubsa_dtr(struct ubsa_softc *sc, int onoff) -{ - - DPRINTF(("ubsa_dtr: onoff = %d\n", onoff)); - - if (sc->sc_dtr == onoff) - return; - sc->sc_dtr = onoff; - - ubsa_request(sc, UBSA_SET_DTR, onoff ? 1 : 0); -} - -Static void -ubsa_rts(struct ubsa_softc *sc, int onoff) -{ - - DPRINTF(("ubsa_rts: onoff = %d\n", onoff)); - - if (sc->sc_rts == onoff) - return; - sc->sc_rts = onoff; - - ubsa_request(sc, UBSA_SET_RTS, onoff ? 1 : 0); -} - -Static void -ubsa_quadumts_dtr(struct ubsa_softc *sc, int onoff) -{ - - DPRINTF(("ubsa_dtr: onoff = %d\n", onoff)); - - if (sc->sc_dtr == onoff) - return; - sc->sc_dtr = onoff; - - ubsa_request(sc, UBSA_QUADUMTS_SET_PIN, (sc->sc_rts ? 2 : 0)+(sc->sc_dtr ? 1 : 0)); -} - -Static void -ubsa_quadumts_rts(struct ubsa_softc *sc, int onoff) -{ - - DPRINTF(("ubsa_rts: onoff = %d\n", onoff)); - - if (sc->sc_rts == onoff) - return; - sc->sc_rts = onoff; - - ubsa_request(sc, UBSA_QUADUMTS_SET_PIN, (sc->sc_rts ? 2 : 0)+(sc->sc_dtr ? 1 : 0)); -} - -Static void -ubsa_break(struct ubsa_softc *sc, int onoff) -{ - DPRINTF(("ubsa_rts: onoff = %d\n", onoff)); - - ubsa_request(sc, UBSA_SET_BREAK, onoff ? 1 : 0); -} - -Static void -ubsa_set(void *addr, int portno, int reg, int onoff) -{ - struct ubsa_softc *sc; - - sc = addr; - switch (reg) { - case UCOM_SET_DTR: - if (sc->sc_quadumts) - ubsa_quadumts_dtr(sc, onoff); - else - ubsa_dtr(sc, onoff); - break; - case UCOM_SET_RTS: - if (sc->sc_quadumts) - ubsa_quadumts_rts(sc, onoff); - else - ubsa_rts(sc, onoff); - break; - case UCOM_SET_BREAK: - if (!sc->sc_quadumts) - ubsa_break(sc, onoff); - break; - default: - break; - } -} - -Static void -ubsa_baudrate(struct ubsa_softc *sc, speed_t speed) -{ - u_int16_t value = 0; - - DPRINTF(("ubsa_baudrate: speed = %d\n", speed)); - - switch(speed) { - case B0: - break; - case B300: - case B600: - case B1200: - case B2400: - case B4800: - case B9600: - case B19200: - case B38400: - case B57600: - case B115200: - case B230400: - value = B230400 / speed; - break; - default: - printf("%s: ubsa_param: unsupported baudrate, " - "forcing default of 9600\n", - USBDEVNAME(sc->sc_dev)); - value = B230400 / B9600; - break; - }; - - if (speed == B0) { - ubsa_flow(sc, 0, 0); - ubsa_dtr(sc, 0); - ubsa_rts(sc, 0); - } else - ubsa_request(sc, UBSA_SET_BAUDRATE, value); -} - -Static void -ubsa_parity(struct ubsa_softc *sc, tcflag_t cflag) -{ - int value; - - DPRINTF(("ubsa_parity: cflag = 0x%x\n", cflag)); - - if (cflag & PARENB) - value = (cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN; - else - value = UBSA_PARITY_NONE; - - ubsa_request(sc, UBSA_SET_PARITY, value); -} - -Static void -ubsa_databits(struct ubsa_softc *sc, tcflag_t cflag) -{ - int value; - - DPRINTF(("ubsa_databits: cflag = 0x%x\n", cflag)); - - switch (cflag & CSIZE) { - case CS5: value = 0; break; - case CS6: value = 1; break; - case CS7: value = 2; break; - case CS8: value = 3; break; - default: - printf("%s: ubsa_param: unsupported databits requested, " - "forcing default of 8\n", - USBDEVNAME(sc->sc_dev)); - value = 3; - } - - ubsa_request(sc, UBSA_SET_DATA_BITS, value); -} - -Static void -ubsa_stopbits(struct ubsa_softc *sc, tcflag_t cflag) -{ - int value; - - DPRINTF(("ubsa_stopbits: cflag = 0x%x\n", cflag)); - - value = (cflag & CSTOPB) ? 1 : 0; - - ubsa_request(sc, UBSA_SET_STOP_BITS, value); -} - -Static void -ubsa_flow(struct ubsa_softc *sc, tcflag_t cflag, tcflag_t iflag) -{ - int value; - - DPRINTF(("ubsa_flow: cflag = 0x%x, iflag = 0x%x\n", cflag, iflag)); - - value = 0; - if (cflag & CRTSCTS) - value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS; - if (iflag & (IXON|IXOFF)) - value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON; - - ubsa_request(sc, UBSA_SET_FLOW_CTRL, value); -} - -Static int -ubsa_param(void *addr, int portno, struct termios *ti) -{ - struct ubsa_softc *sc = addr; - - DPRINTF(("ubsa_param: sc = %p\n", sc)); - - if (!sc->sc_quadumts) { - ubsa_baudrate(sc, ti->c_ospeed); - ubsa_parity(sc, ti->c_cflag); - ubsa_databits(sc, ti->c_cflag); - ubsa_stopbits(sc, ti->c_cflag); - ubsa_flow(sc, ti->c_cflag, ti->c_iflag); - } - - return (0); -} - -Static int -ubsa_open(void *addr, int portno) -{ - struct ubsa_softc *sc = addr; - int err; - - if (sc->sc_dying) - return (ENXIO); - - DPRINTF(("ubsa_open: sc = %p\n", sc)); - - if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { - sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); - err = usbd_open_pipe_intr(sc->sc_iface, - sc->sc_intr_number, - USBD_SHORT_XFER_OK, - &sc->sc_intr_pipe, - sc, - sc->sc_intr_buf, - sc->sc_isize, - ubsa_intr, - UBSA_INTR_INTERVAL); - if (err) { - printf("%s: cannot open interrupt pipe (addr %d)\n", - USBDEVNAME(sc->sc_dev), - sc->sc_intr_number); - return (EIO); - } - } - - return (0); -} - -Static void -ubsa_close(void *addr, int portno) -{ - struct ubsa_softc *sc = addr; - int err; - - if (sc->sc_dying) - return; - - DPRINTF(("ubsa_close: close\n")); - - if (sc->sc_intr_pipe != NULL) { - err = usbd_abort_pipe(sc->sc_intr_pipe); - if (err) - printf("%s: abort interrupt pipe failed: %s\n", - USBDEVNAME(sc->sc_dev), - usbd_errstr(err)); - err = usbd_close_pipe(sc->sc_intr_pipe); - if (err) - printf("%s: close interrupt pipe failed: %s\n", - USBDEVNAME(sc->sc_dev), - usbd_errstr(err)); - free(sc->sc_intr_buf, M_USBDEV); - sc->sc_intr_pipe = NULL; - } -} - -Static void -ubsa_intr(usbd_xfer_handle xfer, usbd_private_handle priv, - usbd_status status) -{ - struct ubsa_softc *sc = priv; - u_char *buf; - - buf = sc->sc_intr_buf; - if (sc->sc_dying) - return; - - if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) - return; - - DPRINTF(("%s: ubsa_intr: abnormal status: %s\n", - USBDEVNAME(sc->sc_dev), usbd_errstr(status))); - usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); - return; - } - - /* incidentally, Belkin adapter status bits match UART 16550 bits */ - sc->sc_lsr = buf[2]; - sc->sc_msr = buf[3]; - - DPRINTF(("%s: ubsa lsr = 0x%02x, msr = 0x%02x\n", - USBDEVNAME(sc->sc_dev), sc->sc_lsr, sc->sc_msr)); - - ucom_status_change((struct ucom_softc *)sc->sc_subdev); -} - -Static void -ubsa_get_status(void *addr, int portno, u_char *lsr, u_char *msr) -{ - struct ubsa_softc *sc = addr; - - DPRINTF(("ubsa_get_status\n")); - - if (lsr != NULL) - *lsr = sc->sc_lsr; - if (msr != NULL) - *msr = sc->sc_msr; -} diff --git a/sys/dev/usb/ubsa_common.c b/sys/dev/usb/ubsa_common.c new file mode 100644 index 00000000000..cedc6d48d37 --- /dev/null +++ b/sys/dev/usb/ubsa_common.c @@ -0,0 +1,458 @@ +/* $NetBSD: ubsa_common.c,v 1.1 2008/01/21 11:36:47 ichiro Exp $ */ +/*- + * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>. + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ichiro FUKUHARA (ichiro@ichiro.org). + * + * 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/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ubsa_common.c,v 1.1 2008/01/21 11:36:47 ichiro Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#ifdef __FreeBSD__ +#include <sys/bus.h> +#endif +#include <sys/ioccom.h> +#include <sys/fcntl.h> +#include <sys/conf.h> +#include <sys/tty.h> +#include <sys/file.h> +#if __FreeBSD_version >= 500014 +#include <sys/selinfo.h> +#else +#include <sys/select.h> +#endif +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/poll.h> +#include <sys/sysctl.h> +#include <sys/bus.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> + +#include <dev/usb/usbcdc.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> +#include <dev/usb/ucomvar.h> +#include <dev/usb/ubsavar.h> + +#ifdef UBSA_DEBUG +extern int ubsadebug; +#define DPRINTFN(n, x) do { \ + if (ubsadebug > (n)) \ + logprintf x; \ + } while (0) +#else +#define DPRINTFN(n, x) +#endif +#define DPRINTF(x) DPRINTFN(0, x) + +Static int +ubsa_request(struct ubsa_softc *sc, int portno, u_int8_t request, u_int16_t value) +{ + usb_device_request_t req; + usbd_status err; + + if (sc->sc_quadumts) + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + else + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + + if (portno > UBSA_MAXCONN) { + printf("%s: ubsa_request: invalid port(%d)#\n", + USBDEVNAME(sc->sc_dev), portno); + return USBD_INVAL; + } + + req.bRequest = request; + USETW(req.wValue, value); + USETW(req.wIndex, sc->sc_iface_number[portno]); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, 0); + if (err) + printf("%s: ubsa_request: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + return (err); +} + +Static void +ubsa_dtr(struct ubsa_softc *sc, int portno, int onoff) +{ + + DPRINTF(("ubsa_dtr: onoff = %d\n", onoff)); + + if (sc->sc_dtr == onoff) + return; + sc->sc_dtr = onoff; + + ubsa_request(sc, portno, UBSA_SET_DTR, onoff ? 1 : 0); +} + +Static void +ubsa_rts(struct ubsa_softc *sc, int portno, int onoff) +{ + + DPRINTF(("ubsa_rts: onoff = %d\n", onoff)); + + if (sc->sc_rts == onoff) + return; + sc->sc_rts = onoff; + + ubsa_request(sc, portno, UBSA_SET_RTS, onoff ? 1 : 0); +} + +Static void +ubsa_quadumts_dtr(struct ubsa_softc *sc, int portno, int onoff) +{ + + DPRINTF(("ubsa_dtr: onoff = %d\n", onoff)); + + if (sc->sc_dtr == onoff) + return; + sc->sc_dtr = onoff; + + ubsa_request(sc, portno, UBSA_QUADUMTS_SET_PIN, + (sc->sc_rts ? 2 : 0)+(sc->sc_dtr ? 1 : 0)); +} + +Static void +ubsa_quadumts_rts(struct ubsa_softc *sc, int portno, int onoff) +{ + + DPRINTF(("ubsa_rts: onoff = %d\n", onoff)); + + if (sc->sc_rts == onoff) + return; + sc->sc_rts = onoff; + + ubsa_request(sc, portno, UBSA_QUADUMTS_SET_PIN, + (sc->sc_rts ? 2 : 0)+(sc->sc_dtr ? 1 : 0)); +} + +Static void +ubsa_break(struct ubsa_softc *sc, int portno, int onoff) +{ + DPRINTF(("ubsa_rts: onoff = %d\n", onoff)); + + ubsa_request(sc, portno, UBSA_SET_BREAK, onoff ? 1 : 0); +} + +Static void +ubsa_set(void *addr, int portno, int reg, int onoff) +{ + struct ubsa_softc *sc; + + sc = addr; + switch (reg) { + case UCOM_SET_DTR: + if (sc->sc_quadumts) + ubsa_quadumts_dtr(sc, portno, onoff); + else + ubsa_dtr(sc, portno, onoff); + break; + case UCOM_SET_RTS: + if (sc->sc_quadumts) + ubsa_quadumts_rts(sc, portno, onoff); + else + ubsa_rts(sc, portno, onoff); + break; + case UCOM_SET_BREAK: + if (!sc->sc_quadumts) + ubsa_break(sc, portno, onoff); + break; + default: + break; + } +} + +Static void +ubsa_baudrate(struct ubsa_softc *sc, int portno, speed_t speed) +{ + u_int16_t value = 0; + + DPRINTF(("ubsa_baudrate: speed = %d\n", speed)); + + switch(speed) { + case B0: + break; + case B300: + case B600: + case B1200: + case B2400: + case B4800: + case B9600: + case B19200: + case B38400: + case B57600: + case B115200: + case B230400: + value = B230400 / speed; + break; + default: + printf("%s: ubsa_param: unsupported baudrate, " + "forcing default of 9600\n", + USBDEVNAME(sc->sc_dev)); + value = B230400 / B9600; + break; + }; + + if (speed == B0) { + ubsa_flow(sc, portno, 0, 0); + ubsa_dtr(sc, portno, 0); + ubsa_rts(sc, portno, 0); + } else + ubsa_request(sc, portno, UBSA_SET_BAUDRATE, value); +} + +Static void +ubsa_parity(struct ubsa_softc *sc, int portno, tcflag_t cflag) +{ + int value; + + DPRINTF(("ubsa_parity: cflag = 0x%x\n", cflag)); + + if (cflag & PARENB) + value = (cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN; + else + value = UBSA_PARITY_NONE; + + ubsa_request(sc, portno, UBSA_SET_PARITY, value); +} + +Static void +ubsa_databits(struct ubsa_softc *sc, int portno, tcflag_t cflag) +{ + int value; + + DPRINTF(("ubsa_databits: cflag = 0x%x\n", cflag)); + + switch (cflag & CSIZE) { + case CS5: value = 0; break; + case CS6: value = 1; break; + case CS7: value = 2; break; + case CS8: value = 3; break; + default: + printf("%s: ubsa_param: unsupported databits requested, " + "forcing default of 8\n", + USBDEVNAME(sc->sc_dev)); + value = 3; + } + + ubsa_request(sc, portno, UBSA_SET_DATA_BITS, value); +} + +Static void +ubsa_stopbits(struct ubsa_softc *sc, int portno, tcflag_t cflag) +{ + int value; + + DPRINTF(("ubsa_stopbits: cflag = 0x%x\n", cflag)); + + value = (cflag & CSTOPB) ? 1 : 0; + + ubsa_request(sc, portno, UBSA_SET_STOP_BITS, value); +} + +Static void +ubsa_flow(struct ubsa_softc *sc, int portno, tcflag_t cflag, tcflag_t iflag) +{ + int value; + + DPRINTF(("ubsa_flow: cflag = 0x%x, iflag = 0x%x\n", cflag, iflag)); + + value = 0; + if (cflag & CRTSCTS) + value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS; + if (iflag & (IXON|IXOFF)) + value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON; + + ubsa_request(sc, portno, UBSA_SET_FLOW_CTRL, value); +} + +Static int +ubsa_param(void *addr, int portno, struct termios *ti) +{ + struct ubsa_softc *sc = addr; + + DPRINTF(("ubsa_param: sc = %p\n", sc)); + + if (!sc->sc_quadumts) { + ubsa_baudrate(sc, portno, ti->c_ospeed); + ubsa_parity(sc, portno, ti->c_cflag); + ubsa_databits(sc, portno, ti->c_cflag); + ubsa_stopbits(sc, portno, ti->c_cflag); + ubsa_flow(sc, portno, ti->c_cflag, ti->c_iflag); + } + + return (0); +} + +Static int +ubsa_open(void *addr, int portno) +{ + struct ubsa_softc *sc = addr; + int err; + + if (sc->sc_dying) + return (ENXIO); + + if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { + sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); + /* XXX only iface# = 0 has intr line */ + /* XXX E220 specific? need to check */ + err = usbd_open_pipe_intr(sc->sc_iface[0], + sc->sc_intr_number, + USBD_SHORT_XFER_OK, + &sc->sc_intr_pipe, + sc, + sc->sc_intr_buf, + sc->sc_isize, + ubsa_intr, + UBSA_INTR_INTERVAL); + if (err) { + printf("%s: cannot open interrupt pipe (addr %d)\n", + USBDEVNAME(sc->sc_dev), + sc->sc_intr_number); + return (EIO); + } + } + + return (0); +} + +Static void +ubsa_close(void *addr, int portno) +{ + struct ubsa_softc *sc = addr; + int err; + + if (sc->sc_dying) + return; + + DPRINTF(("ubsa_close: close\n")); + + if (sc->sc_intr_pipe != NULL) { + err = usbd_abort_pipe(sc->sc_intr_pipe); + if (err) + printf("%s: abort interrupt pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), + usbd_errstr(err)); + err = usbd_close_pipe(sc->sc_intr_pipe); + if (err) + printf("%s: close interrupt pipe failed: %s\n", + USBDEVNAME(sc->sc_dev), + usbd_errstr(err)); + free(sc->sc_intr_buf, M_USBDEV); + sc->sc_intr_pipe = NULL; + } +} + +Static void +ubsa_intr(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + struct ubsa_softc *sc = priv; + u_char *buf; + int i; + + buf = sc->sc_intr_buf; + if (sc->sc_dying) + return; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + return; + + DPRINTF(("%s: ubsa_intr: abnormal status: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(status))); + usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); + return; + } + + /* incidentally, Belkin adapter status bits match UART 16550 bits */ + sc->sc_lsr = buf[2]; + sc->sc_msr = buf[3]; + + DPRINTF(("%s: ubsa lsr = 0x%02x, msr = 0x%02x\n", + USBDEVNAME(sc->sc_dev), sc->sc_lsr, sc->sc_msr)); + + for (i = 0; i < sc->sc_numif; i++) { + ucom_status_change((struct ucom_softc *)sc->sc_subdevs[i]); + } +} + +Static void +ubsa_get_status(void *addr, int portno, u_char *lsr, u_char *msr) +{ + struct ubsa_softc *sc = addr; + + DPRINTF(("ubsa_get_status\n")); + + if (lsr != NULL) + *lsr = sc->sc_lsr; + if (msr != NULL) + *msr = sc->sc_msr; +} + diff --git a/sys/dev/usb/ubsavar.h b/sys/dev/usb/ubsavar.h new file mode 100644 index 00000000000..bb10250f896 --- /dev/null +++ b/sys/dev/usb/ubsavar.h @@ -0,0 +1,167 @@ +/* $NetBSD: ubsavar.h,v 1.1 2008/01/21 11:36:47 ichiro Exp $ */ +/*- + * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>. + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ichiro FUKUHARA (ichiro@ichiro.org). + * + * 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. + */ + +#define UBSA_MODVER 1 /* module version */ + +#define UBSA_DEFAULT_CONFIG_INDEX 1 +#define UBSA_IFACE_INDEX_OFFSET 0 + +#define UBSA_INTR_INTERVAL 100 /* ms */ + +#define UBSA_SET_BAUDRATE 0x00 +#define UBSA_SET_STOP_BITS 0x01 +#define UBSA_SET_DATA_BITS 0x02 +#define UBSA_SET_PARITY 0x03 +#define UBSA_SET_DTR 0x0A +#define UBSA_SET_RTS 0x0B +#define UBSA_SET_BREAK 0x0C +#define UBSA_SET_FLOW_CTRL 0x10 + +#define UBSA_QUADUMTS_SET_PIN 0x22 + +#define UBSA_PARITY_NONE 0x00 +#define UBSA_PARITY_EVEN 0x01 +#define UBSA_PARITY_ODD 0x02 +#define UBSA_PARITY_MARK 0x03 +#define UBSA_PARITY_SPACE 0x04 + +#define UBSA_FLOW_NONE 0x0000 +#define UBSA_FLOW_OCTS 0x0001 +#define UBSA_FLOW_ODSR 0x0002 +#define UBSA_FLOW_IDSR 0x0004 +#define UBSA_FLOW_IDTR 0x0008 +#define UBSA_FLOW_IRTS 0x0010 +#define UBSA_FLOW_ORTS 0x0020 +#define UBSA_FLOW_UNKNOWN 0x0040 +#define UBSA_FLOW_OXON 0x0080 +#define UBSA_FLOW_IXON 0x0100 + +/* line status register */ +#define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */ +#define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */ +#define UBSA_LSR_BI 0x10 /* Break detected */ +#define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */ +#define UBSA_LSR_PE 0x04 /* Parity error */ +#define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */ +#define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */ +#define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */ + +/* modem status register */ +/* All deltas are from the last read of the MSR. */ +#define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */ +#define UBSA_MSR_RI 0x40 /* Current Ring Indicator */ +#define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */ +#define UBSA_MSR_CTS 0x10 /* Current Clear to Send */ +#define UBSA_MSR_DDCD 0x08 /* DCD has changed state */ +#define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */ +#define UBSA_MSR_DDSR 0x02 /* DSR has changed state */ +#define UBSA_MSR_DCTS 0x01 /* CTS has changed state */ + +#define UBSA_MAXCONN 3 + +struct ubsa_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; /* USB device */ + usbd_interface_handle sc_iface[UBSA_MAXCONN]; /* interface */ + + int sc_iface_number[UBSA_MAXCONN]; /* interface number */ + int sc_config_index; /* USB CONFIG_INDEX */ + + int sc_intr_number; /* interrupt number */ + usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */ + u_char *sc_intr_buf; /* interrupt buffer */ + int sc_isize; + + u_char sc_dtr; /* current DTR state */ + u_char sc_rts; /* current RTS state */ + + u_char sc_lsr; /* Local status register */ + u_char sc_msr; /* ubsa status register */ + + device_ptr_t sc_subdevs[UBSA_MAXCONN]; /* ucom device */ + int sc_numif; /* number of interfaces */ + + u_char sc_dying; /* disconnecting */ + u_char sc_quadumts; +}; + + +Static void ubsa_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); + +Static void ubsa_get_status(void *, int, u_char *, u_char *); +Static void ubsa_set(void *, int, int, int); +Static int ubsa_param(void *, int, struct termios *); +Static int ubsa_open(void *, int); +Static void ubsa_close(void *, int); + +Static void ubsa_break(struct ubsa_softc *sc, int, int onoff); +Static int ubsa_request(struct ubsa_softc *, int, u_int8_t, u_int16_t); +Static void ubsa_dtr(struct ubsa_softc *, int, int); +Static void ubsa_quadumts_dtr(struct ubsa_softc *, int, int); +Static void ubsa_rts(struct ubsa_softc *, int, int); +Static void ubsa_quadumts_rts(struct ubsa_softc *, int, int); +Static void ubsa_baudrate(struct ubsa_softc *, int, speed_t); +Static void ubsa_parity(struct ubsa_softc *, int, tcflag_t); +Static void ubsa_databits(struct ubsa_softc *, int, tcflag_t); +Static void ubsa_stopbits(struct ubsa_softc *, int, tcflag_t); +Static void ubsa_flow(struct ubsa_softc *, int, tcflag_t, tcflag_t); + diff --git a/sys/dev/usb/uhmodem.c b/sys/dev/usb/uhmodem.c new file mode 100644 index 00000000000..cd6580fe855 --- /dev/null +++ b/sys/dev/usb/uhmodem.c @@ -0,0 +1,589 @@ +/* $NetBSD: uhmodem.c,v 1.1 2008/01/21 11:36:47 ichiro Exp $ */ + +/* + * Copyright (c) 2008 Yojiro UO <yuo@nui.org>. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*- + * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>. + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ichiro FUKUHARA (ichiro@ichiro.org). + * + * 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/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: uhmodem.c,v 1.1 2008/01/21 11:36:47 ichiro Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/ioccom.h> +#include <sys/fcntl.h> +#include <sys/conf.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/poll.h> +#include <sys/sysctl.h> +#include <sys/bus.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> + +#include <dev/usb/usbcdc.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usb_quirks.h> +#include <dev/usb/ucomvar.h> +#include <dev/usb/ubsavar.h> + + +#define UHMODEMIBUFSIZE 4096 +#define UHMODEMOBUFSIZE 4096 + +#ifdef UHMODEM_DEBUG +Static int uhmodemdebug = 0; +#define DPRINTFN(n, x) do { \ + if (uhmodemdebug > (n)) \ + logprintf x; \ + } while (0) +#else +#define DPRINTFN(n, x) +#endif +#define DPRINTF(x) DPRINTFN(0, x) + +Static int uhmodem_open(void *, int); +Static usbd_status e220_modechange_request(usbd_device_handle); +Static usbd_status e220_endpointhalt(usbd_device_handle); +#if 0 +Static usbd_status e220_testreq(usbd_device_handle); +#endif +void e220_modechange_request_test(usbd_device_handle); + +#define UHMODEM_MAXCONN 2 + +struct uhmodem_softc { + struct ubsa_softc sc_ubsa; +}; + +struct ucom_methods uhmodem_methods = { + ubsa_get_status, + ubsa_set, + ubsa_param, + NULL, + uhmodem_open, + ubsa_close, + NULL, + NULL +}; + +Static const struct usb_devno uhmodem_devs[] = { + /* HUAWEI E220 / Emobile D0[12]HW */ + { USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, +}; +#define uhmodem_lookup(v, p) usb_lookup(uhmodem_devs, v, p) + +USB_DECLARE_DRIVER(uhmodem); + +USB_MATCH(uhmodem) +{ + USB_IFMATCH_START(uhmodem, uaa); + + if (uhmodem_lookup(uaa->vendor, uaa->product) != NULL) + /* XXX interface# 0,1 provide modem function, but this driver + handles all modem in single device. */ + if (uaa->ifaceno == 0) + return (UMATCH_VENDOR_PRODUCT); + return (UMATCH_NONE); +} + +USB_ATTACH(uhmodem) +{ + USB_IFATTACH_START(uhmodem, sc, uaa); + usbd_device_handle dev = uaa->device; + usb_config_descriptor_t *cdesc; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + char *devinfop; + const char *devname = USBDEVNAME(sc->sc_ubsa.sc_dev); + usbd_status err; + struct ucom_attach_args uca; + int i; + int j; + char comname[16]; + + devinfop = usbd_devinfo_alloc(dev, 0); + USB_ATTACH_SETUP; + printf("%s: %s\n", devname, devinfop); + usbd_devinfo_free(devinfop); + + sc->sc_ubsa.sc_udev = dev; + sc->sc_ubsa.sc_config_index = UBSA_DEFAULT_CONFIG_INDEX; + sc->sc_ubsa.sc_numif = 1; /* defaut device has one interface */ + + /* Hauwei E220 need special request to change its mode to modem */ + if ((uaa->ifaceno == 0) && (uaa->class != 255)) { + err = e220_modechange_request(dev); + if (err) { + printf("%s: failed to change mode: %s\n", + devname, usbd_errstr(err)); + sc->sc_ubsa.sc_dying = 1; + goto error; + } + printf("%s: mass storage only mode, reattach to enable modem\n", + devname); + sc->sc_ubsa.sc_dying = 1; + goto error; + } + + /* + * initialize rts, dtr variables to something + * different from boolean 0, 1 + */ + sc->sc_ubsa.sc_dtr = -1; + sc->sc_ubsa.sc_rts = -1; + + sc->sc_ubsa.sc_quadumts = 1; + sc->sc_ubsa.sc_config_index = 0; + sc->sc_ubsa.sc_numif = 2; /* E220 has 2coms */ + + DPRINTF(("uhmodem attach: sc = %p\n", sc)); + + /* Move the device into the configured state. */ + err = usbd_set_config_index(dev, sc->sc_ubsa.sc_config_index, 1); + if (err) { + printf("%s: failed to set configuration: %s\n", + devname, usbd_errstr(err)); + sc->sc_ubsa.sc_dying = 1; + goto error; + } + + /* get the config descriptor */ + cdesc = usbd_get_config_descriptor(sc->sc_ubsa.sc_udev); + if (cdesc == NULL) { + printf("%s: failed to get configuration descriptor\n", + devname); + sc->sc_ubsa.sc_dying = 1; + goto error; + } + + sc->sc_ubsa.sc_intr_number = -1; + sc->sc_ubsa.sc_intr_pipe = NULL; + + /* get the interfaces */ + for (i = 0; i < sc->sc_ubsa.sc_numif; i++) { + err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX_OFFSET+i, + &sc->sc_ubsa.sc_iface[i]); + if (err) { + if (i == 0){ + /* can not get main interface */ + sc->sc_ubsa.sc_dying = 1; + goto error; + } else + break; + } + + /* Find the endpoints */ + id = usbd_get_interface_descriptor(sc->sc_ubsa.sc_iface[i]); + sc->sc_ubsa.sc_iface_number[i] = id->bInterfaceNumber; + + /* initialize endpoints */ + uca.bulkin = uca.bulkout = -1; + + for (j = 0; j < id->bNumEndpoints; j++) { + ed = usbd_interface2endpoint_descriptor( + sc->sc_ubsa.sc_iface[i], j); + if (ed == NULL) { + printf("%s: no endpoint descriptor for %d (interface: %d)\n", + devname, j, i); + break; + } + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + sc->sc_ubsa.sc_intr_number = ed->bEndpointAddress; + sc->sc_ubsa.sc_isize = UGETW(ed->wMaxPacketSize); + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + uca.bulkin = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + uca.bulkout = ed->bEndpointAddress; + } + } /* end of Endpoint loop */ + + if (sc->sc_ubsa.sc_intr_number == -1) { + printf("%s: HUAWEI E220 need to re-attach to enable modem function\n", devname); + if (i == 0) { + /* could not get intr for main tty */ + sc->sc_ubsa.sc_dying = 1; + goto error; + } else + break; + } + if (uca.bulkin == -1) { + printf("%s: Could not find data bulk in\n", devname); + sc->sc_ubsa.sc_dying = 1; + goto error; + } + + if (uca.bulkout == -1) { + printf("%s: Could not find data bulk out\n", devname); + sc->sc_ubsa.sc_dying = 1; + goto error; + } + + switch (i) { + case 0: + sprintf(comname, "modem"); + break; + case 1: + sprintf(comname, "monitor"); + break; + case 2: + sprintf(comname, "unknown"); + break; + default: + sprintf(comname, "int#%d", i); + } + + uca.portno = i; + /* bulkin, bulkout set above */ + uca.ibufsize = UHMODEMIBUFSIZE; + uca.obufsize = UHMODEMOBUFSIZE; + uca.ibufsizepad = UHMODEMIBUFSIZE; + uca.opkthdrlen = 0; + uca.device = dev; + uca.iface = sc->sc_ubsa.sc_iface[i]; + uca.methods = &uhmodem_methods; + uca.arg = &sc->sc_ubsa; + uca.info = comname; + DPRINTF(("uhmodem: int#=%d, in = 0x%x, out = 0x%x, intr = 0x%x\n", + i, uca.bulkin, uca.bulkout, sc->sc_ubsa.sc_intr_number)); + sc->sc_ubsa.sc_subdevs[i] = config_found_sm_loc(self, "ucombus", NULL, + &uca, ucomprint, ucomsubmatch); + } /* end of Interface loop */ + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_ubsa.sc_udev, + USBDEV(sc->sc_ubsa.sc_dev)); + + USB_ATTACH_SUCCESS_RETURN; + +error: + USB_ATTACH_ERROR_RETURN; +} + +USB_DETACH(uhmodem) +{ + USB_DETACH_START(uhmodem, sc); + int i; + int rv = 0; + + DPRINTF(("uhmodem_detach: sc = %p\n", sc)); + + if (sc->sc_ubsa.sc_intr_pipe != NULL) { + usbd_abort_pipe(sc->sc_ubsa.sc_intr_pipe); + usbd_close_pipe(sc->sc_ubsa.sc_intr_pipe); + free(sc->sc_ubsa.sc_intr_buf, M_USBDEV); + sc->sc_ubsa.sc_intr_pipe = NULL; + } + + sc->sc_ubsa.sc_dying = 1; + for (i = 0; i < sc->sc_ubsa.sc_numif; i++) { + if (sc->sc_ubsa.sc_subdevs[i] != NULL) { + rv |= config_detach(sc->sc_ubsa.sc_subdevs[i], flags); + sc->sc_ubsa.sc_subdevs[i] = NULL; + } + } + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ubsa.sc_udev, + USBDEV(sc->sc_ubsa.sc_dev)); + + return (rv); +} + +int +uhmodem_activate(device_ptr_t self, enum devact act) +{ + struct uhmodem_softc *sc = (struct uhmodem_softc *)self; + int rv = 0; + int i; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + + case DVACT_DEACTIVATE: + for (i = 0; i < sc->sc_ubsa.sc_numif; i++) { + if (sc->sc_ubsa.sc_subdevs[i] != NULL) + rv |= config_deactivate(sc->sc_ubsa.sc_subdevs[i]); + } + sc->sc_ubsa.sc_dying = 1; + break; + } + return (rv); +} + +Static int +uhmodem_open(void *addr, int portno) +{ + struct ubsa_softc *sc = addr; + usbd_status err; + + if (sc->sc_dying) + return (ENXIO); + + DPRINTF(("%s: sc = %p\n", __func__, sc)); + + err = e220_endpointhalt(sc->sc_udev); + if (err) + printf("%s: endpointhalt fail\n", __func__); + else + usbd_delay_ms(sc->sc_udev, 50); +#if 0 /* currenly disable */ + err = e220_testreq(sc->sc_udev); + if (err) + printf("%s: send testreq fail\n", __func__); + else + usbd_delay_ms(sc->sc_udev, 50); +#endif + + if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { + sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); + /* XXX only iface# = 0 has intr line */ + /* XXX E220 specific? need to check */ + err = usbd_open_pipe_intr(sc->sc_iface[0], + sc->sc_intr_number, + USBD_SHORT_XFER_OK, + &sc->sc_intr_pipe, + sc, + sc->sc_intr_buf, + sc->sc_isize, + ubsa_intr, + UBSA_INTR_INTERVAL); + if (err) { + printf("%s: cannot open interrupt pipe (addr %d)\n", + USBDEVNAME(sc->sc_dev), + sc->sc_intr_number); + return (EIO); + } + } + + return (0); +} + +/* + * Hauwei E220 needs special request to enable modem function. + * -- DEVICE_REMOTE_WAKEUP ruquest to endpoint 2. + */ +Static usbd_status +e220_modechange_request(usbd_device_handle dev) +{ +#define E220_MODE_CHANGE_REQUEST 0x2 + usb_device_request_t req; + usbd_status err; + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, E220_MODE_CHANGE_REQUEST); + USETW(req.wLength, 0); + + DPRINTF(("%s: send e220 mode change request\n", __func__)); + err = usbd_do_request(dev, &req, 0); + if (err) { + DPRINTF(("%s: E220 mode change fail\n", __func__)); + return (EIO); + } + + return (0); +#undef E220_MODE_CHANGE_REQUEST +} + +Static usbd_status +e220_endpointhalt(usbd_device_handle dev) +{ + usb_device_request_t req; + usbd_status err; + + /* CLEAR feature / endpoint halt to modem i/o */ + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_HALT); + USETW(req.wIndex, 0x0082); /* should get value from softc etc.*/ + USETW(req.wLength, 0); + err = usbd_do_request(dev, &req, 0); + if (err) { + DPRINTF(("%s: E220 request test ENDPOINT_HALT fail\n", __func__)); + return (EIO); + } + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_HALT); + USETW(req.wIndex, 0x0002); /* should get value from softc etc.*/ + USETW(req.wLength, 0); + err = usbd_do_request(dev, &req, 0); + if (err) { + DPRINTF(("%s: E220 request test ENDPOINT_HALT fail\n", __func__)); + return (EIO); + } + + return (0); +} + +#if 0 +/* + * Windows device driver send these sequens of USB requests. + * However currently I can't understand what the messege is, + * disable this code when I get more information about it. + */ +Static usbd_status +e220_testreq(usbd_device_handle dev) +{ + uint8_t data[8]; + usb_device_request_t req; + usbd_status err; + int i; + + /* vendor specific unknown requres */ + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = 0x02; + USETW(req.wValue, 0x0001); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, 2); + data[0] = 0x0; + data[1] = 0x0; + err = usbd_do_request(dev, &req, data); + if (err) + goto error; + + /* vendor specific unknown sequence */ +#define E220_CLASS_SETUP 0x22 +#define E220_CLASS_READ 0x21 +#define E220_CLASS_WRITE 0x20 + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = E220_CLASS_SETUP; // 0x22 + USETW(req.wValue, 0x0001); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, 0); + err = usbd_do_request(dev, &req, 0); + if (err) + goto error; + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = E220_CLASS_READ; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, 7); + err = usbd_do_request(dev, &req, &data); + if (err) + goto error; + + data[1] = 0x8; + data[2] = 0x7; + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = E220_CLASS_WRITE; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, 7); + err = usbd_do_request(dev, &req, data); + if (err) + goto error; + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = E220_CLASS_READ; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, 7); + err = usbd_do_request(dev, &req, &data); + if (err) + goto error; + printf("%s(0x21):", __func__); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = E220_CLASS_SETUP; // 0x22 + USETW(req.wValue, 0x0003); + USETW(req.wIndex, 0x0000); + USETW(req.wLength, 0); + err = usbd_do_request(dev, &req, 0); + if (err) + goto error; + + return (0); +error: + DPRINTF(("%s: E220 request test SETUP fail\n", __func__)); + return (EIO); +} +#endif + diff --git a/sys/dev/usb/umass_quirks.c b/sys/dev/usb/umass_quirks.c index 68667be3f90..21491b8746d 100644 --- a/sys/dev/usb/umass_quirks.c +++ b/sys/dev/usb/umass_quirks.c @@ -1,4 +1,4 @@ -/* $NetBSD: umass_quirks.c,v 1.71 2007/01/09 16:46:02 christos Exp $ */ +/* $NetBSD: umass_quirks.c,v 1.72 2008/01/21 11:36:47 ichiro Exp $ */ /* * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc. @@ -39,7 +39,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: umass_quirks.c,v 1.71 2007/01/09 16:46:02 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: umass_quirks.c,v 1.72 2008/01/21 11:36:47 ichiro Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -58,6 +58,7 @@ __KERNEL_RCSID(0, "$NetBSD: umass_quirks.c,v 1.71 2007/01/09 16:46:02 christos E Static usbd_status umass_init_insystem(struct umass_softc *); Static usbd_status umass_init_shuttle(struct umass_softc *); +Static usbd_status umass_init_e220(struct umass_softc *); Static void umass_fixup_sony(struct umass_softc *); @@ -194,6 +195,13 @@ Static const struct umass_quirk umass_quirks[] = { UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO, NULL, NULL }, + { { USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, + UMASS_WPROTO_UNSPEC, UMASS_CPROTO_UNSPEC, + 0, + 0, + UMASS_QUIRK_USE_DEFAULTMATCH, /* use default MATCH function */ + umass_init_e220, NULL + }, }; const struct umass_quirk * @@ -244,3 +252,24 @@ umass_fixup_sony(struct umass_softc *sc) if (id->bInterfaceSubClass == 0xff) sc->sc_cmd = UMASS_CPROTO_RBC; } + +Static usbd_status +umass_init_e220(struct umass_softc *sc) +{ +#define E220_UMASS_INTERFACE 2 + usbd_status err; + + if (sc->sc_ifaceno != E220_UMASS_INTERFACE) + return (USBD_NOT_CONFIGURED); + + err = usbd_device2interface_handle(sc->sc_udev, sc->sc_ifaceno, &sc->sc_iface); + if (err) { + DPRINTF(UDMASS_USB, + ("%s: could not switch to Alt Interface %d\n", + USBDEVNAME(sc->sc_dev), sc->sc_ifaceno)); + return (err); + } + + return (USBD_NORMAL_COMPLETION); +#undef E220_UMASS_INTERFACE +} |
