Say hello to the u3g driver, implementing support for 3G modems.

This was located in the ubsa driver, but should be moved into a separate
driver:

- 3G modems provide multiple serial ports to allow AT commands while the PPP
  connection is up.
- 3G modems do not provide baud rate or other serial port settings.
- Huawei cards need specific initialisation.
- ubsa is for Belkin adapters, an Linuxy choice for another device like 3G.

Speeds achieved here with a weak signal at best is ~40kb/s (UMTS). No spooky
STALLED messages as well.

Next: Move over all entries for Sierra and Novatel cards once I have found
testers, and implemented serial port enumeration for Sierra (or rather have
Andrea Guzzo do it). They list all endpoints in 1 iface instead of 4 ifaces.

Submitted by:	aguzzo@anywi.com
MFC after:	3 weeks
This commit is contained in:
Nick Hibma 2008-10-09 21:25:01 +00:00
parent bd48866fd8
commit 483b9e4739
10 changed files with 449 additions and 73 deletions

View File

@ -384,6 +384,7 @@ MAN= aac.4 \
twe.4 \
tx.4 \
txp.4 \
u3g.4 \
uark.4 \
uart.4 \
ubsa.4 \

100
share/man/man4/u3g.4 Normal file
View File

@ -0,0 +1,100 @@
.\"
.\" Copyright (c) 2008 AnyWi Technologies
.\" All rights reserved.
.\"
.\" This code is derived from uark.c
.\"
.\" 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 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.
.\"
.\" $FreeBSD$
.\"
.Dd October 7, 2008
.Dt U3G 4
.Os
.Sh NAME
.Nm u3g
.Nd USB support for 3G datacards
.Sh SYNOPSIS
To compile this driver into the kernel,
place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device u3g"
.Cd "device ucom"
.Ed
.Pp
Alternatively, to load the driver as a
module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
u3g_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
driver provides support for the multiple USB-to-serial interfaces exposed by
many 3G usb/pccard modems.
.Pp
The device is accessed through the
.Xr ucom 4
driver which makes it behave like a
.Xr tty 4 .
.Sh HARDWARE
The
.Nm
driver supports the following adapters:
.Pp
.Bl -bullet -compact
.It
Option Globetrotter 3G Fusion (only 3G part, not WLAN)
.It
Option Globetrotter 3G Fusion Quad (only 3G part, not WLAN)
.It
Option Globetrotter 3G Quad
.It
Option Globetrotter 3G
.It
Vodafone Mobile Connect Card 3G
.It
Huawei E220 (E270?)
.It
Huawei Mobile
.El
.Pp
The supported 3G cards provide the necessary modem port for ppp,
pppd, or mpd connections as well as extra ports (depending on the specific
device) to provide other functions (diagnostic port, SIM toolkit port)
.Sh SEE ALSO
.Xr tty 4 ,
.Xr ucom 4 ,
.Xr usb 4 ,
.Xr ubsa 4
.Sh HISTORY
The
.Nm
driver
appeared in
.Fx 7.0 .
The
.Xr ubsa 4
manual page was modified for
.Nm
by
.An Andrea Guzzo Aq aguzzo@anywi.com
in September 2008.
.Sh AUTHORS
The
.Nm
driver was written by
.An Andrea Guzzo Aq aguzzo@anywi.com .
Hardware for testing provided by AnyWi Technologies, Leiden, NL.

View File

@ -2416,6 +2416,8 @@ device uscanner
#
# USB serial support
device ucom
# USB support for 3G modem cards by Option, Huawei and Sierra
device u3g
# USB support for Technologies ARK3116 based serial adapters
device uark
# USB support for Belkin F5U103 and compatible serial adapters
@ -2441,7 +2443,6 @@ device aue
# ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the
# LinkSys USB200M and various other adapters.
device axe
#

View File

@ -1327,6 +1327,7 @@ dev/usb/ohci_pci.c optional ohci pci
dev/usb/sl811hs.c optional slhci
dev/usb/slhci_pccard.c optional slhci pccard
dev/usb/uark.c optional uark
dev/usb/u3g.c optional u3g
dev/usb/ubsa.c optional ubsa
dev/usb/ubser.c optional ubser
dev/usb/ucom.c optional ucom

330
sys/dev/usb/u3g.c Normal file
View File

