Date: Wed, 13 Jan 2010 03:16:31 +0000 (UTC) From: Andrew Thompson <thompsa@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r202181 - in head: share/man/man4 sys/conf sys/dev/usb sys/dev/usb/net sys/modules/usb sys/modules/usb/uhso usr.sbin usr.sbin/uhsoctl Message-ID: <201001130316.o0D3GVkA082296@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: thompsa Date: Wed Jan 13 03:16:31 2010 New Revision: 202181 URL: http://svn.freebsd.org/changeset/base/202181 Log: Add a driver by Fredrik Lindberg for Option HSDPA USB devices. These differ from standard 3G wireless units by supplying a raw IP/IPv6 endpoint rather than using PPP over serial. uhsoctl(1) is used to initiate and close the WAN connection. Obtained from: Fredrik Lindberg <fli@shapeshifter.se> Added: head/share/man/man4/uhso.4 (contents, props changed) head/sys/dev/usb/net/uhso.c (contents, props changed) head/sys/modules/usb/uhso/ head/sys/modules/usb/uhso/Makefile (contents, props changed) head/usr.sbin/uhsoctl/ head/usr.sbin/uhsoctl/Makefile (contents, props changed) head/usr.sbin/uhsoctl/uhsoctl.1 (contents, props changed) head/usr.sbin/uhsoctl/uhsoctl.c (contents, props changed) Modified: head/share/man/man4/Makefile head/sys/conf/NOTES head/sys/conf/files head/sys/dev/usb/usbdevs head/sys/modules/usb/Makefile head/usr.sbin/Makefile Modified: head/share/man/man4/Makefile ============================================================================== --- head/share/man/man4/Makefile Tue Jan 12 23:33:14 2010 (r202180) +++ head/share/man/man4/Makefile Wed Jan 13 03:16:31 2010 (r202181) @@ -424,6 +424,7 @@ MAN= aac.4 \ ugen.4 \ uhci.4 \ uhid.4 \ + uhso.4 \ uipaq.4 \ ukbd.4 \ ulpt.4 \ Added: head/share/man/man4/uhso.4 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/share/man/man4/uhso.4 Wed Jan 13 03:16:31 2010 (r202181) @@ -0,0 +1,115 @@ +.\" Copyright (c) 2009 Fredrik Lindberg +.\" 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 ``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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Aug 12, 2009 +.Os +.Dt UHSO 4 +.Sh NAME +.Nm hso +.Nd support for several HSxPA devices from Option N.V. +.Sh SYNOPSIS +The module can be loaded at boot time by placing the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +uhso_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for several HSxPA devices from Option N.V. that are +based on their packet interface. +Each device has a set of serial ports and a raw IP packet interface. +The serial ports of the device are accessed through the +.Xr ucom 4 +driver which makes them behave like a +.Xr tty 4 . +The packet interface is exposed as a network interface. +.Pp +To establish a connection on the packet interface the use of the proprietary +AT commands +.Dq Li AT_OWANCALL +and +.Dq Li AT_OWANDATA +are required on any of the serial ports. +.Pp +The network interface must be configured manually using the data obtain from +these calls. +.Pp +Each device usually have at least two or more serial ports, their individual purpose +can be identified through +.Xr sysctl 8 . +.Sh HARDWARE +The +.Nm +driver supports at least the following cards +.Pp +.Bl -bullet -compact +.It +Option GlobeSurfer iCON 7.2 (new firmware) +.It +Option iCON 225 +.El +.Pp +The device features a mass storage device referred to as +.Dq Zero-CD +which contains drivers for Microsoft Windows. +The driver automatically switches the device to modem mode. +.Sh EXAMPLES +Establishing a packet interface connection +.Bd -literal -offset indent +AT+CGDCONT=1,,"apn.provider" +AT_OWANCALL=1,1,1 +OK +_OWANCALL=1,1 + +AT_OWANDATA=1 +_OWANDATA: 1, 10.11.12.13, 0.0.0.0, 10.2.3.4, 10.2.3.5, \e + 0.0.0.0, 0.0.0.0, 72000 +.Ed +.Pp +Configuring the interface +.Bd -literal -offset indent +ifconfig uhso0 10.11.12.13 up +route add default -interface uhso0 +echo "nameserver 10.2.3.4" > /etc/resolv.conf +echo "nameserver 10.2.3.5" >> /etc/resolv.conf +.Ed +.Pp +The connection can be terminated with +.Bd -literal -offset indent +AT_OWANCALL=1,0,1 +.Ed +.Sh FILES +.Bl -tag -width "XXXXXX" +.It Pa /dev/cuaU?.? +.El +.Sh SEE ALSO +.Xr ucom 4 , +.Xr usb 4 +.Sh AUTHORS +The +.Nm +driver was written by +.An Fredrik Lindberg Aq fli@shapeshifter.se . Modified: head/sys/conf/NOTES ============================================================================== --- head/sys/conf/NOTES Tue Jan 12 23:33:14 2010 (r202180) +++ head/sys/conf/NOTES Wed Jan 13 03:16:31 2010 (r202181) @@ -2652,6 +2652,9 @@ device rue # # Davicom DM9601E USB to fast ethernet. Supports the Corega FEther USB-TXC. device udav +# +# HSxPA devices from Option N.V +device uhso # # Ralink Technology RT2501USB/RT2601USB wireless driver Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Tue Jan 12 23:33:14 2010 (r202180) +++ head/sys/conf/files Wed Jan 13 03:16:31 2010 (r202181) @@ -1676,7 +1676,7 @@ dev/usb/usb_request.c optional usb dev/usb/usb_transfer.c optional usb dev/usb/usb_util.c optional usb # -# USB ethernet drivers +# USB network drivers # dev/usb/net/if_aue.c optional aue dev/usb/net/if_axe.c optional axe @@ -1687,6 +1687,7 @@ dev/usb/net/if_rue.c optional rue dev/usb/net/if_udav.c optional udav dev/usb/net/usb_ethernet.c optional aue | axe | cdce | cue | kue | rue | \ udav +dev/usb/net/uhso.c optional uhso # # USB WLAN drivers # Added: head/sys/dev/usb/net/uhso.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/usb/net/uhso.c Wed Jan 13 03:16:31 2010 (r202181) @@ -0,0 +1,1754 @@ +/*- + * Copyright (c) 2009 Fredrik Lindberg + * 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 ``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 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> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/tty.h> +#include <sys/sysctl.h> +#include <sys/condvar.h> +#include <sys/sx.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/systm.h> + +#include <machine/bus.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> +#include <net/bpf.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usb_cdc.h> +#include "usbdevs.h" +#define USB_DEBUG_VAR uhso_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_device.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/serial/usb_serial.h> +#include <dev/usb/usb_msctest.h> + +struct uhso_tty { + struct uhso_softc *ht_sc; + struct usb_xfer *ht_xfer[3]; + int ht_muxport; + int ht_open; + char ht_name[32]; +}; + +struct uhso_softc { + device_t sc_dev; + struct usb_device *sc_udev; + struct mtx sc_mtx; + uint32_t sc_type; + + struct usb_xfer *sc_xfer[3]; + uint8_t sc_iface_no; + uint8_t sc_iface_index; + + /* Control pipe */ + struct usb_xfer * sc_ctrl_xfer[2]; + uint8_t sc_ctrl_iface_no; + + /* Network */ + struct usb_xfer *sc_if_xfer[2]; + struct ifnet *sc_ifp; + struct mbuf *sc_mwait; /* partial packet */ + size_t sc_waitlen; /* no. of outstanding bytes */ + struct ifqueue sc_rxq; + struct callout sc_c; + + /* TTY related structures */ + struct ucom_super_softc sc_super_ucom; + int sc_ttys; + struct uhso_tty *sc_tty; + struct ucom_softc *sc_ucom; + int sc_msr; + int sc_lsr; + int sc_line; +}; + + +#define UHSO_MAX_MTU 2048 + +/* + * There are mainly two type of cards floating around. + * The first one has 2,3 or 4 interfaces with a multiplexed serial port + * and packet interface on the first interface and bulk serial ports + * on the others. + * The second type of card has several other interfaces, their purpose + * can be detected during run-time. + */ +#define UHSO_IFACE_SPEC(usb_type, port, port_type) \ + (((usb_type) << 24) | ((port) << 16) | (port_type)) + +#define UHSO_IFACE_USB_TYPE(x) ((x >> 24) & 0xff) +#define UHSO_IFACE_PORT(x) ((x >> 16) & 0xff) +#define UHSO_IFACE_PORT_TYPE(x) (x & 0xff) + +/* + * USB interface types + */ +#define UHSO_IF_NET 0x01 /* Network packet interface */ +#define UHSO_IF_MUX 0x02 /* Multiplexed serial port */ +#define UHSO_IF_BULK 0x04 /* Bulk interface */ + +/* + * Port types + */ +#define UHSO_PORT_UNKNOWN 0x00 +#define UHSO_PORT_SERIAL 0x01 /* Serial port */ +#define UHSO_PORT_NETWORK 0x02 /* Network packet interface */ + +/* + * Multiplexed serial port destination sub-port names + */ +#define UHSO_MPORT_TYPE_CTL 0x00 /* Control port */ +#define UHSO_MPORT_TYPE_APP 0x01 /* Application */ +#define UHSO_MPORT_TYPE_PCSC 0x02 +#define UHSO_MPORT_TYPE_GPS 0x03 +#define UHSO_MPORT_TYPE_APP2 0x04 +#define UHSO_MPORT_TYPE_MAX UHSO_MPORT_TYPE_APP2 +#define UHSO_MPORT_TYPE_NOMAX 8 /* Max number of mux ports */ + +/* + * Port definitions + */ +#define UHSO_PORT_TYPE_CTL 0x01 +#define UHSO_PORT_TYPE_APP 0x02 +#define UHSO_PORT_TYPE_APP2 0x03 +#define UHSO_PORT_TYPE_MODEM 0x04 +#define UHSO_PORT_TYPE_NETWORK 0x05 +#define UHSO_PORT_TYPE_DIAG 0x06 +#define UHSO_PORT_TYPE_DIAG2 0x07 +#define UHSO_PORT_TYPE_GPS 0x08 +#define UHSO_PORT_TYPE_GPSCTL 0x09 +#define UHSO_PORT_TYPE_PCSC 0x0a +#define UHSO_PORT_TYPE_MSD 0x0b +#define UHSO_PORT_TYPE_VOICE 0x0c +#define UHSO_PORT_TYPE_MAX 0x0c + +static eventhandler_tag uhso_etag; + +/* Overall port type */ +static char *uhso_port[] = { + "Unknown", + "Serial", + "Network", + "Network/Serial" +}; + +/* Map between interface port type read from device and description type */ +static char uhso_port_map[] = { + 0, + UHSO_PORT_TYPE_DIAG, + UHSO_PORT_TYPE_GPS, + UHSO_PORT_TYPE_GPSCTL, + UHSO_PORT_TYPE_APP, + UHSO_PORT_TYPE_APP2, + UHSO_PORT_TYPE_CTL, + UHSO_PORT_TYPE_NETWORK, + UHSO_PORT_TYPE_MODEM, + UHSO_PORT_TYPE_MSD, + UHSO_PORT_TYPE_PCSC, + UHSO_PORT_TYPE_VOICE +}; +static char uhso_port_map_max = sizeof(uhso_port_map) / sizeof(char); + +static char uhso_mux_port_map[] = { + UHSO_PORT_TYPE_CTL, + UHSO_PORT_TYPE_APP, + UHSO_PORT_TYPE_PCSC, + UHSO_PORT_TYPE_GPS, + UHSO_PORT_TYPE_APP2 +}; + +static char *uhso_port_type[] = { + "Unknown", + "Control", + "Application", + "Application (Secondary)", + "Modem", + "Network", + "Diagnostic", + "Diagnostic (Secondary)", + "GPS", + "GPS Control", + "PC Smartcard", + "MSD", + "Voice", +}; + +static char *uhso_port_type_sysctl[] = { + "unknown", + "control", + "application", + "application", + "modem", + "network", + "diagnostic", + "diagnostic", + "gps", + "gps_control", + "pcsc", + "msd", + "voice", +}; + + +#define UHSO_STATIC_IFACE 0x01 +#define UHSO_AUTO_IFACE 0x02 + +static const struct usb_device_id uhso_devs[] = { +#define UHSO_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + /* Option GlobeSurfer iCON 7.2 */ + UHSO_DEV(OPTION, GSICON72, UHSO_STATIC_IFACE), + /* Option iCON 225 */ + UHSO_DEV(OPTION, GTHSDPA, UHSO_STATIC_IFACE), + /* Option GlobeSurfer iCON HSUPA */ + UHSO_DEV(OPTION, GSICONHSUPA, UHSO_STATIC_IFACE), + /* Option GlobeTrotter HSUPA */ + UHSO_DEV(OPTION, GTHSUPA, UHSO_STATIC_IFACE), + /* GE40x */ + UHSO_DEV(OPTION, GE40X, UHSO_AUTO_IFACE), + UHSO_DEV(OPTION, GE40X_1, UHSO_AUTO_IFACE), + UHSO_DEV(OPTION, GE40X_2, UHSO_AUTO_IFACE), + UHSO_DEV(OPTION, GE40X_3, UHSO_AUTO_IFACE), + /* Option GlobeSurfer iCON 401 */ + UHSO_DEV(OPTION, ICON401, UHSO_AUTO_IFACE), + /* Option GlobeTrotter Module 382 */ + UHSO_DEV(OPTION, GMT382, UHSO_AUTO_IFACE), + /* Option iCON EDGE */ + UHSO_DEV(OPTION, ICONEDGE, UHSO_STATIC_IFACE), + /* Option Module HSxPA */ + UHSO_DEV(OPTION, MODHSXPA, UHSO_STATIC_IFACE), + /* Option iCON 321 */ + UHSO_DEV(OPTION, ICON321, UHSO_STATIC_IFACE), + /* Option iCON 322 */ + UHSO_DEV(OPTION, GTICON322, UHSO_STATIC_IFACE) +#undef UHSO_DEV +}; + +SYSCTL_NODE(_hw_usb, OID_AUTO, uhso, CTLFLAG_RW, 0, "USB uhso"); + +#ifdef USB_DEBUG +#ifdef UHSO_DEBUG +static int uhso_debug = UHSO_DEBUG; +#else +static int uhso_debug = -1; +#endif + +SYSCTL_INT(_hw_usb_uhso, OID_AUTO, debug, CTLFLAG_RW, + &uhso_debug, 0, "Debug level"); + +#define UHSO_DPRINTF(n, x, ...) {\ + if (uhso_debug >= n) {\ + printf("%s: " x, __func__, ##__VA_ARGS__);\ + }\ +} +#else +#define UHSO_DPRINTF(n, x, ...) +#endif + +#ifdef UHSO_DEBUG_HEXDUMP +# define UHSO_HEXDUMP(_buf, _len) do { \ + { \ + size_t __tmp; \ + const char *__buf = (const char *)_buf; \ + for (__tmp = 0; __tmp < _len; __tmp++) \ + printf("%02hhx ", *__buf++); \ + printf("\n"); \ + } \ +} while(0) +#else +# define UHSO_HEXDUMP(_buf, _len) +#endif + +enum { + UHSO_MUX_ENDPT_INTR = 0, + UHSO_MUX_ENDPT_MAX +}; + +enum { + UHSO_CTRL_READ = 0, + UHSO_CTRL_WRITE, + UHSO_CTRL_MAX +}; + +enum { + UHSO_IFNET_READ = 0, + UHSO_IFNET_WRITE, + UHSO_IFNET_MAX +}; + +enum { + UHSO_BULK_ENDPT_READ = 0, + UHSO_BULK_ENDPT_WRITE, + UHSO_BULK_ENDPT_INTR, + UHSO_BULK_ENDPT_MAX +}; + +static usb_callback_t uhso_mux_intr_callback; +static usb_callback_t uhso_mux_read_callback; +static usb_callback_t uhso_mux_write_callback; +static usb_callback_t uhso_bs_read_callback; +static usb_callback_t uhso_bs_write_callback; +static usb_callback_t uhso_bs_intr_callback; +static usb_callback_t uhso_ifnet_read_callback; +static usb_callback_t uhso_ifnet_write_callback; + +static const struct usb_config uhso_ctrl_config[UHSO_CTRL_MAX] = { + [UHSO_CTRL_READ] = { + .type = UE_CONTROL, + .endpoint = 0x00, + .direction = UE_DIR_ANY, + .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, + .bufsize = sizeof(struct usb_device_request) + 1024, + .callback = &uhso_mux_read_callback + }, + + [UHSO_CTRL_WRITE] = { + .type = UE_CONTROL, + .endpoint = 0x00, + .direction = UE_DIR_ANY, + .flags = { .pipe_bof = 1, .force_short_xfer = 1 }, + .bufsize = sizeof(struct usb_device_request) + 1024, + .timeout = 1000, + .callback = &uhso_mux_write_callback + } +}; + +static const struct usb_config uhso_mux_config[UHSO_MUX_ENDPT_MAX] = { + [UHSO_MUX_ENDPT_INTR] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = { .short_xfer_ok = 1 }, + .bufsize = 0, + .callback = &uhso_mux_intr_callback, + } +}; + +static const struct usb_config uhso_ifnet_config[UHSO_IFNET_MAX] = { + [UHSO_IFNET_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, + .bufsize = MCLBYTES, + .callback = &uhso_ifnet_read_callback + }, + [UHSO_IFNET_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .flags = { .pipe_bof = 1, .force_short_xfer = 1 }, + .bufsize = MCLBYTES, + .timeout = 5 * USB_MS_HZ, + .callback = &uhso_ifnet_write_callback + } +}; + +static const struct usb_config uhso_bs_config[UHSO_BULK_ENDPT_MAX] = { + [UHSO_BULK_ENDPT_READ] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, + .bufsize = 4096, + .callback = &uhso_bs_read_callback + }, + + [UHSO_BULK_ENDPT_WRITE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .flags = { .pipe_bof = 1, .force_short_xfer = 1 }, + .bufsize = 8192, + .callback = &uhso_bs_write_callback + }, + + [UHSO_BULK_ENDPT_INTR] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = { .short_xfer_ok = 1 }, + .bufsize = 0, + .callback = &uhso_bs_intr_callback, + } +}; + +static int uhso_probe_iface(struct uhso_softc *, int, + int (*probe)(struct uhso_softc *, int)); +static int uhso_probe_iface_auto(struct uhso_softc *, int); +static int uhso_probe_iface_static(struct uhso_softc *, int); + +static int uhso_attach_muxserial(struct uhso_softc *, struct usb_interface *, + int type); +static int uhso_attach_bulkserial(struct uhso_softc *, struct usb_interface *, + int type); +static int uhso_attach_ifnet(struct uhso_softc *, struct usb_interface *, + int type); +static void uhso_test_autoinst(void *, struct usb_device *, + struct usb_attach_arg *); +static int uhso_driver_loaded(struct module *, int, void *); + +static void uhso_ucom_start_read(struct ucom_softc *); +static void uhso_ucom_stop_read(struct ucom_softc *); +static void uhso_ucom_start_write(struct ucom_softc *); +static void uhso_ucom_stop_write(struct ucom_softc *); +static void uhso_ucom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); +static void uhso_ucom_cfg_set_dtr(struct ucom_softc *, uint8_t); +static void uhso_ucom_cfg_set_rts(struct ucom_softc *, uint8_t); + +static void uhso_if_init(void *); +static void uhso_if_start(struct ifnet *); +static void uhso_if_stop(struct uhso_softc *); +static int uhso_if_ioctl(struct ifnet *, u_long, caddr_t); +static int uhso_if_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct route *); +static void uhso_if_rxflush(void *); + +static device_probe_t uhso_probe; +static device_attach_t uhso_attach; +static device_detach_t uhso_detach; + +static device_method_t uhso_methods[] = { + DEVMETHOD(device_probe, uhso_probe), + DEVMETHOD(device_attach, uhso_attach), + DEVMETHOD(device_detach, uhso_detach), + { 0, 0 } +}; + +static driver_t uhso_driver = { + "uhso", + uhso_methods, + sizeof(struct uhso_softc) +}; + +static devclass_t uhso_devclass; +DRIVER_MODULE(uhso, uhub, uhso_driver, uhso_devclass, uhso_driver_loaded, 0); +MODULE_DEPEND(uhso, ucom, 1, 1, 1); +MODULE_DEPEND(uhso, usb, 1, 1, 1); +MODULE_VERSION(uhso, 1); + +static struct ucom_callback uhso_ucom_callback = { + .ucom_cfg_get_status = &uhso_ucom_cfg_get_status, + .ucom_cfg_set_dtr = &uhso_ucom_cfg_set_dtr, + .ucom_cfg_set_rts = &uhso_ucom_cfg_set_rts, + .ucom_start_read = uhso_ucom_start_read, + .ucom_stop_read = uhso_ucom_stop_read, + .ucom_start_write = uhso_ucom_start_write, + .ucom_stop_write = uhso_ucom_stop_write +}; + +static int +uhso_probe(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != 0) + return (ENXIO); + if (uaa->device->ddesc.bDeviceClass != 0xff) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa)); +} + +static int +uhso_attach(device_t self) +{ + struct uhso_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + struct usb_config_descriptor *cd; + struct usb_interface_descriptor *id; + struct sysctl_ctx_list *sctx; + struct sysctl_oid *soid; + struct sysctl_oid *tree, *tty_node; + struct ucom_softc *ucom; + struct uhso_tty *ht; + int i, error, port; + void *probe_f; + usb_error_t uerr; + char *desc; + + device_set_usb_desc(self); + + UHSO_DPRINTF(0, "Device is in modem mode, devClass=%x\n", + uaa->device->ddesc.bDeviceClass); + + sc->sc_dev = self; + sc->sc_udev = uaa->device; + mtx_init(&sc->sc_mtx, "uhso", NULL, MTX_DEF); + + sc->sc_ucom = NULL; + sc->sc_ttys = 0; + + cd = usbd_get_config_descriptor(uaa->device); + id = usbd_get_interface_descriptor(uaa->iface); + sc->sc_ctrl_iface_no = id->bInterfaceNumber; + + sc->sc_iface_no = uaa->info.bIfaceNum; + sc->sc_iface_index = uaa->info.bIfaceIndex; + + /* Setup control pipe */ + uerr = usbd_transfer_setup(uaa->device, + &sc->sc_iface_index, sc->sc_ctrl_xfer, + uhso_ctrl_config, UHSO_CTRL_MAX, sc, &sc->sc_mtx); + if (uerr) { + device_printf(self, "Failed to setup control pipe: %s\n", + usbd_errstr(uerr)); + goto out; + } + + if (USB_GET_DRIVER_INFO(uaa) == UHSO_STATIC_IFACE) + probe_f = uhso_probe_iface_static; + else if (USB_GET_DRIVER_INFO(uaa) == UHSO_AUTO_IFACE) + probe_f = uhso_probe_iface_auto; + else + goto out; + + error = uhso_probe_iface(sc, uaa->info.bIfaceNum, probe_f); + if (error != 0) + goto out; + + + sctx = device_get_sysctl_ctx(sc->sc_dev); + soid = device_get_sysctl_tree(sc->sc_dev); + + SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "type", + CTLFLAG_RD, uhso_port[UHSO_IFACE_PORT(sc->sc_type)], 0, + "Port available at this interface"); + + if (sc->sc_ttys > 0) { + SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "ports", + CTLFLAG_RD, &sc->sc_ttys, 0, "Number of attached serial ports"); + + tree = SYSCTL_ADD_NODE(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, + "port", CTLFLAG_RD, NULL, "Serial ports"); + } + + for (i = 0; i < sc->sc_ttys; i++) { + ht = &sc->sc_tty[i]; + ucom = &sc->sc_ucom[i]; + + + if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) + port = uhso_mux_port_map[ht->ht_muxport]; + else + port = UHSO_IFACE_PORT_TYPE(sc->sc_type); + + desc = uhso_port_type_sysctl[port]; + + tty_node = SYSCTL_ADD_NODE(sctx, SYSCTL_CHILDREN(tree), OID_AUTO, + desc, CTLFLAG_RD, NULL, ""); + + ht->ht_name[0] = 0; + if (sc->sc_ttys == 1) + snprintf(ht->ht_name, 32, "cuaU%d", ucom->sc_unit); + else { + snprintf(ht->ht_name, 32, "cuaU%d.%d", + ucom->sc_unit - ucom->sc_local_unit, + ucom->sc_local_unit); + } + + desc = uhso_port_type[port]; + SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(tty_node), OID_AUTO, + "tty", CTLFLAG_RD, ht->ht_name, 0, ""); + SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(tty_node), OID_AUTO, + "desc", CTLFLAG_RD, desc, 0, ""); + + if (bootverbose) + device_printf(sc->sc_dev, + "\"%s\" port at %s\n", desc, ht->ht_name); + } + + return (0); +out: + uhso_detach(sc->sc_dev); + return (ENXIO); + +} + +static int +uhso_detach(device_t self) +{ + struct uhso_softc *sc = device_get_softc(self); + int i; + + usbd_transfer_unsetup(sc->sc_xfer, 3); + usbd_transfer_unsetup(sc->sc_ctrl_xfer, UHSO_CTRL_MAX); + if (sc->sc_ttys > 0) { + ucom_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_ttys); + + for (i = 0; i < sc->sc_ttys; i++) { + if (sc->sc_tty[i].ht_muxport != -1) { + usbd_transfer_unsetup(sc->sc_tty[i].ht_xfer, + UHSO_CTRL_MAX); + } + } + + free(sc->sc_tty, M_USBDEV); + free(sc->sc_ucom, M_USBDEV); + } + + if (sc->sc_ifp != NULL) { + + callout_drain(&sc->sc_c); + + mtx_lock(&sc->sc_mtx); + uhso_if_stop(sc); + bpfdetach(sc->sc_ifp); + if_detach(sc->sc_ifp); + if_free(sc->sc_ifp); + mtx_unlock(&sc->sc_mtx); + + usbd_transfer_unsetup(sc->sc_if_xfer, UHSO_IFNET_MAX); + } + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +uhso_test_autoinst(void *arg, struct usb_device *udev, + struct usb_attach_arg *uaa) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *id; + + if (uaa->dev_state != UAA_DEV_READY) + return; + + iface = usbd_get_iface(udev, 0); + if (iface == NULL) + return; + id = iface->idesc; + if (id == NULL || id->bInterfaceClass != UICLASS_MASS) + return; + if (usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa)) + return; /* no device match */ + + if (usb_msc_eject(udev, 0, MSC_EJECT_REZERO) == 0) { + /* success, mark the udev as disappearing */ + uaa->dev_state = UAA_DEV_EJECTING; + } +} + +static int +uhso_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + /* register our autoinstall handler */ + uhso_etag = EVENTHANDLER_REGISTER(usb_dev_configured, + uhso_test_autoinst, NULL, EVENTHANDLER_PRI_ANY); + break; + case MOD_UNLOAD: + EVENTHANDLER_DEREGISTER(usb_dev_configured, uhso_etag); + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +static int uhso_probe_iface_auto(struct uhso_softc *sc, int index) +{ + struct usb_device_request req; + usb_error_t uerr; + uint16_t actlen = 0; + char port; + char buf[17] = {0}; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = 0x86; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 17); + + uerr = usbd_do_request_flags(sc->sc_udev, NULL, &req, buf, + 0, &actlen, USB_MS_HZ); + if (uerr != 0) { + device_printf(sc->sc_dev, "usbd_do_request_flags failed: %s\n", + usbd_errstr(uerr)); + return (0); + } + + UHSO_DPRINTF(3, "actlen=%d\n", actlen); + UHSO_HEXDUMP(buf, 17); + + if (index < 0 || index > 16) { + UHSO_DPRINTF(0, "Index %d out of range\n", index); + return (0); + } + + UHSO_DPRINTF(3, "index=%d, type=%x\n", index, buf[index]); + + if (buf[index] >= uhso_port_map_max) + port = 0; + else + port = uhso_port_map[(int)buf[index]]; + + if (port == UHSO_PORT_TYPE_NETWORK) + return (UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_NETWORK, port)); + else if (port == UHSO_PORT_TYPE_VOICE) + return (0); + else + return (UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_SERIAL, port)); + + return (0); +} + +static int +uhso_probe_iface_static(struct uhso_softc *sc, int index) +{ + struct usb_config_descriptor *cd; + + cd = usbd_get_config_descriptor(sc->sc_udev); + if (cd->bNumInterface <= 3) { + switch (index) { + case 0: + return UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX, + UHSO_PORT_SERIAL | UHSO_PORT_NETWORK, 0); + case 1: + return UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG); + case 2: + return UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_SERIAL, UHSO_PORT_TYPE_MODEM); + } + } + else { + switch (index) { + case 0: + return UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX, + UHSO_PORT_SERIAL | UHSO_PORT_NETWORK, 0); + case 1: + return UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG2); + case 2: + return UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_SERIAL, UHSO_PORT_TYPE_MODEM); + case 3: + return UHSO_IFACE_SPEC(UHSO_IF_BULK, + UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG); + } + } + return (0); +} + +static int +uhso_probe_iface(struct uhso_softc *sc, int index, + int (*probe)(struct uhso_softc *, int)) +{ + struct usb_interface *iface; + int type, error, error0; + + UHSO_DPRINTF(1, "Probing for interface %d, cb=%p\n", index, probe); + + type = probe(sc, index); + UHSO_DPRINTF(1, "Probe result %x\n", type); + if (type <= 0) + return (ENXIO); + + sc->sc_type = type; + iface = usbd_get_iface(sc->sc_udev, index); + + if (UHSO_IFACE_USB_TYPE(type) & (UHSO_IF_MUX | UHSO_IF_NET)) { + error0 = uhso_attach_muxserial(sc, iface, type); + error = uhso_attach_ifnet(sc, iface, type); + + if (error0 && error) + return (ENXIO); + + if (sc->sc_ttys > 0) { + error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, + sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx); + if (error) { + device_printf(sc->sc_dev, "ucom_attach failed\n"); + return (ENXIO); + } + } + + mtx_lock(&sc->sc_mtx); + usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]); + mtx_unlock(&sc->sc_mtx); + } + else if ((UHSO_IFACE_USB_TYPE(type) & UHSO_IF_BULK) && + UHSO_IFACE_PORT(type) & UHSO_PORT_SERIAL) { + + error = uhso_attach_bulkserial(sc, iface, type); + if (error) + return (ENXIO); + + error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, + sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx); + if (error) { + device_printf(sc->sc_dev, "ucom_attach failed\n"); + return (ENXIO); + } + } + else { + return (ENXIO); + } + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201001130316.o0D3GVkA082296>