@ -0,0 +1,330 @@
/*
* Copyright (c) 2008 AnyWi Technologies
* Author: Andrea Guzzo <aguzzo@anywi.com>
* * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
* * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
*
* 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 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/ioccom.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/file.h>
#include <sys/selinfo.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/ucomvar.h>
#include "usbdevs.h"
#ifdef U3G_DEBUG
#define DPRINTFN(n, x) do { if (u3gdebug > (n)) printf x; } while (0)
int u3gtebug = 0;
#else
#define DPRINTFN(n, x)
#endif
#define DPRINTF(x) DPRINTFN(0, x)
#define U3GBUFSZ 1024
#define U3G_MAXPORTS 4
struct u3g_softc {
struct ucom_softc sc_ucom[U3G_MAXPORTS];;
device_t sc_dev;
usbd_device_handle sc_udev;
u_char sc_msr;
u_char sc_lsr;
u_char numports;
usbd_interface_handle sc_intr_iface; /* interrupt interface */
#ifdef U3G_DEBUG
int sc_intr_number; /* interrupt number */
usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
u_char *sc_intr_buf; /* interrupt buffer */
#endif
int sc_isize;
};
struct ucom_callback u3g_callback = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
static const struct usb_devno u3g_devs[] = {
/* OEM: Option */
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G },
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD },
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS },
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36 },
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G },
/* OEM: Huawei */
{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE },
{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 },
{ 0, 0 }
};
#ifdef U3G_DEBUG
static void
u3g_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct u3g_softc *sc = (struct u3g_softc *)priv;
device_printf(sc->sc_dev, "INTERRUPT CALLBACK\n");
}
#endif
static int
u3g_huawei_reinit(usbd_device_handle dev)
{
/* The Huawei device presents itself as a umass device with Windows
* drivers on it. After installation of the driver, it reinits into a
* 3G serial device.
*/
usb_device_request_t req;
usb_config_descriptor_t *cdesc;
/* Get the config descriptor */
cdesc = usbd_get_config_descriptor(dev);
if (cdesc == NULL)
return (UMATCH_NONE);
/* One iface means umass mode, more than 1 (4 usually) means 3G mode */
if (cdesc->bNumInterface > 1)
return (UMATCH_VENDOR_PRODUCT);
req.bmRequestType = UT_WRITE_DEVICE;
req.bRequest = UR_SET_FEATURE;
USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
USETW(req.wIndex, UHF_PORT_SUSPEND);
USETW(req.wLength, 0);
(void) usbd_do_request(dev, &req, 0);
return UMATCH_NONE; /* mismatch; it will be gone and reappear */
}
static int
u3g_match(device_t self)
{
struct usb_attach_arg *uaa = device_get_ivars(self);
if (uaa->iface != NULL)
return (UMATCH_NONE);
if (uaa->vendor == USB_VENDOR_HUAWEI)
return u3g_huawei_reinit(uaa->device);
if (usb_lookup(u3g_devs, uaa->vendor, uaa->product))
return UMATCH_VENDOR_PRODUCT;
return UMATCH_NONE;
}
static int
u3g_attach(device_t self)
{
struct u3g_softc *sc = device_get_softc(self);
struct usb_attach_arg *uaa = device_get_ivars(self);
usbd_device_handle dev = uaa->device;
usbd_interface_handle iface;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status error;
int i, n;
usb_config_descriptor_t *cdesc;
struct ucom_softc *ucom = NULL;
char devnamefmt[32];
sc->sc_dev = self;
#ifdef DEBUG
sc->sc_intr_number = -1;
sc->sc_intr_pipe = NULL;
#endif
/* Move the device into the configured state. */
error = usbd_set_config_index(dev, 1, 1);
if (error) {
device_printf(self, "failed to set configuration: %s\n",
usbd_errstr(error));
goto bad;
}
/* get the config descriptor */
cdesc = usbd_get_config_descriptor(dev);
if (cdesc == NULL) {
device_printf(self, "failed to get configuration descriptor\n");
goto bad;
}
sc->sc_udev = dev;
sc->numports = (cdesc->bNumInterface <= U3G_MAXPORTS)?cdesc->bNumInterface:U3G_MAXPORTS;
for ( i = 0; i < sc->numports; i++ ) {
ucom = &sc->sc_ucom[i];
ucom->sc_dev = self;
ucom->sc_udev = dev;
error = usbd_device2interface_handle(dev, i, &iface);
if (error) {
device_printf(ucom->sc_dev,
"failed to get interface, err=%s\n",
usbd_errstr(error));
ucom->sc_dying = 1;
goto bad;
}
id = usbd_get_interface_descriptor(iface);
ucom->sc_iface = iface;
ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
for (n = 0; n < id->bNumEndpoints; n++) {
ed = usbd_interface2endpoint_descriptor(iface, n);
if (ed == NULL) {
device_printf(ucom->sc_dev,
"could not read endpoint descriptor\n");
goto bad;
}
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
ucom->sc_bulkin_no = ed->bEndpointAddress;
else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
ucom->sc_bulkout_no = ed->bEndpointAddress;
}
if (ucom->sc_bulkin_no == -1 || ucom->sc_bulkout_no == -1) {
device_printf(ucom->sc_dev, "missing endpoint\n");
goto bad;
}
ucom->sc_parent = sc;
ucom->sc_ibufsize = U3GBUFSZ;
ucom->sc_obufsize = U3GBUFSZ;
ucom->sc_ibufsizepad = U3GBUFSZ;
ucom->sc_opkthdrlen = 0;
ucom->sc_callback = &u3g_callback;
sprintf(devnamefmt,"U%d.%%d", device_get_unit(self));
DPRINTF(("u3g: in=0x%x out=0x%x, devname=%s\n",
ucom->sc_bulkin_no, ucom->sc_bulkout_no, devnamefmt));
#if __FreeBSD_version < 800000
ucom_attach_tty(ucom, TS_CALLOUT, devnamefmt, i);
#else
ucom_attach_tty(ucom, devnamefmt, i);
#endif
}
#ifdef U3G_DEBUG
if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
error = usbd_open_pipe_intr(sc->sc_intr_iface,
sc->sc_intr_number,
USBD_SHORT_XFER_OK,
&sc->sc_intr_pipe,
sc,
sc->sc_intr_buf,
sc->sc_isize,
u3g_intr,
100);
if (error) {
device_printf(self,
"cannot open interrupt pipe (addr %d)\n",
sc->sc_intr_number);
goto bad;
}
}
#endif
device_printf(self, "configured %d serial ports (/dev/cuaU%d.X)",
sc->numports, device_get_unit(self));
return 0;
bad:
DPRINTF(("u3g_attach: ATTACH ERROR\n"));
ucom->sc_dying = 1;
return ENXIO;
}
static int
u3g_detach(device_t self)
{
struct u3g_softc *sc = device_get_softc(self);
int rv = 0;
int i;
DPRINTF(("u3g_detach: sc=%p\n", sc));
for (i = 0; i < sc->numports; i++) {
if(sc->sc_ucom[i].sc_udev) {
sc->sc_ucom[i].sc_dying = 1;
rv = ucom_detach(&sc->sc_ucom[i]);
if(rv != 0) {
device_printf(self, "Can't deallocat port %d", i);
return rv;
}
}
}
#ifdef U3G_DEBUG
if (sc->sc_intr_pipe != NULL) {
int err = usbd_abort_pipe(sc->sc_intr_pipe);
if (err)
device_printf(self,
"abort interrupt pipe failed: %s\n",
usbd_errstr(err));
err = usbd_close_pipe(sc->sc_intr_pipe);
if (err)
device_printf(self,
"close interrupt pipe failed: %s\n",
usbd_errstr(err));
free(sc->sc_intr_buf, M_USBDEV);
sc->sc_intr_pipe = NULL;
}
#endif
return 0;
}
static device_method_t u3g_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, u3g_match),
DEVMETHOD(device_attach, u3g_attach),
DEVMETHOD(device_detach, u3g_detach),
{ 0, 0 }
};
static driver_t u3g_driver = {
"ucom",
u3g_methods,
sizeof (struct u3g_softc)
};
DRIVER_MODULE(u3g, uhub, u3g_driver, ucom_devclass, usbd_driver_load, 0);
MODULE_DEPEND(u3g, usb, 1, 1, 1);
MODULE_DEPEND(u3g, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);

View File

@ -161,8 +161,6 @@ SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RW,
struct ubsa_softc {
struct ucom_softc sc_ucom;
int sc_huawei;
int sc_iface_number; /* interface number */
usbd_interface_handle sc_intr_iface; /* interrupt interface */
@ -228,24 +226,11 @@ static const struct ubsa_product {
{ USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232 },
/* Peracom */
{ USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1 },
/* Dell version of the Novatel 740 */
{ USB_VENDOR_DELL, USB_PRODUCT_DELL_U740 },
/* Option Vodafone MC3G */
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G },
/* Option GlobeTrotter 3G */
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G },
/* Option GlobeTrotter 3G QUAD */
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD },
/* Option GlobeTrotter 3G+ */
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS },
/* Option GlobeTrotter Max 3.6 */
{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36 },
/* Huawei Mobile */
{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE },
{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E270 },
/* Merlin */
{ USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620 },
/* Qualcomm, Inc. ZTE CDMA */
{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM },
/* Novatel */
{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM },
/* Novatel Wireless Merlin ES620 */
{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620 },
@ -256,6 +241,8 @@ static const struct ubsa_product {
/* Novatel Wireless Merlin U740 */
{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740 },
{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2 },
/* Dell version of the Novatel 740 */
{ USB_VENDOR_DELL, USB_PRODUCT_DELL_U740 },
/* Novatel Wireless Merlin U950D */
{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U950D },
/* Novatel Wireless Merlin V620 */
@ -341,52 +328,6 @@ MODULE_DEPEND(ubsa, usb, 1, 1, 1);
MODULE_DEPEND(ubsa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
MODULE_VERSION(ubsa, UBSA_MODVER);
/*
* Huawei Exxx radio devices have a built in flash disk which is their
* default power up configuration. This allows the device to carry its
* own installation software.
*
* Instead of following the USB spec, and create multiple configuration
* descriptors for this, the devices expects the driver to send
* UF_DEVICE_REMOTE_WAKEUP to endpoint 2 to reset the device, so it
* reprobes, now with the radio exposed.
*/
static usbd_status
ubsa_huawei(device_t self, struct usb_attach_arg *uaa) {
usb_device_request_t req; usbd_device_handle dev;
usb_config_descriptor_t *cdesc;
if (self == NULL)
return (UMATCH_NONE);
if (uaa == NULL)
return (UMATCH_NONE);
dev = uaa->device;
if (dev == NULL)
return (UMATCH_NONE);
/* get the config descriptor */
cdesc = usbd_get_config_descriptor(dev);
if (cdesc == NULL)
return (UMATCH_NONE);
if (cdesc->bNumInterface > 1)
return (0);
/* Bend it like Beckham */
device_printf(self, "Kicking Huawei device into radio mode\n");
memset(&req, 0, sizeof req);
req.bmRequestType = UT_WRITE_DEVICE;
req.bRequest = UR_SET_FEATURE;
USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
USETW(req.wIndex, 2);
USETW(req.wLength, 0);
/* We get error return, but it works */
(void)usbd_do_request(dev, &req, 0);
return (UMATCH_NONE);
}
static int
ubsa_match(device_t self)
{
@ -399,9 +340,6 @@ ubsa_match(device_t self)
for (i = 0; ubsa_products[i].vendor != 0; i++) {
if (ubsa_products[i].vendor == uaa->vendor &&
ubsa_products[i].product == uaa->product) {
if (uaa->vendor == USB_VENDOR_HUAWEI &&
ubsa_huawei(self, uaa))
break;
return (UMATCH_VENDOR_PRODUCT);
}
}
@ -424,9 +362,6 @@ ubsa_attach(device_t self)
dev = uaa->device;
ucom = &sc->sc_ucom;
if (uaa->vendor == USB_VENDOR_HUAWEI)
sc->sc_huawei = 1;
/*
* initialize rts, dtr variables to something
* different from boolean 0, 1
@ -575,8 +510,6 @@ ubsa_request(struct ubsa_softc *sc, u_int8_t request, u_int16_t value)
usbd_status err;
/* The huawei Exxx devices support none of these tricks */
if (sc->sc_huawei)
return (0);
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = request;
USETW(req.wValue, value);

View File

@ -1434,7 +1434,7 @@ product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync
/* HUAWEI products */
product HUAWEI MOBILE 0x1001 Huawei Mobile
product HUAWEI E270 0x1003 Huawei HSPA modem
product HUAWEI E220 0x1003 Huawei HSDPA modem
/* HUAWEI 3com products */
product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g

View File

@ -304,6 +304,7 @@ device urio # Diamond Rio 500 MP3 player
device uscanner # Scanners
# USB Serial devices
device ucom # Generic com ttys
device u3g # USB-based 3G modems (Option, Huawei, Sierra)
device uark # Technologies ARK3116 based serial adapters
device ubsa # Belkin F5U103 and compatible serial adapters
device uftdi # For FTDI usb serial adapters

View File

@ -269,6 +269,7 @@ SUBDIR= ${_3dfx} \
twe \
tx \
txp \
u3g \
uark \
uart \
ubsa \

8
sys/modules/u3g/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/usb
KMOD= u3g
SRCS= u3g.c ucomvar.h opt_usb.h device_if.h bus_if.h usbdevs.h
.include <bsd.kmod.mk>