forked from Minki/linux
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (47 commits) USB: Add device id for Sierra Wireless MC8755 USB: cleanup sierra wireless driver a bit USB: Sierra Wireless driver update USB: ftdi_sio whitespace fixes USB-SERIAL:cp2101 Add new device ID USB/gadget/net2280: handle sysfs errors usbtouchscreen: fix data reading for ITM touchscreens UEAGLE: fix ueagle-atm Oops USB: xpad: dance pad support USB: input: extract() and implement() are bit field manipulation routines USB: Memory leak in drivers/usb/serial/airprime.c USB Storage: unusual_devs.h entry for Sony Ericsson P990i USB: fix usbatm tiny race USB: unusual_devs entry for Nokia 6234 USB: mos7840.c: fix a check-after-dereference USB: ftdi-elan.c: remove dead code USB: Mitsumi USB FDD 061M: UNUSUAL_DEV multilun fix USB: fix dereference in drivers/usb/misc/adutux.c USB: add USB serial mos7720 driver USB: move trancevibrator.c to the proper usb directory ...
This commit is contained in:
commit
699ddda6ec
@ -3,20 +3,37 @@ xpad - Linux USB driver for X-Box gamepads
|
||||
This is the very first release of a driver for X-Box gamepads.
|
||||
Basically, this was hacked away in just a few hours, so don't expect
|
||||
miracles.
|
||||
|
||||
In particular, there is currently NO support for the rumble pack.
|
||||
You won't find many ff-aware linux applications anyway.
|
||||
|
||||
|
||||
0. Status
|
||||
---------
|
||||
0. Notes
|
||||
--------
|
||||
|
||||
For now, this driver has only been tested on just one Linux-Box.
|
||||
This one is running a 2.4.18 kernel with usb-uhci on an amd athlon 600.
|
||||
Driver updated for kernel 2.6.17.11. (Based on a patch for 2.6.11.4.)
|
||||
|
||||
The jstest-program from joystick-1.2.15 (jstest-version 2.1.0) reports
|
||||
8 axes and 10 buttons.
|
||||
The number of buttons/axes reported varies based on 3 things:
|
||||
- if you are using a known controller
|
||||
- if you are using a known dance pad
|
||||
- if using an unknown device (one not listed below), what you set in the
|
||||
module configuration for "Map D-PAD to buttons rather than axes for unknown
|
||||
pads" (module option dpad_to_buttons)
|
||||
|
||||
Alls 8 axes work, though they all have the same range (-32768..32767)
|
||||
If you set dpad_to_buttons to 0 and you are using an unknown device (one
|
||||
not listed below), the driver will map the directional pad to axes (X/Y),
|
||||
if you said N it will map the d-pad to buttons, which is needed for dance
|
||||
style games to function correctly. The default is Y.
|
||||
|
||||
dpad_to_buttons has no effect for known pads.
|
||||
|
||||
0.1 Normal Controllers
|
||||
----------------------
|
||||
With a normal controller, the directional pad is mapped to its own X/Y axes.
|
||||
The jstest-program from joystick-1.2.15 (jstest-version 2.1.0) will report 8
|
||||
axes and 10 buttons.
|
||||
|
||||
All 8 axes work, though they all have the same range (-32768..32767)
|
||||
and the zero-setting is not correct for the triggers (I don't know if that
|
||||
is some limitation of jstest, since the input device setup should be fine. I
|
||||
didn't have a look at jstest itself yet).
|
||||
@ -30,16 +47,50 @@ in game functionality were OK. However, I find it rather difficult to
|
||||
play first person shooters with a pad. Your mileage may vary.
|
||||
|
||||
|
||||
0.2 Xbox Dance Pads
|
||||
-------------------
|
||||
When using a known dance pad, jstest will report 6 axes and 14 buttons.
|
||||
|
||||
For dance style pads (like the redoctane pad) several changes
|
||||
have been made. The old driver would map the d-pad to axes, resulting
|
||||
in the driver being unable to report when the user was pressing both
|
||||
left+right or up+down, making DDR style games unplayable.
|
||||
|
||||
Known dance pads automatically map the d-pad to buttons and will work
|
||||
correctly out of the box.
|
||||
|
||||
If your dance pad is recognized by the driver but is using axes instead
|
||||
of buttons, see section 0.3 - Unknown Controllers
|
||||
|
||||
I've tested this with Stepmania, and it works quite well.
|
||||
|
||||
|
||||
0.3 Unkown Controllers
|
||||
----------------------
|
||||
If you have an unkown xbox controller, it should work just fine with
|
||||
the default settings.
|
||||
|
||||
HOWEVER if you have an unknown dance pad not listed below, it will not
|
||||
work UNLESS you set "dpad_to_buttons" to 1 in the module configuration.
|
||||
|
||||
PLEASE if you have an unkown controller, email Dom <binary1230@yahoo.com> with
|
||||
a dump from /proc/bus/usb and a description of the pad (manufacturer, country,
|
||||
whether it is a dance pad or normal controller) so that we can add your pad
|
||||
to the list of supported devices, ensuring that it will work out of the
|
||||
box in the future.
|
||||
|
||||
|
||||
1. USB adapter
|
||||
--------------
|
||||
|
||||
Before you can actually use the driver, you need to get yourself an
|
||||
adapter cable to connect the X-Box controller to your Linux-Box.
|
||||
adapter cable to connect the X-Box controller to your Linux-Box. You
|
||||
can buy these online fairly cheap, or build your own.
|
||||
|
||||
Such a cable is pretty easy to build. The Controller itself is a USB compound
|
||||
device (a hub with three ports for two expansion slots and the controller
|
||||
device) with the only difference in a nonstandard connector (5 pins vs. 4 on
|
||||
standard USB connector).
|
||||
Such a cable is pretty easy to build. The Controller itself is a USB
|
||||
compound device (a hub with three ports for two expansion slots and
|
||||
the controller device) with the only difference in a nonstandard connector
|
||||
(5 pins vs. 4 on standard USB connector).
|
||||
|
||||
You just need to solder a USB connector onto the cable and keep the
|
||||
yellow wire unconnected. The other pins have the same order on both
|
||||
@ -51,36 +102,36 @@ original one. You can buy an extension cable and cut that instead. That way,
|
||||
you can still use the controller with your X-Box, if you have one ;)
|
||||
|
||||
|
||||
2. driver installation
|
||||
2. Driver Installation
|
||||
----------------------
|
||||
|
||||
Once you have the adapter cable and the controller is connected, you need
|
||||
to load your USB subsystem and should cat /proc/bus/usb/devices.
|
||||
There should be an entry like the one at the end [4].
|
||||
|
||||
Currently (as of version 0.0.4), the following three devices are included:
|
||||
Currently (as of version 0.0.6), the following devices are included:
|
||||
original Microsoft XBOX controller (US), vendor=0x045e, product=0x0202
|
||||
smaller Microsoft XBOX controller (US), vendor=0x045e, product=0x0289
|
||||
original Microsoft XBOX controller (Japan), vendor=0x045e, product=0x0285
|
||||
InterAct PowerPad Pro (Germany), vendor=0x05fd, product=0x107a
|
||||
RedOctane Xbox Dance Pad (US), vendor=0x0c12, product=0x8809
|
||||
|
||||
If you have another controller that is not listed above and is not recognized
|
||||
by the driver, please drop me a line with the appropriate info (that is, include
|
||||
the name, vendor and product ID, as well as the country where you bought it;
|
||||
sending the whole dump out of /proc/bus/usb/devices along would be even better).
|
||||
The driver should work with xbox pads not listed above as well, however
|
||||
you will need to do something extra for dance pads to work.
|
||||
|
||||
In theory, the driver should work with other controllers than mine
|
||||
(InterAct PowerPad pro, bought in Germany) just fine, but I cannot test this
|
||||
for I only have this one controller.
|
||||
If you have a controller not listed above, see 0.3 - Unknown Controllers
|
||||
|
||||
If you compiled and installed the driver, test the functionality:
|
||||
> modprobe xpad
|
||||
> modprobe joydev
|
||||
> jstest /dev/js0
|
||||
|
||||
There should be a single line showing 18 inputs (8 axes, 10 buttons), and
|
||||
it's values should change if you move the sticks and push the buttons.
|
||||
If you're using a normal controller, there should be a single line showing
|
||||
18 inputs (8 axes, 10 buttons), and its values should change if you move
|
||||
the sticks and push the buttons. If you're using a dance pad, it should
|
||||
show 20 inputs (6 axes, 14 buttons).
|
||||
|
||||
It works? Voila, your done ;)
|
||||
It works? Voila, you're done ;)
|
||||
|
||||
|
||||
3. Thanks
|
||||
@ -111,6 +162,22 @@ I: If#= 0 Alt= 0 #EPs= 2 Cls=58(unk. ) Sub=42 Prot=00 Driver=(none)
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 32 Ivl= 10ms
|
||||
E: Ad=02(O) Atr=03(Int.) MxPS= 32 Ivl= 10ms
|
||||
|
||||
5. /proc/bus/usb/devices - dump from Redoctane Xbox Dance Pad (US):
|
||||
|
||||
T: Bus=01 Lev=02 Prnt=09 Port=00 Cnt=01 Dev#= 10 Spd=12 MxCh= 0
|
||||
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||
P: Vendor=0c12 ProdID=8809 Rev= 0.01
|
||||
S: Product=XBOX DDR
|
||||
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
|
||||
I: If#= 0 Alt= 0 #EPs= 2 Cls=58(unk. ) Sub=42 Prot=00 Driver=xpad
|
||||
E: Ad=82(I) Atr=03(Int.) MxPS= 32 Ivl=4ms
|
||||
E: Ad=02(O) Atr=03(Int.) MxPS= 32 Ivl=4ms
|
||||
|
||||
--
|
||||
Marko Friedemann <mfr@bmx-chemnitz.de>
|
||||
2002-07-16
|
||||
- original doc
|
||||
|
||||
Dominic Cerquetti <binary1230@yahoo.com>
|
||||
2005-03-19
|
||||
- added stuff for dance pads, new d-pad->axes mappings
|
||||
|
@ -33,7 +33,6 @@ obj-$(CONFIG_USB_KBTAB) += input/
|
||||
obj-$(CONFIG_USB_MOUSE) += input/
|
||||
obj-$(CONFIG_USB_MTOUCH) += input/
|
||||
obj-$(CONFIG_USB_POWERMATE) += input/
|
||||
obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
|
||||
obj-$(CONFIG_USB_WACOM) += input/
|
||||
obj-$(CONFIG_USB_XPAD) += input/
|
||||
|
||||
@ -66,6 +65,7 @@ obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
|
||||
obj-$(CONFIG_USB_RIO500) += misc/
|
||||
obj-$(CONFIG_USB_SISUSBVGA) += misc/
|
||||
obj-$(CONFIG_USB_TEST) += misc/
|
||||
obj-$(CONFIG_USB_TRANCEVIBRATOR)+= misc/
|
||||
obj-$(CONFIG_USB_USS720) += misc/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
|
@ -793,6 +793,9 @@ static const struct usb_device_id cxacru_usb_ids[] = {
|
||||
{ /* V = Conexant P = ADSL modem */
|
||||
USB_DEVICE(0x0572, 0xcb06), .driver_info = (unsigned long) &cxacru_cb00
|
||||
},
|
||||
{ /* V = Conexant P = ADSL modem (ZTE ZXDSL 852) */
|
||||
USB_DEVICE(0x0572, 0xcb07), .driver_info = (unsigned long) &cxacru_cb00
|
||||
},
|
||||
{ /* V = Olitec P = ADSL modem version 2 */
|
||||
USB_DEVICE(0x08e3, 0x0100), .driver_info = (unsigned long) &cxacru_cafe
|
||||
},
|
||||
|
@ -55,7 +55,6 @@ static const char speedtch_driver_name[] = "speedtch";
|
||||
#define OFFSET_d 9 /* size 4 */
|
||||
#define OFFSET_e 13 /* size 1 */
|
||||
#define OFFSET_f 14 /* size 1 */
|
||||
#define TOTAL 15
|
||||
|
||||
#define SIZE_7 1
|
||||
#define SIZE_b 8
|
||||
@ -79,6 +78,18 @@ static int dl_512_first = DEFAULT_DL_512_FIRST;
|
||||
static int enable_isoc = DEFAULT_ENABLE_ISOC;
|
||||
static int sw_buffering = DEFAULT_SW_BUFFERING;
|
||||
|
||||
#define DEFAULT_B_MAX_DSL 8128
|
||||
#define DEFAULT_MODEM_MODE 11
|
||||
#define MODEM_OPTION_LENGTH 16
|
||||
static const unsigned char DEFAULT_MODEM_OPTION[MODEM_OPTION_LENGTH] = {
|
||||
0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static unsigned int BMaxDSL = DEFAULT_B_MAX_DSL;
|
||||
static unsigned char ModemMode = DEFAULT_MODEM_MODE;
|
||||
static unsigned char ModemOption[MODEM_OPTION_LENGTH];
|
||||
static int num_ModemOption;
|
||||
|
||||
module_param(altsetting, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(altsetting,
|
||||
"Alternative setting for data interface (bulk_default: "
|
||||
@ -100,6 +111,17 @@ MODULE_PARM_DESC(sw_buffering,
|
||||
"Enable software buffering (default: "
|
||||
__MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
|
||||
|
||||
module_param(BMaxDSL, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(BMaxDSL,
|
||||
"default: " __MODULE_STRING(DEFAULT_B_MAX_DSL));
|
||||
|
||||
module_param(ModemMode, byte, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(ModemMode,
|
||||
"default: " __MODULE_STRING(DEFAULT_MODEM_MODE));
|
||||
|
||||
module_param_array(ModemOption, byte, &num_ModemOption, S_IRUGO);
|
||||
MODULE_PARM_DESC(ModemOption, "default: 0x10,0x00,0x00,0x00,0x20");
|
||||
|
||||
#define INTERFACE_DATA 1
|
||||
#define ENDPOINT_INT 0x81
|
||||
#define ENDPOINT_BULK_DATA 0x07
|
||||
@ -108,10 +130,17 @@ MODULE_PARM_DESC(sw_buffering,
|
||||
|
||||
#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
|
||||
|
||||
struct speedtch_params {
|
||||
unsigned int altsetting;
|
||||
unsigned int BMaxDSL;
|
||||
unsigned char ModemMode;
|
||||
unsigned char ModemOption[MODEM_OPTION_LENGTH];
|
||||
};
|
||||
|
||||
struct speedtch_instance_data {
|
||||
struct usbatm_data *usbatm;
|
||||
|
||||
unsigned int altsetting;
|
||||
struct speedtch_params params; /* set in probe, constant afterwards */
|
||||
|
||||
struct work_struct status_checker;
|
||||
|
||||
@ -123,7 +152,7 @@ struct speedtch_instance_data {
|
||||
struct urb *int_urb;
|
||||
unsigned char int_data[16];
|
||||
|
||||
unsigned char scratch_buffer[TOTAL];
|
||||
unsigned char scratch_buffer[16];
|
||||
};
|
||||
|
||||
/***************
|
||||
@ -186,6 +215,34 @@ static void speedtch_test_sequence(struct speedtch_instance_data *instance)
|
||||
0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret);
|
||||
|
||||
/* Extra initialisation in recent drivers - gives higher speeds */
|
||||
|
||||
/* URBext1 */
|
||||
buf[0] = instance->params.ModemMode;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x11, 0x00, buf, 1, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URBext1: %d\n", __func__, ret);
|
||||
|
||||
/* URBext2 */
|
||||
/* This seems to be the one which actually triggers the higher sync
|
||||
rate -- it does require the new firmware too, although it works OK
|
||||
with older firmware */
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x14, 0x00,
|
||||
instance->params.ModemOption,
|
||||
MODEM_OPTION_LENGTH, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URBext2: %d\n", __func__, ret);
|
||||
|
||||
/* URBext3 */
|
||||
buf[0] = instance->params.BMaxDSL & 0xff;
|
||||
buf[1] = instance->params.BMaxDSL >> 8;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x12, 0x00, buf, 2, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URBext3: %d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
|
||||
@ -285,8 +342,8 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
|
||||
because we're in our own kernel thread anyway. */
|
||||
msleep_interruptible(1000);
|
||||
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->altsetting)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->altsetting, ret);
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->params.altsetting, ret);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
@ -372,7 +429,7 @@ static int speedtch_read_status(struct speedtch_instance_data *instance)
|
||||
unsigned char *buf = instance->scratch_buffer;
|
||||
int ret;
|
||||
|
||||
memset(buf, 0, TOTAL);
|
||||
memset(buf, 0, 16);
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
|
||||
@ -746,17 +803,21 @@ static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
|
||||
instance->usbatm = usbatm;
|
||||
|
||||
/* altsetting and enable_isoc may change at any moment, so take a snapshot */
|
||||
instance->altsetting = altsetting;
|
||||
/* module parameters may change at any moment, so take a snapshot */
|
||||
instance->params.altsetting = altsetting;
|
||||
instance->params.BMaxDSL = BMaxDSL;
|
||||
instance->params.ModemMode = ModemMode;
|
||||
memcpy(instance->params.ModemOption, DEFAULT_MODEM_OPTION, MODEM_OPTION_LENGTH);
|
||||
memcpy(instance->params.ModemOption, ModemOption, num_ModemOption);
|
||||
use_isoc = enable_isoc;
|
||||
|
||||
if (instance->altsetting)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->altsetting)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->altsetting, ret);
|
||||
instance->altsetting = 0; /* fall back to default */
|
||||
if (instance->params.altsetting)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->params.altsetting, ret);
|
||||
instance->params.altsetting = 0; /* fall back to default */
|
||||
}
|
||||
|
||||
if (!instance->altsetting && use_isoc)
|
||||
if (!instance->params.altsetting && use_isoc)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_ISOC_ALTSETTING)) < 0) {
|
||||
usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret);
|
||||
use_isoc = 0; /* fall back to bulk */
|
||||
@ -783,14 +844,14 @@ static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
usb_info(usbatm, "isochronous transfer not supported - using bulk\n");
|
||||
}
|
||||
|
||||
if (!use_isoc && !instance->altsetting)
|
||||
if (!use_isoc && !instance->params.altsetting)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_BULK_ALTSETTING)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret);
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
if (!instance->altsetting)
|
||||
instance->altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING;
|
||||
if (!instance->params.altsetting)
|
||||
instance->params.altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING;
|
||||
|
||||
usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0);
|
||||
|
||||
|
@ -68,7 +68,7 @@
|
||||
|
||||
#include "usbatm.h"
|
||||
|
||||
#define EAGLEUSBVERSION "ueagle 1.3"
|
||||
#define EAGLEUSBVERSION "ueagle 1.4"
|
||||
|
||||
|
||||
/*
|
||||
@ -80,14 +80,14 @@
|
||||
dev_dbg(&(usb_dev)->dev, \
|
||||
"[ueagle-atm dbg] %s: " format, \
|
||||
__FUNCTION__, ##args); \
|
||||
} while (0)
|
||||
} while (0)
|
||||
|
||||
#define uea_vdbg(usb_dev, format, args...) \
|
||||
do { \
|
||||
if (debug >= 2) \
|
||||
dev_dbg(&(usb_dev)->dev, \
|
||||
"[ueagle-atm vdbg] " format, ##args); \
|
||||
} while (0)
|
||||
} while (0)
|
||||
|
||||
#define uea_enters(usb_dev) \
|
||||
uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
|
||||
@ -218,8 +218,8 @@ enum {
|
||||
#define UEA_CHIP_VERSION(x) \
|
||||
((x)->driver_info & 0xf)
|
||||
|
||||
#define IS_ISDN(sc) \
|
||||
(le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)
|
||||
#define IS_ISDN(usb_dev) \
|
||||
(le16_to_cpu((usb_dev)->descriptor.bcdDevice) & 0x80)
|
||||
|
||||
#define INS_TO_USBDEV(ins) ins->usb_dev
|
||||
|
||||
@ -625,12 +625,12 @@ static int request_dsp(struct uea_softc *sc)
|
||||
char *dsp_name;
|
||||
|
||||
if (UEA_CHIP_VERSION(sc) == ADI930) {
|
||||
if (IS_ISDN(sc))
|
||||
if (IS_ISDN(sc->usb_dev))
|
||||
dsp_name = FW_DIR "DSP9i.bin";
|
||||
else
|
||||
dsp_name = FW_DIR "DSP9p.bin";
|
||||
} else {
|
||||
if (IS_ISDN(sc))
|
||||
if (IS_ISDN(sc->usb_dev))
|
||||
dsp_name = FW_DIR "DSPei.bin";
|
||||
else
|
||||
dsp_name = FW_DIR "DSPep.bin";
|
||||
@ -744,7 +744,7 @@ static inline void wake_up_cmv_ack(struct uea_softc *sc)
|
||||
|
||||
static inline int wait_cmv_ack(struct uea_softc *sc)
|
||||
{
|
||||
int ret = wait_event_timeout(sc->cmv_ack_wait,
|
||||
int ret = wait_event_interruptible_timeout(sc->cmv_ack_wait,
|
||||
sc->cmv_ack, ACK_TIMEOUT);
|
||||
sc->cmv_ack = 0;
|
||||
|
||||
@ -885,7 +885,8 @@ static int uea_stat(struct uea_softc *sc)
|
||||
break;
|
||||
|
||||
case 3: /* fail ... */
|
||||
uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n");
|
||||
uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
|
||||
" (may be try other cmv/dsp)\n");
|
||||
return -EAGAIN;
|
||||
|
||||
case 4 ... 6: /* test state */
|
||||
@ -913,12 +914,6 @@ static int uea_stat(struct uea_softc *sc)
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
|
||||
ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
|
||||
sc->stats.phy.firmid);
|
||||
}
|
||||
|
||||
/* always update it as atm layer could not be init when we switch to
|
||||
@ -1033,9 +1028,9 @@ static int request_cmvs(struct uea_softc *sc,
|
||||
|
||||
if (cmv_file[sc->modem_index] == NULL) {
|
||||
if (UEA_CHIP_VERSION(sc) == ADI930)
|
||||
file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin";
|
||||
file = (IS_ISDN(sc->usb_dev)) ? "CMV9i.bin" : "CMV9p.bin";
|
||||
else
|
||||
file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
|
||||
file = (IS_ISDN(sc->usb_dev)) ? "CMVei.bin" : "CMVep.bin";
|
||||
} else
|
||||
file = cmv_file[sc->modem_index];
|
||||
|
||||
@ -1131,6 +1126,13 @@ static int uea_start_reset(struct uea_softc *sc)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Dump firmware version */
|
||||
ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
|
||||
sc->stats.phy.firmid);
|
||||
|
||||
/* get options */
|
||||
ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
|
||||
if (ret < 0)
|
||||
@ -1147,6 +1149,8 @@ static int uea_start_reset(struct uea_softc *sc)
|
||||
/* Enter in R-ACT-REQ */
|
||||
ret = uea_write_cmv(sc, SA_CNTL, 0, 2);
|
||||
uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
|
||||
uea_info(INS_TO_USBDEV(sc), "Modem started, "
|
||||
"waiting synchronization\n");
|
||||
out:
|
||||
release_firmware(cmvs_fw);
|
||||
sc->reset = 0;
|
||||
@ -1172,7 +1176,10 @@ static int uea_kthread(void *data)
|
||||
if (!ret)
|
||||
ret = uea_stat(sc);
|
||||
if (ret != -EAGAIN)
|
||||
msleep(1000);
|
||||
msleep_interruptible(1000);
|
||||
if (try_to_freeze())
|
||||
uea_err(INS_TO_USBDEV(sc), "suspend/resume not supported, "
|
||||
"please unplug/replug your modem\n");
|
||||
}
|
||||
uea_leaves(INS_TO_USBDEV(sc));
|
||||
return ret;
|
||||
@ -1566,6 +1573,7 @@ UEA_ATTR(uscorr, 0);
|
||||
UEA_ATTR(dscorr, 0);
|
||||
UEA_ATTR(usunc, 0);
|
||||
UEA_ATTR(dsunc, 0);
|
||||
UEA_ATTR(firmid, 0);
|
||||
|
||||
/* Retrieve the device End System Identifier (MAC) */
|
||||
|
||||
@ -1597,7 +1605,7 @@ static int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf)
|
||||
{
|
||||
struct uea_softc *sc = usbatm->driver_data;
|
||||
|
||||
wait_event(sc->sync_q, IS_OPERATIONAL(sc));
|
||||
wait_event_interruptible(sc->sync_q, IS_OPERATIONAL(sc));
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1639,16 +1647,13 @@ static struct attribute *attrs[] = {
|
||||
&dev_attr_stat_dscorr.attr,
|
||||
&dev_attr_stat_usunc.attr,
|
||||
&dev_attr_stat_dsunc.attr,
|
||||
&dev_attr_stat_firmid.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group attr_grp = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
static int create_fs_entries(struct usb_interface *intf)
|
||||
{
|
||||
return sysfs_create_group(&intf->dev.kobj, &attr_grp);
|
||||
}
|
||||
|
||||
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
@ -1708,31 +1713,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
|
||||
}
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&intf->dev.kobj, &attr_grp);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = uea_boot(sc);
|
||||
if (ret < 0) {
|
||||
kfree(sc);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = create_fs_entries(intf);
|
||||
if (ret) {
|
||||
uea_stop(sc);
|
||||
kfree(sc);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_fs_entries(struct usb_interface *intf)
|
||||
{
|
||||
sysfs_remove_group(&intf->dev.kobj, &attr_grp);
|
||||
error:
|
||||
kfree(sc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
|
||||
{
|
||||
struct uea_softc *sc = usbatm->driver_data;
|
||||
|
||||
destroy_fs_entries(intf);
|
||||
sysfs_remove_group(&intf->dev.kobj, &attr_grp);
|
||||
uea_stop(sc);
|
||||
kfree(sc);
|
||||
}
|
||||
@ -1753,10 +1752,10 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
struct usb_device *usb = interface_to_usbdev(intf);
|
||||
|
||||
uea_enters(usb);
|
||||
uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s\n",
|
||||
uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s %s\n",
|
||||
le16_to_cpu(usb->descriptor.idVendor),
|
||||
le16_to_cpu(usb->descriptor.idProduct),
|
||||
chip_name[UEA_CHIP_VERSION(id)]);
|
||||
chip_name[UEA_CHIP_VERSION(id)], IS_ISDN(usb)?"isdn":"pots");
|
||||
|
||||
usb_reset_device(usb);
|
||||
|
||||
|
@ -1001,6 +1001,7 @@ static int usbatm_do_heavy_init(void *arg)
|
||||
|
||||
daemonize(instance->driver->driver_name);
|
||||
allow_signal(SIGTERM);
|
||||
instance->thread_pid = current->pid;
|
||||
|
||||
complete(&instance->thread_started);
|
||||
|
||||
@ -1025,10 +1026,6 @@ static int usbatm_heavy_init(struct usbatm_data *instance)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&instance->serialize);
|
||||
instance->thread_pid = ret;
|
||||
mutex_unlock(&instance->serialize);
|
||||
|
||||
wait_for_completion(&instance->thread_started);
|
||||
|
||||
return 0;
|
||||
|
@ -325,7 +325,7 @@ static void acm_rx_tasklet(unsigned long _acm)
|
||||
struct acm_rb *buf;
|
||||
struct tty_struct *tty = acm->tty;
|
||||
struct acm_ru *rcv;
|
||||
//unsigned long flags;
|
||||
unsigned long flags;
|
||||
int i = 0;
|
||||
dbg("Entering acm_rx_tasklet");
|
||||
|
||||
@ -333,15 +333,15 @@ static void acm_rx_tasklet(unsigned long _acm)
|
||||
return;
|
||||
|
||||
next_buffer:
|
||||
spin_lock(&acm->read_lock);
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
if (list_empty(&acm->filled_read_bufs)) {
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
goto urbs;
|
||||
}
|
||||
buf = list_entry(acm->filled_read_bufs.next,
|
||||
struct acm_rb, list);
|
||||
list_del(&buf->list);
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
|
||||
dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
|
||||
|
||||
@ -356,29 +356,29 @@ next_buffer:
|
||||
memmove(buf->base, buf->base + i, buf->size - i);
|
||||
buf->size -= i;
|
||||
spin_unlock(&acm->throttle_lock);
|
||||
spin_lock(&acm->read_lock);
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
list_add(&buf->list, &acm->filled_read_bufs);
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&acm->throttle_lock);
|
||||
|
||||
spin_lock(&acm->read_lock);
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
list_add(&buf->list, &acm->spare_read_bufs);
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
goto next_buffer;
|
||||
|
||||
urbs:
|
||||
while (!list_empty(&acm->spare_read_bufs)) {
|
||||
spin_lock(&acm->read_lock);
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
if (list_empty(&acm->spare_read_urbs)) {
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
return;
|
||||
}
|
||||
rcv = list_entry(acm->spare_read_urbs.next,
|
||||
struct acm_ru, list);
|
||||
list_del(&rcv->list);
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
|
||||
buf = list_entry(acm->spare_read_bufs.next,
|
||||
struct acm_rb, list);
|
||||
@ -400,9 +400,9 @@ urbs:
|
||||
free-urbs-pool and resubmited ASAP */
|
||||
if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
|
||||
list_add(&buf->list, &acm->spare_read_bufs);
|
||||
spin_lock(&acm->read_lock);
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
list_add(&rcv->list, &acm->spare_read_urbs);
|
||||
spin_unlock(&acm->read_lock);
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1083,6 +1083,9 @@ static struct usb_device_id acm_ids[] = {
|
||||
{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
|
||||
.driver_info = SINGLE_RX_URB, /* firmware bug */
|
||||
},
|
||||
|
@ -154,6 +154,7 @@ struct usblp {
|
||||
unsigned char used; /* True if open */
|
||||
unsigned char present; /* True if not disconnected */
|
||||
unsigned char bidir; /* interface is bidirectional */
|
||||
unsigned char sleeping; /* interface is suspended */
|
||||
unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */
|
||||
/* first 2 bytes are (big-endian) length */
|
||||
};
|
||||
@ -183,6 +184,7 @@ static void usblp_dump(struct usblp *usblp) {
|
||||
dbg("quirks=%d", usblp->quirks);
|
||||
dbg("used=%d", usblp->used);
|
||||
dbg("bidir=%d", usblp->bidir);
|
||||
dbg("sleeping=%d", usblp->sleeping);
|
||||
dbg("device_id_string=\"%s\"",
|
||||
usblp->device_id_string ?
|
||||
usblp->device_id_string + 2 :
|
||||
@ -338,6 +340,20 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
||||
return newerr;
|
||||
}
|
||||
|
||||
static int handle_bidir (struct usblp *usblp)
|
||||
{
|
||||
if (usblp->bidir && usblp->used && !usblp->sleeping) {
|
||||
usblp->readcount = 0;
|
||||
usblp->readurb->dev = usblp->dev;
|
||||
if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) {
|
||||
usblp->used = 0;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* File op functions.
|
||||
*/
|
||||
@ -390,14 +406,9 @@ static int usblp_open(struct inode *inode, struct file *file)
|
||||
usblp->writeurb->status = 0;
|
||||
usblp->readurb->status = 0;
|
||||
|
||||
if (usblp->bidir) {
|
||||
usblp->readcount = 0;
|
||||
usblp->readurb->dev = usblp->dev;
|
||||
if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) {
|
||||
retval = -EIO;
|
||||
usblp->used = 0;
|
||||
file->private_data = NULL;
|
||||
}
|
||||
if (handle_bidir(usblp) < 0) {
|
||||
file->private_data = NULL;
|
||||
retval = -EIO;
|
||||
}
|
||||
out:
|
||||
mutex_unlock (&usblp_mutex);
|
||||
@ -460,6 +471,11 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (usblp->sleeping) {
|
||||
retval = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd),
|
||||
_IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd) );
|
||||
|
||||
@ -658,6 +674,11 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (usblp->sleeping) {
|
||||
up (&usblp->sem);
|
||||
return writecount ? writecount : -ENODEV;
|
||||
}
|
||||
|
||||
if (usblp->writeurb->status != 0) {
|
||||
if (usblp->quirks & USBLP_QUIRK_BIDIR) {
|
||||
if (!usblp->wcomplete)
|
||||
@ -749,6 +770,11 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (usblp->sleeping) {
|
||||
count = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (usblp->readurb->status) {
|
||||
err("usblp%d: error %d reading from printer",
|
||||
usblp->minor, usblp->readurb->status);
|
||||
@ -1167,6 +1193,41 @@ static void usblp_disconnect(struct usb_interface *intf)
|
||||
mutex_unlock (&usblp_mutex);
|
||||
}
|
||||
|
||||
static int usblp_suspend (struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct usblp *usblp = usb_get_intfdata (intf);
|
||||
|
||||
/* this races against normal access and open */
|
||||
mutex_lock (&usblp_mutex);
|
||||
down (&usblp->sem);
|
||||
/* we take no more IO */
|
||||
usblp->sleeping = 1;
|
||||
/* we wait for anything printing */
|
||||
wait_event (usblp->wait, usblp->wcomplete || !usblp->present);
|
||||
usblp_unlink_urbs(usblp);
|
||||
up (&usblp->sem);
|
||||
mutex_unlock (&usblp_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usblp_resume (struct usb_interface *intf)
|
||||
{
|
||||
struct usblp *usblp = usb_get_intfdata (intf);
|
||||
int r;
|
||||
|
||||
mutex_lock (&usblp_mutex);
|
||||
down (&usblp->sem);
|
||||
|
||||
usblp->sleeping = 0;
|
||||
r = handle_bidir (usblp);
|
||||
|
||||
up (&usblp->sem);
|
||||
mutex_unlock (&usblp_mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct usb_device_id usblp_ids [] = {
|
||||
{ USB_DEVICE_INFO(7, 1, 1) },
|
||||
{ USB_DEVICE_INFO(7, 1, 2) },
|
||||
@ -1183,6 +1244,8 @@ static struct usb_driver usblp_driver = {
|
||||
.name = "usblp",
|
||||
.probe = usblp_probe,
|
||||
.disconnect = usblp_disconnect,
|
||||
.suspend = usblp_suspend,
|
||||
.resume = usblp_resume,
|
||||
.id_table = usblp_ids,
|
||||
};
|
||||
|
||||
|
@ -1588,15 +1588,18 @@ const struct file_operations usbfs_device_file_operations = {
|
||||
.release = usbdev_release,
|
||||
};
|
||||
|
||||
static void usbdev_add(struct usb_device *dev)
|
||||
static int usbdev_add(struct usb_device *dev)
|
||||
{
|
||||
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
|
||||
|
||||
dev->class_dev = class_device_create(usb_device_class, NULL,
|
||||
MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
|
||||
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
|
||||
if (IS_ERR(dev->class_dev))
|
||||
return PTR_ERR(dev->class_dev);
|
||||
|
||||
dev->class_dev->class_data = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usbdev_remove(struct usb_device *dev)
|
||||
@ -1609,7 +1612,8 @@ static int usbdev_notify(struct notifier_block *self, unsigned long action,
|
||||
{
|
||||
switch (action) {
|
||||
case USB_DEVICE_ADD:
|
||||
usbdev_add(dev);
|
||||
if (usbdev_add(dev))
|
||||
return NOTIFY_BAD;
|
||||
break;
|
||||
case USB_DEVICE_REMOVE:
|
||||
usbdev_remove(dev);
|
||||
|
@ -223,7 +223,7 @@ int usb_create_ep_files(struct device *parent,
|
||||
ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
|
||||
if (!ep_dev) {
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
goto error_alloc;
|
||||
}
|
||||
|
||||
/* fun calculation to determine the minor of this endpoint */
|
||||
@ -241,33 +241,31 @@ int usb_create_ep_files(struct device *parent,
|
||||
|
||||
retval = device_register(&ep_dev->dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
goto error_register;
|
||||
retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
|
||||
if (retval)
|
||||
goto error_group;
|
||||
|
||||
endpoint->ep_dev = ep_dev;
|
||||
|
||||
/* create the symlink to the old-style "ep_XX" directory */
|
||||
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
retval = sysfs_create_link(&parent->kobj,
|
||||
&endpoint->ep_dev->dev.kobj, name);
|
||||
retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name);
|
||||
if (retval)
|
||||
goto error_link;
|
||||
exit:
|
||||
endpoint->ep_dev = ep_dev;
|
||||
return retval;
|
||||
|
||||
error_link:
|
||||
sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
|
||||
|
||||
error_group:
|
||||
device_unregister(&ep_dev->dev);
|
||||
endpoint->ep_dev = NULL;
|
||||
destroy_endpoint_class();
|
||||
return retval;
|
||||
error:
|
||||
|
||||
error_register:
|
||||
kfree(ep_dev);
|
||||
error_alloc:
|
||||
destroy_endpoint_class();
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -282,8 +280,6 @@ void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
|
||||
sysfs_remove_group(&endpoint->ep_dev->dev.kobj, &ep_dev_attr_grp);
|
||||
device_unregister(&endpoint->ep_dev->dev);
|
||||
endpoint->ep_dev = NULL;
|
||||
destroy_endpoint_class();
|
||||
}
|
||||
destroy_endpoint_class();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2044,8 +2044,10 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
|
||||
return retval;
|
||||
}
|
||||
|
||||
device_create_file (&dev->pdev->dev, &dev_attr_function);
|
||||
device_create_file (&dev->pdev->dev, &dev_attr_queues);
|
||||
retval = device_create_file (&dev->pdev->dev, &dev_attr_function);
|
||||
if (retval) goto err_unbind;
|
||||
retval = device_create_file (&dev->pdev->dev, &dev_attr_queues);
|
||||
if (retval) goto err_func;
|
||||
|
||||
/* ... then enable host detection and ep0; and we're ready
|
||||
* for set_configuration as well as eventual disconnect.
|
||||
@ -2060,6 +2062,14 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
|
||||
|
||||
/* pci writes may still be posted */
|
||||
return 0;
|
||||
|
||||
err_func:
|
||||
device_remove_file (&dev->pdev->dev, &dev_attr_function);
|
||||
err_unbind:
|
||||
driver->unbind (&dev->gadget);
|
||||
dev->gadget.dev.driver = NULL;
|
||||
dev->driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL (usb_gadget_register_driver);
|
||||
|
||||
@ -2974,8 +2984,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
: "disabled");
|
||||
the_controller = dev;
|
||||
|
||||
device_register (&dev->gadget.dev);
|
||||
device_create_file (&pdev->dev, &dev_attr_registers);
|
||||
retval = device_register (&dev->gadget.dev);
|
||||
if (retval) goto done;
|
||||
retval = device_create_file (&pdev->dev, &dev_attr_registers);
|
||||
if (retval) goto done;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -754,7 +754,9 @@ show_registers (struct class_device *class_dev, char *buf)
|
||||
}
|
||||
|
||||
if (ehci->reclaim) {
|
||||
temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
|
||||
temp = scnprintf (next, size, "reclaim qh %p%s\n",
|
||||
ehci->reclaim,
|
||||
ehci->reclaim_ready ? " ready" : "");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
|
||||
#define EHCI_TUNE_MULT_TT 1
|
||||
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
|
||||
|
||||
#define EHCI_IAA_MSECS 10 /* arbitrary */
|
||||
#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
|
||||
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
|
||||
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
|
||||
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
|
||||
@ -254,7 +254,6 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void end_unlink_async (struct ehci_hcd *ehci);
|
||||
static void ehci_work(struct ehci_hcd *ehci);
|
||||
|
||||
#include "ehci-hub.c"
|
||||
@ -264,29 +263,6 @@ static void ehci_work(struct ehci_hcd *ehci);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void ehci_iaa_watchdog (unsigned long param)
|
||||
{
|
||||
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
|
||||
unsigned long flags;
|
||||
u32 status;
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
WARN_ON(!ehci->reclaim);
|
||||
|
||||
/* lost IAA irqs wedge things badly; seen first with a vt8235 */
|
||||
if (ehci->reclaim) {
|
||||
status = readl (&ehci->regs->status);
|
||||
if (status & STS_IAA) {
|
||||
ehci_vdbg (ehci, "lost IAA\n");
|
||||
COUNT (ehci->stats.lost_iaa);
|
||||
writel (STS_IAA, &ehci->regs->status);
|
||||
end_unlink_async (ehci);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
}
|
||||
|
||||
static void ehci_watchdog (unsigned long param)
|
||||
{
|
||||
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
|
||||
@ -294,7 +270,18 @@ static void ehci_watchdog (unsigned long param)
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
|
||||
/* stop async processing after it's idled a bit */
|
||||
/* lost IAA irqs wedge things badly; seen with a vt8235 */
|
||||
if (ehci->reclaim) {
|
||||
u32 status = readl (&ehci->regs->status);
|
||||
if (status & STS_IAA) {
|
||||
ehci_vdbg (ehci, "lost IAA\n");
|
||||
COUNT (ehci->stats.lost_iaa);
|
||||
writel (STS_IAA, &ehci->regs->status);
|
||||
ehci->reclaim_ready = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* stop async processing after it's idled a bit */
|
||||
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
|
||||
start_unlink_async (ehci, ehci->async);
|
||||
|
||||
@ -345,6 +332,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
||||
static void ehci_work (struct ehci_hcd *ehci)
|
||||
{
|
||||
timer_action_done (ehci, TIMER_IO_WATCHDOG);
|
||||
if (ehci->reclaim_ready)
|
||||
end_unlink_async (ehci);
|
||||
|
||||
/* another CPU may drop ehci->lock during a schedule scan while
|
||||
* it reports urb completions. this flag guards against bogus
|
||||
@ -379,7 +368,6 @@ static void ehci_stop (struct usb_hcd *hcd)
|
||||
|
||||
/* no more interrupts ... */
|
||||
del_timer_sync (&ehci->watchdog);
|
||||
del_timer_sync (&ehci->iaa_watchdog);
|
||||
|
||||
spin_lock_irq(&ehci->lock);
|
||||
if (HC_IS_RUNNING (hcd->state))
|
||||
@ -426,10 +414,6 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||
ehci->watchdog.function = ehci_watchdog;
|
||||
ehci->watchdog.data = (unsigned long) ehci;
|
||||
|
||||
init_timer(&ehci->iaa_watchdog);
|
||||
ehci->iaa_watchdog.function = ehci_iaa_watchdog;
|
||||
ehci->iaa_watchdog.data = (unsigned long) ehci;
|
||||
|
||||
/*
|
||||
* hw default: 1K periodic list heads, one per frame.
|
||||
* periodic_size can shrink by USBCMD update if hcc_params allows.
|
||||
@ -446,6 +430,7 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
|
||||
|
||||
ehci->reclaim = NULL;
|
||||
ehci->reclaim_ready = 0;
|
||||
ehci->next_uframe = -1;
|
||||
|
||||
/*
|
||||
@ -619,7 +604,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||
/* complete the unlinking of some qh [4.15.2.3] */
|
||||
if (status & STS_IAA) {
|
||||
COUNT (ehci->stats.reclaim);
|
||||
end_unlink_async (ehci);
|
||||
ehci->reclaim_ready = 1;
|
||||
bh = 1;
|
||||
}
|
||||
|
||||
@ -723,14 +708,10 @@ static int ehci_urb_enqueue (
|
||||
|
||||
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
// BUG_ON(qh->qh_state != QH_STATE_LINKED);
|
||||
|
||||
/* failfast */
|
||||
if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
|
||||
end_unlink_async (ehci);
|
||||
|
||||
/* defer till later if busy */
|
||||
else if (ehci->reclaim) {
|
||||
/* if we need to use IAA and it's busy, defer */
|
||||
if (qh->qh_state == QH_STATE_LINKED
|
||||
&& ehci->reclaim
|
||||
&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
|
||||
struct ehci_qh *last;
|
||||
|
||||
for (last = ehci->reclaim;
|
||||
@ -740,8 +721,12 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
qh->qh_state = QH_STATE_UNLINK_WAIT;
|
||||
last->reclaim = qh;
|
||||
|
||||
/* start IAA cycle */
|
||||
} else
|
||||
/* bypass IAA if the hc can't care */
|
||||
} else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
|
||||
end_unlink_async (ehci);
|
||||
|
||||
/* something else might have unlinked the qh by now */
|
||||
if (qh->qh_state == QH_STATE_LINKED)
|
||||
start_unlink_async (ehci, qh);
|
||||
}
|
||||
|
||||
@ -763,19 +748,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
||||
qh = (struct ehci_qh *) urb->hcpriv;
|
||||
if (!qh)
|
||||
break;
|
||||
switch (qh->qh_state) {
|
||||
case QH_STATE_LINKED:
|
||||
case QH_STATE_COMPLETING:
|
||||
unlink_async (ehci, qh);
|
||||
break;
|
||||
case QH_STATE_UNLINK:
|
||||
case QH_STATE_UNLINK_WAIT:
|
||||
/* already started */
|
||||
break;
|
||||
case QH_STATE_IDLE:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
unlink_async (ehci, qh);
|
||||
break;
|
||||
|
||||
case PIPE_INTERRUPT:
|
||||
@ -867,7 +840,6 @@ rescan:
|
||||
unlink_async (ehci, qh);
|
||||
/* FALL THROUGH */
|
||||
case QH_STATE_UNLINK: /* wait for hw to finish? */
|
||||
case QH_STATE_UNLINK_WAIT:
|
||||
idle_timeout:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
schedule_timeout_uninterruptible(1);
|
||||
|
@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
}
|
||||
ehci->command = readl (&ehci->regs->command);
|
||||
if (ehci->reclaim)
|
||||
end_unlink_async (ehci);
|
||||
ehci->reclaim_ready = 1;
|
||||
ehci_work(ehci);
|
||||
|
||||
/* suspend any active/unsuspended ports, maybe allow wakeup */
|
||||
|
@ -303,7 +303,7 @@ restart:
|
||||
/* emptying the schedule aborts any urbs */
|
||||
spin_lock_irq(&ehci->lock);
|
||||
if (ehci->reclaim)
|
||||
end_unlink_async (ehci);
|
||||
ehci->reclaim_ready = 1;
|
||||
ehci_work(ehci);
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
|
||||
|
@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
|
||||
struct ehci_qh *qh = ehci->reclaim;
|
||||
struct ehci_qh *next;
|
||||
|
||||
iaa_watchdog_done (ehci);
|
||||
timer_action_done (ehci, TIMER_IAA_WATCHDOG);
|
||||
|
||||
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
|
||||
qh->qh_state = QH_STATE_IDLE;
|
||||
@ -977,6 +977,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
|
||||
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
|
||||
next = qh->reclaim;
|
||||
ehci->reclaim = next;
|
||||
ehci->reclaim_ready = 0;
|
||||
qh->reclaim = NULL;
|
||||
|
||||
qh_completions (ehci, qh);
|
||||
@ -1051,10 +1052,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
return;
|
||||
}
|
||||
|
||||
ehci->reclaim_ready = 0;
|
||||
cmd |= CMD_IAAD;
|
||||
writel (cmd, &ehci->regs->command);
|
||||
(void) readl (&ehci->regs->command);
|
||||
iaa_watchdog_start (ehci);
|
||||
timer_action (ehci, TIMER_IAA_WATCHDOG);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -58,6 +58,7 @@ struct ehci_hcd { /* one per controller */
|
||||
/* async schedule support */
|
||||
struct ehci_qh *async;
|
||||
struct ehci_qh *reclaim;
|
||||
unsigned reclaim_ready : 1;
|
||||
unsigned scanning : 1;
|
||||
|
||||
/* periodic schedule support */
|
||||
@ -80,7 +81,6 @@ struct ehci_hcd { /* one per controller */
|
||||
struct dma_pool *itd_pool; /* itd per iso urb */
|
||||
struct dma_pool *sitd_pool; /* sitd per split iso urb */
|
||||
|
||||
struct timer_list iaa_watchdog;
|
||||
struct timer_list watchdog;
|
||||
unsigned long actions;
|
||||
unsigned stamp;
|
||||
@ -114,21 +114,9 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
iaa_watchdog_start (struct ehci_hcd *ehci)
|
||||
{
|
||||
WARN_ON(timer_pending(&ehci->iaa_watchdog));
|
||||
mod_timer (&ehci->iaa_watchdog,
|
||||
jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
|
||||
}
|
||||
|
||||
static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
|
||||
{
|
||||
del_timer (&ehci->iaa_watchdog);
|
||||
}
|
||||
|
||||
enum ehci_timer_action {
|
||||
TIMER_IO_WATCHDOG,
|
||||
TIMER_IAA_WATCHDOG,
|
||||
TIMER_ASYNC_SHRINK,
|
||||
TIMER_ASYNC_OFF,
|
||||
};
|
||||
@ -146,6 +134,9 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
|
||||
unsigned long t;
|
||||
|
||||
switch (action) {
|
||||
case TIMER_IAA_WATCHDOG:
|
||||
t = EHCI_IAA_JIFFIES;
|
||||
break;
|
||||
case TIMER_IO_WATCHDOG:
|
||||
t = EHCI_IO_JIFFIES;
|
||||
break;
|
||||
@ -162,7 +153,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
|
||||
// async queue SHRINK often precedes IAA. while it's ready
|
||||
// to go OFF neither can matter, and afterwards the IO
|
||||
// watchdog stops unless there's still periodic traffic.
|
||||
if (time_before_eq(t, ehci->watchdog.expires)
|
||||
if (action != TIMER_IAA_WATCHDOG
|
||||
&& t > ehci->watchdog.expires
|
||||
&& timer_pending (&ehci->watchdog))
|
||||
return;
|
||||
mod_timer (&ehci->watchdog, t);
|
||||
|
@ -262,6 +262,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
|
||||
*/
|
||||
.start = ohci_pnx4008_start,
|
||||
.stop = ohci_stop,
|
||||
.shutdown = ohci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
@ -280,7 +281,11 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
|
||||
*/
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
|
||||
.hub_irq_enable = ohci_rhsc_enable,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
@ -410,8 +415,6 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
|
||||
goto out4;
|
||||
}
|
||||
|
||||
hcd->self.hcpriv = (void *)hcd;
|
||||
|
||||
pnx4008_start_hc();
|
||||
platform_set_drvdata(pdev, hcd);
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
@ -196,12 +197,42 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_wakeup_is_broken(struct uhci_hcd *uhci)
|
||||
{
|
||||
static struct dmi_system_id broken_wakeup_table[] = {
|
||||
{
|
||||
.ident = "Asus A7V8X",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "A7V8X"),
|
||||
DMI_MATCH(DMI_BOARD_VERSION, "REV 1.xx"),
|
||||
}
|
||||
},
|
||||
{ }
|
||||
};
|
||||
int port;
|
||||
|
||||
/* One of Asus's motherboards has a bug which causes it to
|
||||
* wake up immediately from suspend-to-RAM if any of the ports
|
||||
* are connected. In such cases we will not set EGSM.
|
||||
*/
|
||||
if (dmi_check_system(broken_wakeup_table)) {
|
||||
for (port = 0; port < uhci->rh_numports; ++port) {
|
||||
if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
|
||||
USBPORTSC_CCS)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
|
||||
__releases(uhci->lock)
|
||||
__acquires(uhci->lock)
|
||||
{
|
||||
int auto_stop;
|
||||
int int_enable;
|
||||
int int_enable, egsm_enable;
|
||||
|
||||
auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
|
||||
dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
|
||||
@ -217,15 +248,18 @@ __acquires(uhci->lock)
|
||||
}
|
||||
|
||||
/* Enable resume-detect interrupts if they work.
|
||||
* Then enter Global Suspend mode, still configured.
|
||||
* Then enter Global Suspend mode if _it_ works, still configured.
|
||||
*/
|
||||
egsm_enable = USBCMD_EGSM;
|
||||
uhci->working_RD = 1;
|
||||
int_enable = USBINTR_RESUME;
|
||||
if (resume_detect_interrupts_are_broken(uhci)) {
|
||||
if (remote_wakeup_is_broken(uhci))
|
||||
egsm_enable = 0;
|
||||
if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable)
|
||||
uhci->working_RD = int_enable = 0;
|
||||
}
|
||||
|
||||
outw(int_enable, uhci->io_addr + USBINTR);
|
||||
outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD);
|
||||
outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD);
|
||||
mb();
|
||||
udelay(5);
|
||||
|
||||
|
@ -348,13 +348,3 @@ config USB_APPLETOUCH
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called appletouch.
|
||||
|
||||
config USB_TRANCEVIBRATOR
|
||||
tristate "PlayStation 2 Trance Vibrator driver support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want to connect a PlayStation 2 Trance Vibrator
|
||||
device to your computer's USB port.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called trancevibrator.
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
wacom-objs := wacom_sys.o wacom_wac.o
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
usbhid-objs := hid-core.o
|
||||
|
||||
# Optional parts of multipart objects.
|
||||
@ -48,7 +48,6 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_USB_YEALINK) += yealink.o
|
||||
obj-$(CONFIG_USB_XPAD) += xpad.o
|
||||
obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o
|
||||
obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
|
||||
|
||||
ifeq ($(CONFIG_USB_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
|
@ -750,21 +750,31 @@ static __inline__ __u32 s32ton(__s32 value, unsigned n)
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract/implement a data field from/to a report.
|
||||
* Extract/implement a data field from/to a little endian report (bit array).
|
||||
*/
|
||||
|
||||
static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
|
||||
{
|
||||
report += (offset >> 5) << 2; offset &= 31;
|
||||
return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1ULL << n) - 1);
|
||||
u32 x;
|
||||
|
||||
report += offset >> 3; /* adjust byte index */
|
||||
offset &= 8 - 1;
|
||||
x = get_unaligned((u32 *) report);
|
||||
x = le32_to_cpu(x);
|
||||
x = (x >> offset) & ((1 << n) - 1);
|
||||
return x;
|
||||
}
|
||||
|
||||
static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
|
||||
{
|
||||
report += (offset >> 5) << 2; offset &= 31;
|
||||
put_unaligned((get_unaligned((__le64*)report)
|
||||
& cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)))
|
||||
| cpu_to_le64((__u64)value << offset), (__le64*)report);
|
||||
u32 x;
|
||||
|
||||
report += offset >> 3;
|
||||
offset &= 8 - 1;
|
||||
x = get_unaligned((u32 *)report);
|
||||
x &= cpu_to_le32(~((((__u32) 1 << n) - 1) << offset));
|
||||
x |= cpu_to_le32(value << offset);
|
||||
put_unaligned(x,(u32 *)report);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -256,10 +256,10 @@ static int itm_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *pr
|
||||
{
|
||||
*x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
|
||||
*y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
|
||||
*press = ((pkt[2] & 0x1F) << 7) | (pkt[5] & 0x7F);
|
||||
*press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
|
||||
*touch = ~pkt[7] & 0x20;
|
||||
|
||||
return 1;
|
||||
return *touch;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -63,6 +63,7 @@
|
||||
* v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
|
||||
* - where wacom_sys.c deals with system specific code,
|
||||
* - and wacom_wac.c deals with Wacom specific code
|
||||
* - Support Intuos3 4x6
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -118,6 +119,7 @@ extern void wacom_input_sync(void *wcombo);
|
||||
extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
|
||||
|
@ -110,7 +110,7 @@ __u16 wacom_be16_to_cpu(unsigned char *data)
|
||||
__u16 wacom_le16_to_cpu(unsigned char *data)
|
||||
{
|
||||
__u16 value;
|
||||
value = be16_to_cpu(*(__be16 *) data);
|
||||
value = le16_to_cpu(*(__le16 *) data);
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
input_dev->evbit[0] |= BIT(EV_MSC);
|
||||
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_4);
|
||||
}
|
||||
|
||||
void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
@ -155,11 +155,16 @@ void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
|
||||
}
|
||||
|
||||
void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3);
|
||||
input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
|
||||
}
|
||||
|
||||
void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
|
||||
{
|
||||
input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
|
||||
input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
|
||||
}
|
||||
|
||||
@ -218,8 +223,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
|
||||
strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
|
||||
|
||||
wacom_wac->features = get_wacom_feature(id);
|
||||
if (wacom_wac->features->pktlen > 10)
|
||||
BUG();
|
||||
BUG_ON(wacom_wac->features->pktlen > 10);
|
||||
|
||||
input_dev->name = wacom_wac->features->name;
|
||||
wacom->wacom_wac = wacom_wac;
|
||||
@ -244,7 +248,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
|
||||
usb_fill_int_urb(wacom->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
wacom_wac->data, wacom_wac->features->pktlen,
|
||||
wacom_wac->features->irq, wacom, endpoint->bInterval);
|
||||
wacom_sys_irq, wacom, endpoint->bInterval);
|
||||
wacom->irq->transfer_dma = wacom->data_dma;
|
||||
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
@ -278,8 +282,8 @@ static void wacom_disconnect(struct usb_interface *intf)
|
||||
input_unregister_device(wacom->dev);
|
||||
usb_free_urb(wacom->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
|
||||
kfree(wacom);
|
||||
kfree(wacom->wacom_wac);
|
||||
kfree(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,9 +191,9 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
|
||||
if (wacom->features->type == WACOM_G4)
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[6]);
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f);
|
||||
else
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[7]);
|
||||
wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -303,8 +303,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
}
|
||||
/* only large I3 support Lens Cursor */
|
||||
if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
|
||||
(wacom->features->type == INTUOS3))) {
|
||||
if(!((wacom->tool[idx] == BTN_TOOL_LENS)
|
||||
&& ((wacom->features->type == INTUOS3)
|
||||
|| (wacom->features->type == INTUOS3S)))) {
|
||||
wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 1);
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
@ -315,10 +316,14 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
|
||||
|
||||
/* Exit report */
|
||||
if ((data[1] & 0xfe) == 0x80) {
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
return 2;
|
||||
if(!((wacom->tool[idx] == BTN_TOOL_LENS)
|
||||
&& ((wacom->features->type == INTUOS3)
|
||||
|| (wacom->features->type == INTUOS3S)))) {
|
||||
wacom_report_key(wcombo, wacom->tool[idx], 0);
|
||||
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
|
||||
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -382,7 +387,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
|
||||
wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
|
||||
|
||||
if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
|
||||
if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) |
|
||||
data[2] | (data[3] & 0x1f) | data[4])
|
||||
wacom_report_key(wcombo, wacom->tool[1], 1);
|
||||
else
|
||||
wacom_report_key(wcombo, wacom->tool[1], 0);
|
||||
@ -432,7 +438,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
((t - 1) / 2) : -t / 2);
|
||||
}
|
||||
|
||||
} else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
|
||||
} else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3S) {
|
||||
/* 4D mouse packet */
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
|
||||
@ -452,12 +458,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||
- ((data[8] & 0x02) >> 1));
|
||||
|
||||
/* I3 2D mouse side buttons */
|
||||
if (wacom->features->type == INTUOS3) {
|
||||
if (wacom->features->type >= INTUOS3S && wacom->features->type <= INTUOS3L) {
|
||||
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
|
||||
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
|
||||
}
|
||||
|
||||
} else if (wacom->features->type < INTUOS3) {
|
||||
} else if (wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L) {
|
||||
/* Lens cursor packets */
|
||||
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
|
||||
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
|
||||
@ -490,6 +496,7 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
|
||||
return (wacom_ptu_irq(wacom_wac, wcombo));
|
||||
break;
|
||||
case INTUOS:
|
||||
case INTUOS3S:
|
||||
case INTUOS3:
|
||||
case INTUOS3L:
|
||||
case CINTIQ:
|
||||
@ -515,6 +522,8 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
|
||||
case CINTIQ:
|
||||
input_dev_i3(input_dev, wacom_wac);
|
||||
/* fall through */
|
||||
case INTUOS3S:
|
||||
input_dev_i3s(input_dev, wacom_wac);
|
||||
case INTUOS:
|
||||
input_dev_i(input_dev, wacom_wac);
|
||||
break;
|
||||
@ -530,49 +539,50 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
|
||||
}
|
||||
|
||||
static struct wacom_features wacom_features[] = {
|
||||
{ "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_sys_irq },
|
||||
{ "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, WACOM_G4, wacom_sys_irq },
|
||||
{ "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, WACOM_G4, wacom_sys_irq },
|
||||
{ "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom PenStation2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_sys_irq },
|
||||
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_sys_irq},
|
||||
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_sys_irq},
|
||||
{ "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL700", 8, 6758, 5406, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom PL510", 8, 6282, 4762, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom DTU710", 8, 34080, 27660, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom DTF521", 8, 6282, 4762, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom DTF720", 8, 6858, 5506, 511, 32, PL, wacom_sys_irq },
|
||||
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PTU, wacom_sys_irq },
|
||||
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_sys_irq },
|
||||
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_sys_irq },
|
||||
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_sys_irq },
|
||||
{ "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS3L, wacom_sys_irq },
|
||||
{ "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS3L, wacom_sys_irq },
|
||||
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 15, INTUOS3, wacom_sys_irq },
|
||||
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_sys_irq },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
|
||||
{ "Wacom Penpartner", 7, 5040, 3780, 255, 0, PENPARTNER },
|
||||
{ "Wacom Graphire", 8, 10206, 7422, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire3", 8, 10208, 7424, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 63, GRAPHIRE },
|
||||
{ "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 63, WACOM_G4 },
|
||||
{ "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 63, WACOM_G4 },
|
||||
{ "Wacom Volito", 8, 5104, 3712, 511, 0, GRAPHIRE },
|
||||
{ "Wacom PenStation2", 8, 3250, 2320, 255, 0, GRAPHIRE },
|
||||
{ "Wacom Volito2 4x5", 8, 5104, 3712, 511, 0, GRAPHIRE },
|
||||
{ "Wacom Volito2 2x3", 8, 3248, 2320, 511, 0, GRAPHIRE },
|
||||
{ "Wacom PenPartner2", 8, 3250, 2320, 255, 0, GRAPHIRE },
|
||||
{ "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
|
||||
{ "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
|
||||
{ "Wacom PL600", 8, 6126, 4604, 255, 0, PL },
|
||||
{ "Wacom PL600SX", 8, 6260, 5016, 255, 0, PL },
|
||||
{ "Wacom PL550", 8, 6144, 4608, 511, 0, PL },
|
||||
{ "Wacom PL800", 8, 7220, 5780, 511, 0, PL },
|
||||
{ "Wacom PL700", 8, 6758, 5406, 511, 0, PL },
|
||||
{ "Wacom PL510", 8, 6282, 4762, 511, 0, PL },
|
||||
{ "Wacom DTU710", 8, 34080, 27660, 511, 0, PL },
|
||||
{ "Wacom DTF521", 8, 6282, 4762, 511, 0, PL },
|
||||
{ "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
|
||||
{ "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
|
||||
{ "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 63, INTUOS },
|
||||
{ "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
|
||||
{ "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 63, INTUOS3L },
|
||||
{ "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L },
|
||||
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
|
||||
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 15, INTUOS3S },
|
||||
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
|
||||
{ "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -618,6 +628,7 @@ static struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
|
||||
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
|
||||
{ }
|
||||
|
@ -20,6 +20,7 @@ enum {
|
||||
PTU,
|
||||
PL,
|
||||
INTUOS,
|
||||
INTUOS3S,
|
||||
INTUOS3,
|
||||
INTUOS3L,
|
||||
CINTIQ,
|
||||
@ -34,7 +35,6 @@ struct wacom_features {
|
||||
int pressure_max;
|
||||
int distance_max;
|
||||
int type;
|
||||
usb_complete_t irq;
|
||||
};
|
||||
|
||||
struct wacom_wac {
|
||||
|
@ -1,8 +1,9 @@
|
||||
/*
|
||||
* X-Box gamepad - v0.0.5
|
||||
* X-Box gamepad - v0.0.6
|
||||
*
|
||||
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
|
||||
*
|
||||
* 2005 Dominic Cerquetti <binary1230@yahoo.com>
|
||||
* 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -30,9 +31,10 @@
|
||||
* - Greg Kroah-Hartman - usb-skeleton driver
|
||||
*
|
||||
* TODO:
|
||||
* - fine tune axes
|
||||
* - fine tune axes (especially trigger axes)
|
||||
* - fix "analog" buttons (reported as digital now)
|
||||
* - get rumble working
|
||||
* - need USB IDs for other dance pads
|
||||
*
|
||||
* History:
|
||||
*
|
||||
@ -57,25 +59,40 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_VERSION "v0.0.5"
|
||||
#define DRIVER_VERSION "v0.0.6"
|
||||
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
|
||||
#define DRIVER_DESC "X-Box pad driver"
|
||||
|
||||
#define XPAD_PKT_LEN 32
|
||||
|
||||
/* xbox d-pads should map to buttons, as is required for DDR pads
|
||||
but we map them to axes when possible to simplify things */
|
||||
#define MAP_DPAD_TO_BUTTONS 0
|
||||
#define MAP_DPAD_TO_AXES 1
|
||||
#define MAP_DPAD_UNKNOWN -1
|
||||
|
||||
static int dpad_to_buttons;
|
||||
module_param(dpad_to_buttons, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
|
||||
|
||||
static const struct xpad_device {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
char *name;
|
||||
u8 dpad_mapping;
|
||||
} xpad_device[] = {
|
||||
{ 0x045e, 0x0202, "Microsoft X-Box pad (US)" },
|
||||
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)" },
|
||||
{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)" },
|
||||
{ 0x0000, 0x0000, "X-Box pad" }
|
||||
{ 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES },
|
||||
{ 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES },
|
||||
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES },
|
||||
{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES },
|
||||
{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS },
|
||||
{ 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN }
|
||||
};
|
||||
|
||||
static const signed short xpad_btn[] = {
|
||||
@ -84,11 +101,23 @@ static const signed short xpad_btn[] = {
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
/* only used if MAP_DPAD_TO_BUTTONS */
|
||||
static const signed short xpad_btn_pad[] = {
|
||||
BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
|
||||
BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
static const signed short xpad_abs[] = {
|
||||
ABS_X, ABS_Y, /* left stick */
|
||||
ABS_RX, ABS_RY, /* right stick */
|
||||
ABS_Z, ABS_RZ, /* triggers left/right */
|
||||
ABS_HAT0X, ABS_HAT0Y, /* digital pad */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
/* only used if MAP_DPAD_TO_AXES */
|
||||
static const signed short xpad_abs_pad[] = {
|
||||
ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
@ -100,14 +129,16 @@ static struct usb_device_id xpad_table [] = {
|
||||
MODULE_DEVICE_TABLE (usb, xpad_table);
|
||||
|
||||
struct usb_xpad {
|
||||
struct input_dev *dev; /* input device interface */
|
||||
struct usb_device *udev; /* usb device */
|
||||
struct input_dev *dev; /* input device interface */
|
||||
struct usb_device *udev; /* usb device */
|
||||
|
||||
struct urb *irq_in; /* urb for interrupt in report */
|
||||
unsigned char *idata; /* input data */
|
||||
struct urb *irq_in; /* urb for interrupt in report */
|
||||
unsigned char *idata; /* input data */
|
||||
dma_addr_t idata_dma;
|
||||
|
||||
char phys[65]; /* physical device path */
|
||||
char phys[65]; /* physical device path */
|
||||
|
||||
int dpad_mapping; /* map d-pad to buttons or to axes */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -137,14 +168,21 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||
input_report_abs(dev, ABS_RZ, data[11]);
|
||||
|
||||
/* digital pad */
|
||||
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
||||
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
||||
input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||
input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
||||
} else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
|
||||
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
||||
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
||||
input_report_key(dev, BTN_0, data[2] & 0x01); // up
|
||||
input_report_key(dev, BTN_1, data[2] & 0x02); // down
|
||||
}
|
||||
|
||||
/* start/back buttons and stick press left/right */
|
||||
input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4);
|
||||
input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5);
|
||||
input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6);
|
||||
input_report_key(dev, BTN_THUMBR, data[2] >> 7);
|
||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
||||
input_report_key(dev, BTN_BACK, data[2] & 0x20);
|
||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
||||
|
||||
/* "analog" buttons A, B, X, Y */
|
||||
input_report_key(dev, BTN_A, data[4]);
|
||||
@ -206,6 +244,28 @@ static void xpad_close (struct input_dev *dev)
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
}
|
||||
|
||||
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
{
|
||||
set_bit(abs, input_dev->absbit);
|
||||
|
||||
switch (abs) {
|
||||
case ABS_X:
|
||||
case ABS_Y:
|
||||
case ABS_RX:
|
||||
case ABS_RY: /* the two sticks */
|
||||
input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
|
||||
break;
|
||||
case ABS_Z:
|
||||
case ABS_RZ: /* the triggers */
|
||||
input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
|
||||
break;
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */
|
||||
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev (intf);
|
||||
@ -235,6 +295,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
goto fail2;
|
||||
|
||||
xpad->udev = udev;
|
||||
xpad->dpad_mapping = xpad_device[i].dpad_mapping;
|
||||
if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
|
||||
xpad->dpad_mapping = dpad_to_buttons;
|
||||
xpad->dev = input_dev;
|
||||
usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
|
||||
strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
|
||||
@ -249,32 +312,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
|
||||
/* set up buttons */
|
||||
for (i = 0; xpad_btn[i] >= 0; i++)
|
||||
set_bit(xpad_btn[i], input_dev->keybit);
|
||||
if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
|
||||
for (i = 0; xpad_btn_pad[i] >= 0; i++)
|
||||
set_bit(xpad_btn_pad[i], input_dev->keybit);
|
||||
|
||||
for (i = 0; xpad_abs[i] >= 0; i++) {
|
||||
|
||||
signed short t = xpad_abs[i];
|
||||
|
||||
set_bit(t, input_dev->absbit);
|
||||
|
||||
switch (t) {
|
||||
case ABS_X:
|
||||
case ABS_Y:
|
||||
case ABS_RX:
|
||||
case ABS_RY: /* the two sticks */
|
||||
input_set_abs_params(input_dev, t, -32768, 32767, 16, 128);
|
||||
break;
|
||||
case ABS_Z:
|
||||
case ABS_RZ: /* the triggers */
|
||||
input_set_abs_params(input_dev, t, 0, 255, 0, 0);
|
||||
break;
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y: /* the d-pad */
|
||||
input_set_abs_params(input_dev, t, -1, 1, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* set up axes */
|
||||
for (i = 0; xpad_abs[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs[i]);
|
||||
if (xpad->dpad_mapping == MAP_DPAD_TO_AXES)
|
||||
for (i = 0; xpad_abs_pad[i] >= 0; i++)
|
||||
xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
|
||||
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(xpad->irq_in, udev,
|
||||
@ -305,7 +355,8 @@ static void xpad_disconnect(struct usb_interface *intf)
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
input_unregister_device(xpad->dev);
|
||||
usb_free_urb(xpad->irq_in);
|
||||
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
||||
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN,
|
||||
xpad->idata, xpad->idata_dma);
|
||||
kfree(xpad);
|
||||
}
|
||||
}
|
||||
|
@ -223,6 +223,16 @@ config USB_LD
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ldusb.
|
||||
|
||||
config USB_TRANCEVIBRATOR
|
||||
tristate "PlayStation 2 Trance Vibrator driver support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want to connect a PlayStation 2 Trance Vibrator
|
||||
device to your computer's USB port.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called trancevibrator.
|
||||
|
||||
config USB_TEST
|
||||
tristate "USB testing driver (DEVELOPMENT)"
|
||||
depends on USB && USB_DEVICEFS && EXPERIMENTAL
|
||||
|
@ -21,6 +21,7 @@ obj-$(CONFIG_USB_PHIDGETMOTORCONTROL) += phidgetmotorcontrol.o
|
||||
obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
|
||||
obj-$(CONFIG_USB_RIO500) += rio500.o
|
||||
obj-$(CONFIG_USB_TEST) += usbtest.o
|
||||
obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
|
||||
obj-$(CONFIG_USB_USS720) += uss720.o
|
||||
|
||||
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
|
||||
|
@ -370,7 +370,8 @@ static int adu_release(struct inode *inode, struct file *file)
|
||||
retval = adu_release_internal(dev);
|
||||
|
||||
exit:
|
||||
up(&dev->sem);
|
||||
if (dev)
|
||||
up(&dev->sem);
|
||||
dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
@ -513,8 +513,6 @@ static void ftdi_elan_respond_work(void *data)
|
||||
ftdi->disconnected += 1;
|
||||
} else if (retval == -ENODEV) {
|
||||
ftdi->disconnected += 1;
|
||||
} else if (retval == -ENODEV) {
|
||||
ftdi->disconnected += 1;
|
||||
} else if (retval == -EILSEQ) {
|
||||
ftdi->disconnected += 1;
|
||||
} else {
|
||||
@ -1186,11 +1184,8 @@ static ssize_t ftdi_elan_write(struct file *file,
|
||||
int retval = 0;
|
||||
struct urb *urb;
|
||||
char *buf;
|
||||
char data[30 *3 + 4];
|
||||
char *d = data;
|
||||
const char __user *s = user_buffer;
|
||||
int m = (sizeof(data) - 1) / 3;
|
||||
struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
|
||||
struct usb_ftdi *ftdi = file->private_data;
|
||||
|
||||
if (ftdi->disconnected > 0) {
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -1220,27 +1215,18 @@ static ssize_t ftdi_elan_write(struct file *file,
|
||||
if (retval) {
|
||||
dev_err(&ftdi->udev->dev, "failed submitting write urb, error %"
|
||||
"d\n", retval);
|
||||
goto error_4;
|
||||
goto error_3;
|
||||
}
|
||||
usb_free_urb(urb);
|
||||
exit:;
|
||||
if (count > m) {
|
||||
int I = m - 1;
|
||||
while (I-- > 0) {
|
||||
d += sprintf(d, " %02X", 0x000000FF & *s++);
|
||||
}
|
||||
d += sprintf(d, " ..");
|
||||
} else {
|
||||
int I = count;
|
||||
while (I-- > 0) {
|
||||
d += sprintf(d, " %02X", 0x000000FF & *s++);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return count;
|
||||
error_4: error_3:usb_buffer_free(ftdi->udev, count, buf,
|
||||
urb->transfer_dma);
|
||||
error_2:usb_free_urb(urb);
|
||||
error_1:return retval;
|
||||
error_3:
|
||||
usb_buffer_free(ftdi->udev, count, buf, urb->transfer_dma);
|
||||
error_2:
|
||||
usb_free_urb(urb);
|
||||
error_1:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct file_operations ftdi_elan_fops = {
|
||||
|
@ -207,6 +207,14 @@ config USB_NET_PLUSB
|
||||
Choose this option if you're using a host-to-host cable
|
||||
with one of these chips.
|
||||
|
||||
config USB_NET_MCS7830
|
||||
tristate "MosChip MCS7830 based Ethernet adapters"
|
||||
depends on USB_USBNET
|
||||
help
|
||||
Choose this option if you're using a 10/100 Ethernet USB2
|
||||
adapter based on the MosChip 7830 controller. This includes
|
||||
adapters marketed under the DeLOCK brand.
|
||||
|
||||
config USB_NET_RNDIS_HOST
|
||||
tristate "Host for RNDIS devices (EXPERIMENTAL)"
|
||||
depends on USB_USBNET && EXPERIMENTAL
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_USB_NET_PLUSB) += plusb.o
|
||||
obj-$(CONFIG_USB_NET_RNDIS_HOST) += rndis_host.o
|
||||
obj-$(CONFIG_USB_NET_CDC_SUBSET) += cdc_subset.o
|
||||
obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o
|
||||
obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o
|
||||
obj-$(CONFIG_USB_USBNET) += usbnet.o
|
||||
|
||||
ifeq ($(CONFIG_USB_DEBUG),y)
|
||||
|
@ -569,10 +569,12 @@ static int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
|
||||
struct usbnet *dev = netdev_priv(netdev);
|
||||
u16 res;
|
||||
|
||||
mutex_lock(&dev->phy_mutex);
|
||||
asix_set_sw_mii(dev);
|
||||
asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
|
||||
(__u16)loc, 2, (u16 *)&res);
|
||||
asix_set_hw_mii(dev);
|
||||
mutex_unlock(&dev->phy_mutex);
|
||||
|
||||
devdbg(dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", phy_id, loc, le16_to_cpu(res & 0xffff));
|
||||
|
||||
@ -586,10 +588,12 @@ asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
|
||||
u16 res = cpu_to_le16(val);
|
||||
|
||||
devdbg(dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", phy_id, loc, val);
|
||||
mutex_lock(&dev->phy_mutex);
|
||||
asix_set_sw_mii(dev);
|
||||
asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
|
||||
(__u16)loc, 2, (u16 *)&res);
|
||||
asix_set_hw_mii(dev);
|
||||
mutex_unlock(&dev->phy_mutex);
|
||||
}
|
||||
|
||||
/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
|
||||
@ -700,32 +704,6 @@ static void asix_get_drvinfo (struct net_device *net,
|
||||
info->eedump_len = data->eeprom_len;
|
||||
}
|
||||
|
||||
static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
return mii_ethtool_gset(&dev->mii,cmd);
|
||||
}
|
||||
|
||||
static int asix_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
int res = mii_ethtool_sset(&dev->mii,cmd);
|
||||
|
||||
/* link speed/duplex might have changed */
|
||||
if (dev->driver_info->link_reset)
|
||||
dev->driver_info->link_reset(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int asix_nway_reset(struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
return mii_nway_restart(&dev->mii);
|
||||
}
|
||||
|
||||
static u32 asix_get_link(struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
@ -746,15 +724,15 @@ static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
|
||||
static struct ethtool_ops ax88172_ethtool_ops = {
|
||||
.get_drvinfo = asix_get_drvinfo,
|
||||
.get_link = asix_get_link,
|
||||
.nway_reset = asix_nway_reset,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
.get_wol = asix_get_wol,
|
||||
.set_wol = asix_set_wol,
|
||||
.get_eeprom_len = asix_get_eeprom_len,
|
||||
.get_eeprom = asix_get_eeprom,
|
||||
.get_settings = asix_get_settings,
|
||||
.set_settings = asix_set_settings,
|
||||
.get_settings = usbnet_get_settings,
|
||||
.set_settings = usbnet_set_settings,
|
||||
.nway_reset = usbnet_nway_reset,
|
||||
};
|
||||
|
||||
static void ax88172_set_multicast(struct net_device *net)
|
||||
@ -885,15 +863,15 @@ out1:
|
||||
static struct ethtool_ops ax88772_ethtool_ops = {
|
||||
.get_drvinfo = asix_get_drvinfo,
|
||||
.get_link = asix_get_link,
|
||||
.nway_reset = asix_nway_reset,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
.get_wol = asix_get_wol,
|
||||
.set_wol = asix_set_wol,
|
||||
.get_eeprom_len = asix_get_eeprom_len,
|
||||
.get_eeprom = asix_get_eeprom,
|
||||
.get_settings = asix_get_settings,
|
||||
.set_settings = asix_set_settings,
|
||||
.get_settings = usbnet_get_settings,
|
||||
.set_settings = usbnet_set_settings,
|
||||
.nway_reset = usbnet_nway_reset,
|
||||
};
|
||||
|
||||
static int ax88772_link_reset(struct usbnet *dev)
|
||||
@ -1046,15 +1024,15 @@ out1:
|
||||
static struct ethtool_ops ax88178_ethtool_ops = {
|
||||
.get_drvinfo = asix_get_drvinfo,
|
||||
.get_link = asix_get_link,
|
||||
.nway_reset = asix_nway_reset,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
.get_wol = asix_get_wol,
|
||||
.set_wol = asix_set_wol,
|
||||
.get_eeprom_len = asix_get_eeprom_len,
|
||||
.get_eeprom = asix_get_eeprom,
|
||||
.get_settings = asix_get_settings,
|
||||
.set_settings = asix_set_settings,
|
||||
.get_settings = usbnet_get_settings,
|
||||
.set_settings = usbnet_set_settings,
|
||||
.nway_reset = usbnet_nway_reset,
|
||||
};
|
||||
|
||||
static int marvell_phy_init(struct usbnet *dev)
|
||||
|
@ -498,7 +498,7 @@ static struct usb_driver cdc_driver = {
|
||||
|
||||
static int __init cdc_init(void)
|
||||
{
|
||||
BUG_ON((sizeof(((struct usbnet *)0)->data)
|
||||
BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
|
||||
< sizeof(struct cdc_state)));
|
||||
|
||||
return usb_register(&cdc_driver);
|
||||
|
@ -65,16 +65,6 @@
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define kaweth_dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" ,##arg)
|
||||
#else
|
||||
#define kaweth_dbg(format, arg...) do {} while (0)
|
||||
#endif
|
||||
#define kaweth_err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" ,##arg)
|
||||
#define kaweth_info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ##arg)
|
||||
#define kaweth_warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ##arg)
|
||||
|
||||
|
||||
#include "kawethfw.h"
|
||||
|
||||
#define KAWETH_MTU 1514
|
||||
@ -86,6 +76,9 @@
|
||||
|
||||
#define KAWETH_STATUS_BROKEN 0x0000001
|
||||
#define KAWETH_STATUS_CLOSING 0x0000002
|
||||
#define KAWETH_STATUS_SUSPENDING 0x0000004
|
||||
|
||||
#define KAWETH_STATUS_BLOCKED (KAWETH_STATUS_CLOSING | KAWETH_STATUS_SUSPENDING)
|
||||
|
||||
#define KAWETH_PACKET_FILTER_PROMISCUOUS 0x01
|
||||
#define KAWETH_PACKET_FILTER_ALL_MULTICAST 0x02
|
||||
@ -112,6 +105,8 @@
|
||||
#define STATE_MASK 0x40
|
||||
#define STATE_SHIFT 5
|
||||
|
||||
#define IS_BLOCKED(s) (s & KAWETH_STATUS_BLOCKED)
|
||||
|
||||
|
||||
MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr>, Brad Hards <bhards@bigpond.net.au> and Oliver Neukum <oliver@neukum.org>");
|
||||
MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver");
|
||||
@ -128,6 +123,8 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,
|
||||
unsigned int pipe,
|
||||
struct usb_ctrlrequest *cmd, void *data,
|
||||
int len, int timeout);
|
||||
static int kaweth_suspend(struct usb_interface *intf, pm_message_t message);
|
||||
static int kaweth_resume(struct usb_interface *intf);
|
||||
|
||||
/****************************************************************
|
||||
* usb_device_id
|
||||
@ -179,6 +176,8 @@ static struct usb_driver kaweth_driver = {
|
||||
.name = driver_name,
|
||||
.probe = kaweth_probe,
|
||||
.disconnect = kaweth_disconnect,
|
||||
.suspend = kaweth_suspend,
|
||||
.resume = kaweth_resume,
|
||||
.id_table = usb_klsi_table,
|
||||
};
|
||||
|
||||
@ -222,6 +221,7 @@ struct kaweth_device
|
||||
int suspend_lowmem_rx;
|
||||
int suspend_lowmem_ctrl;
|
||||
int linkstate;
|
||||
int opened;
|
||||
struct work_struct lowmem_work;
|
||||
|
||||
struct usb_device *dev;
|
||||
@ -265,17 +265,17 @@ static int kaweth_control(struct kaweth_device *kaweth,
|
||||
{
|
||||
struct usb_ctrlrequest *dr;
|
||||
|
||||
kaweth_dbg("kaweth_control()");
|
||||
dbg("kaweth_control()");
|
||||
|
||||
if(in_interrupt()) {
|
||||
kaweth_dbg("in_interrupt()");
|
||||
dbg("in_interrupt()");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
|
||||
|
||||
if (!dr) {
|
||||
kaweth_dbg("kmalloc() failed");
|
||||
dbg("kmalloc() failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -300,7 +300,7 @@ static int kaweth_read_configuration(struct kaweth_device *kaweth)
|
||||
{
|
||||
int retval;
|
||||
|
||||
kaweth_dbg("Reading kaweth configuration");
|
||||
dbg("Reading kaweth configuration");
|
||||
|
||||
retval = kaweth_control(kaweth,
|
||||
usb_rcvctrlpipe(kaweth->dev, 0),
|
||||
@ -322,7 +322,7 @@ static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
kaweth_dbg("Setting URB size to %d", (unsigned)urb_size);
|
||||
dbg("Setting URB size to %d", (unsigned)urb_size);
|
||||
|
||||
retval = kaweth_control(kaweth,
|
||||
usb_sndctrlpipe(kaweth->dev, 0),
|
||||
@ -344,7 +344,7 @@ static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait)
|
||||
{
|
||||
int retval;
|
||||
|
||||
kaweth_dbg("Set SOFS wait to %d", (unsigned)sofs_wait);
|
||||
dbg("Set SOFS wait to %d", (unsigned)sofs_wait);
|
||||
|
||||
retval = kaweth_control(kaweth,
|
||||
usb_sndctrlpipe(kaweth->dev, 0),
|
||||
@ -367,7 +367,7 @@ static int kaweth_set_receive_filter(struct kaweth_device *kaweth,
|
||||
{
|
||||
int retval;
|
||||
|
||||
kaweth_dbg("Set receive filter to %d", (unsigned)receive_filter);
|
||||
dbg("Set receive filter to %d", (unsigned)receive_filter);
|
||||
|
||||
retval = kaweth_control(kaweth,
|
||||
usb_sndctrlpipe(kaweth->dev, 0),
|
||||
@ -392,7 +392,7 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth,
|
||||
__u8 type)
|
||||
{
|
||||
if(data_len > KAWETH_FIRMWARE_BUF_SIZE) {
|
||||
kaweth_err("Firmware too big: %d", data_len);
|
||||
err("Firmware too big: %d", data_len);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
@ -403,13 +403,13 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth,
|
||||
kaweth->firmware_buf[4] = type;
|
||||
kaweth->firmware_buf[5] = interrupt;
|
||||
|
||||
kaweth_dbg("High: %i, Low:%i", kaweth->firmware_buf[3],
|
||||
dbg("High: %i, Low:%i", kaweth->firmware_buf[3],
|
||||
kaweth->firmware_buf[2]);
|
||||
|
||||
kaweth_dbg("Downloading firmware at %p to kaweth device at %p",
|
||||
dbg("Downloading firmware at %p to kaweth device at %p",
|
||||
data,
|
||||
kaweth);
|
||||
kaweth_dbg("Firmware length: %d", data_len);
|
||||
dbg("Firmware length: %d", data_len);
|
||||
|
||||
return kaweth_control(kaweth,
|
||||
usb_sndctrlpipe(kaweth->dev, 0),
|
||||
@ -437,7 +437,7 @@ static int kaweth_trigger_firmware(struct kaweth_device *kaweth,
|
||||
kaweth->firmware_buf[6] = 0x00;
|
||||
kaweth->firmware_buf[7] = 0x00;
|
||||
|
||||
kaweth_dbg("Triggering firmware");
|
||||
dbg("Triggering firmware");
|
||||
|
||||
return kaweth_control(kaweth,
|
||||
usb_sndctrlpipe(kaweth->dev, 0),
|
||||
@ -457,7 +457,7 @@ static int kaweth_reset(struct kaweth_device *kaweth)
|
||||
{
|
||||
int result;
|
||||
|
||||
kaweth_dbg("kaweth_reset(%p)", kaweth);
|
||||
dbg("kaweth_reset(%p)", kaweth);
|
||||
result = kaweth_control(kaweth,
|
||||
usb_sndctrlpipe(kaweth->dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
@ -470,7 +470,7 @@ static int kaweth_reset(struct kaweth_device *kaweth)
|
||||
|
||||
mdelay(10);
|
||||
|
||||
kaweth_dbg("kaweth_reset() returns %d.",result);
|
||||
dbg("kaweth_reset() returns %d.",result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -534,7 +534,7 @@ static void kaweth_resubmit_tl(void *d)
|
||||
{
|
||||
struct kaweth_device *kaweth = (struct kaweth_device *)d;
|
||||
|
||||
if (kaweth->status | KAWETH_STATUS_CLOSING)
|
||||
if (IS_BLOCKED(kaweth->status))
|
||||
return;
|
||||
|
||||
if (kaweth->suspend_lowmem_rx)
|
||||
@ -568,7 +568,7 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
|
||||
kaweth->suspend_lowmem_rx = 1;
|
||||
schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
|
||||
}
|
||||
kaweth_err("resubmitting rx_urb %d failed", result);
|
||||
err("resubmitting rx_urb %d failed", result);
|
||||
} else {
|
||||
kaweth->suspend_lowmem_rx = 0;
|
||||
}
|
||||
@ -601,11 +601,15 @@ static void kaweth_usb_receive(struct urb *urb)
|
||||
return;
|
||||
}
|
||||
|
||||
if (kaweth->status & KAWETH_STATUS_CLOSING)
|
||||
spin_lock(&kaweth->device_lock);
|
||||
if (IS_BLOCKED(kaweth->status)) {
|
||||
spin_unlock(&kaweth->device_lock);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&kaweth->device_lock);
|
||||
|
||||
if(urb->status && urb->status != -EREMOTEIO && count != 1) {
|
||||
kaweth_err("%s RX status: %d count: %d packet_len: %d",
|
||||
err("%s RX status: %d count: %d packet_len: %d",
|
||||
net->name,
|
||||
urb->status,
|
||||
count,
|
||||
@ -616,9 +620,9 @@ static void kaweth_usb_receive(struct urb *urb)
|
||||
|
||||
if(kaweth->net && (count > 2)) {
|
||||
if(pkt_len > (count - 2)) {
|
||||
kaweth_err("Packet length too long for USB frame (pkt_len: %x, count: %x)",pkt_len, count);
|
||||
kaweth_err("Packet len & 2047: %x", pkt_len & 2047);
|
||||
kaweth_err("Count 2: %x", count2);
|
||||
err("Packet length too long for USB frame (pkt_len: %x, count: %x)",pkt_len, count);
|
||||
err("Packet len & 2047: %x", pkt_len & 2047);
|
||||
err("Count 2: %x", count2);
|
||||
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
|
||||
return;
|
||||
}
|
||||
@ -655,7 +659,7 @@ static int kaweth_open(struct net_device *net)
|
||||
struct kaweth_device *kaweth = netdev_priv(net);
|
||||
int res;
|
||||
|
||||
kaweth_dbg("Opening network device.");
|
||||
dbg("Opening network device.");
|
||||
|
||||
res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL);
|
||||
if (res)
|
||||
@ -678,6 +682,7 @@ static int kaweth_open(struct net_device *net)
|
||||
usb_kill_urb(kaweth->rx_urb);
|
||||
return -EIO;
|
||||
}
|
||||
kaweth->opened = 1;
|
||||
|
||||
netif_start_queue(net);
|
||||
|
||||
@ -688,14 +693,8 @@ static int kaweth_open(struct net_device *net)
|
||||
/****************************************************************
|
||||
* kaweth_close
|
||||
****************************************************************/
|
||||
static int kaweth_close(struct net_device *net)
|
||||
static void kaweth_kill_urbs(struct kaweth_device *kaweth)
|
||||
{
|
||||
struct kaweth_device *kaweth = netdev_priv(net);
|
||||
|
||||
netif_stop_queue(net);
|
||||
|
||||
kaweth->status |= KAWETH_STATUS_CLOSING;
|
||||
|
||||
usb_kill_urb(kaweth->irq_urb);
|
||||
usb_kill_urb(kaweth->rx_urb);
|
||||
usb_kill_urb(kaweth->tx_urb);
|
||||
@ -706,6 +705,21 @@ static int kaweth_close(struct net_device *net)
|
||||
we hit them again */
|
||||
usb_kill_urb(kaweth->irq_urb);
|
||||
usb_kill_urb(kaweth->rx_urb);
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* kaweth_close
|
||||
****************************************************************/
|
||||
static int kaweth_close(struct net_device *net)
|
||||
{
|
||||
struct kaweth_device *kaweth = netdev_priv(net);
|
||||
|
||||
netif_stop_queue(net);
|
||||
kaweth->opened = 0;
|
||||
|
||||
kaweth->status |= KAWETH_STATUS_CLOSING;
|
||||
|
||||
kaweth_kill_urbs(kaweth);
|
||||
|
||||
kaweth->status &= ~KAWETH_STATUS_CLOSING;
|
||||
|
||||
@ -732,7 +746,7 @@ static void kaweth_usb_transmit_complete(struct urb *urb)
|
||||
|
||||
if (unlikely(urb->status != 0))
|
||||
if (urb->status != -ENOENT)
|
||||
kaweth_dbg("%s: TX status %d.", kaweth->net->name, urb->status);
|
||||
dbg("%s: TX status %d.", kaweth->net->name, urb->status);
|
||||
|
||||
netif_wake_queue(kaweth->net);
|
||||
dev_kfree_skb_irq(skb);
|
||||
@ -752,6 +766,9 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||
|
||||
kaweth_async_set_rx_mode(kaweth);
|
||||
netif_stop_queue(net);
|
||||
if (IS_BLOCKED(kaweth->status)) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* We now decide whether we can put our special header into the sk_buff */
|
||||
if (skb_cloned(skb) || skb_headroom(skb) < 2) {
|
||||
@ -783,7 +800,8 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||
|
||||
if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC)))
|
||||
{
|
||||
kaweth_warn("kaweth failed tx_urb %d", res);
|
||||
warn("kaweth failed tx_urb %d", res);
|
||||
skip:
|
||||
kaweth->stats.tx_errors++;
|
||||
|
||||
netif_start_queue(net);
|
||||
@ -812,7 +830,7 @@ static void kaweth_set_rx_mode(struct net_device *net)
|
||||
KAWETH_PACKET_FILTER_BROADCAST |
|
||||
KAWETH_PACKET_FILTER_MULTICAST;
|
||||
|
||||
kaweth_dbg("Setting Rx mode to %d", packet_filter_bitmap);
|
||||
dbg("Setting Rx mode to %d", packet_filter_bitmap);
|
||||
|
||||
netif_stop_queue(net);
|
||||
|
||||
@ -850,10 +868,10 @@ static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)
|
||||
KAWETH_CONTROL_TIMEOUT);
|
||||
|
||||
if(result < 0) {
|
||||
kaweth_err("Failed to set Rx mode: %d", result);
|
||||
err("Failed to set Rx mode: %d", result);
|
||||
}
|
||||
else {
|
||||
kaweth_dbg("Set Rx mode to %d", packet_filter_bitmap);
|
||||
dbg("Set Rx mode to %d", packet_filter_bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -874,13 +892,49 @@ static void kaweth_tx_timeout(struct net_device *net)
|
||||
{
|
||||
struct kaweth_device *kaweth = netdev_priv(net);
|
||||
|
||||
kaweth_warn("%s: Tx timed out. Resetting.", net->name);
|
||||
warn("%s: Tx timed out. Resetting.", net->name);
|
||||
kaweth->stats.tx_errors++;
|
||||
net->trans_start = jiffies;
|
||||
|
||||
usb_unlink_urb(kaweth->tx_urb);
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* kaweth_suspend
|
||||
****************************************************************/
|
||||
static int kaweth_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct kaweth_device *kaweth = usb_get_intfdata(intf);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&kaweth->device_lock, flags);
|
||||
kaweth->status |= KAWETH_STATUS_SUSPENDING;
|
||||
spin_unlock_irqrestore(&kaweth->device_lock, flags);
|
||||
|
||||
kaweth_kill_urbs(kaweth);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* kaweth_resume
|
||||
****************************************************************/
|
||||
static int kaweth_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct kaweth_device *kaweth = usb_get_intfdata(intf);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&kaweth->device_lock, flags);
|
||||
kaweth->status &= ~KAWETH_STATUS_SUSPENDING;
|
||||
spin_unlock_irqrestore(&kaweth->device_lock, flags);
|
||||
|
||||
if (!kaweth->opened)
|
||||
return 0;
|
||||
kaweth_resubmit_rx_urb(kaweth, GFP_NOIO);
|
||||
kaweth_resubmit_int_urb(kaweth, GFP_NOIO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* kaweth_probe
|
||||
****************************************************************/
|
||||
@ -895,15 +949,15 @@ static int kaweth_probe(
|
||||
const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
int result = 0;
|
||||
|
||||
kaweth_dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x",
|
||||
dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x",
|
||||
dev->devnum,
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct),
|
||||
le16_to_cpu(dev->descriptor.bcdDevice));
|
||||
|
||||
kaweth_dbg("Device at %p", dev);
|
||||
dbg("Device at %p", dev);
|
||||
|
||||
kaweth_dbg("Descriptor length: %x type: %x",
|
||||
dbg("Descriptor length: %x type: %x",
|
||||
(int)dev->descriptor.bLength,
|
||||
(int)dev->descriptor.bDescriptorType);
|
||||
|
||||
@ -918,7 +972,7 @@ static int kaweth_probe(
|
||||
spin_lock_init(&kaweth->device_lock);
|
||||
init_waitqueue_head(&kaweth->term_wait);
|
||||
|
||||
kaweth_dbg("Resetting.");
|
||||
dbg("Resetting.");
|
||||
|
||||
kaweth_reset(kaweth);
|
||||
|
||||
@ -928,17 +982,17 @@ static int kaweth_probe(
|
||||
*/
|
||||
|
||||
if (le16_to_cpu(dev->descriptor.bcdDevice) >> 8) {
|
||||
kaweth_info("Firmware present in device.");
|
||||
info("Firmware present in device.");
|
||||
} else {
|
||||
/* Download the firmware */
|
||||
kaweth_info("Downloading firmware...");
|
||||
info("Downloading firmware...");
|
||||
kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL);
|
||||
if ((result = kaweth_download_firmware(kaweth,
|
||||
kaweth_new_code,
|
||||
len_kaweth_new_code,
|
||||
100,
|
||||
2)) < 0) {
|
||||
kaweth_err("Error downloading firmware (%d)", result);
|
||||
err("Error downloading firmware (%d)", result);
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
@ -947,7 +1001,7 @@ static int kaweth_probe(
|
||||
len_kaweth_new_code_fix,
|
||||
100,
|
||||
3)) < 0) {
|
||||
kaweth_err("Error downloading firmware fix (%d)", result);
|
||||
err("Error downloading firmware fix (%d)", result);
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
@ -956,7 +1010,7 @@ static int kaweth_probe(
|
||||
len_kaweth_trigger_code,
|
||||
126,
|
||||
2)) < 0) {
|
||||
kaweth_err("Error downloading trigger code (%d)", result);
|
||||
err("Error downloading trigger code (%d)", result);
|
||||
goto err_fw;
|
||||
|
||||
}
|
||||
@ -966,18 +1020,18 @@ static int kaweth_probe(
|
||||
len_kaweth_trigger_code_fix,
|
||||
126,
|
||||
3)) < 0) {
|
||||
kaweth_err("Error downloading trigger code fix (%d)", result);
|
||||
err("Error downloading trigger code fix (%d)", result);
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
|
||||
if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) {
|
||||
kaweth_err("Error triggering firmware (%d)", result);
|
||||
err("Error triggering firmware (%d)", result);
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
/* Device will now disappear for a moment... */
|
||||
kaweth_info("Firmware loaded. I'll be back...");
|
||||
info("Firmware loaded. I'll be back...");
|
||||
err_fw:
|
||||
free_page((unsigned long)kaweth->firmware_buf);
|
||||
free_netdev(netdev);
|
||||
@ -987,14 +1041,14 @@ err_fw:
|
||||
result = kaweth_read_configuration(kaweth);
|
||||
|
||||
if(result < 0) {
|
||||
kaweth_err("Error reading configuration (%d), no net device created", result);
|
||||
err("Error reading configuration (%d), no net device created", result);
|
||||
goto err_free_netdev;
|
||||
}
|
||||
|
||||
kaweth_info("Statistics collection: %x", kaweth->configuration.statistics_mask);
|
||||
kaweth_info("Multicast filter limit: %x", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1));
|
||||
kaweth_info("MTU: %d", le16_to_cpu(kaweth->configuration.segment_size));
|
||||
kaweth_info("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
|
||||
info("Statistics collection: %x", kaweth->configuration.statistics_mask);
|
||||
info("Multicast filter limit: %x", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1));
|
||||
info("MTU: %d", le16_to_cpu(kaweth->configuration.segment_size));
|
||||
info("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
|
||||
(int)kaweth->configuration.hw_addr[0],
|
||||
(int)kaweth->configuration.hw_addr[1],
|
||||
(int)kaweth->configuration.hw_addr[2],
|
||||
@ -1005,17 +1059,17 @@ err_fw:
|
||||
if(!memcmp(&kaweth->configuration.hw_addr,
|
||||
&bcast_addr,
|
||||
sizeof(bcast_addr))) {
|
||||
kaweth_err("Firmware not functioning properly, no net device created");
|
||||
err("Firmware not functioning properly, no net device created");
|
||||
goto err_free_netdev;
|
||||
}
|
||||
|
||||
if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) {
|
||||
kaweth_dbg("Error setting URB size");
|
||||
dbg("Error setting URB size");
|
||||
goto err_free_netdev;
|
||||
}
|
||||
|
||||
if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) {
|
||||
kaweth_err("Error setting SOFS wait");
|
||||
err("Error setting SOFS wait");
|
||||
goto err_free_netdev;
|
||||
}
|
||||
|
||||
@ -1025,11 +1079,11 @@ err_fw:
|
||||
KAWETH_PACKET_FILTER_MULTICAST);
|
||||
|
||||
if(result < 0) {
|
||||
kaweth_err("Error setting receive filter");
|
||||
err("Error setting receive filter");
|
||||
goto err_free_netdev;
|
||||
}
|
||||
|
||||
kaweth_dbg("Initializing net device.");
|
||||
dbg("Initializing net device.");
|
||||
|
||||
kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!kaweth->tx_urb)
|
||||
@ -1086,13 +1140,13 @@ err_fw:
|
||||
|
||||
SET_NETDEV_DEV(netdev, &intf->dev);
|
||||
if (register_netdev(netdev) != 0) {
|
||||
kaweth_err("Error registering netdev.");
|
||||
err("Error registering netdev.");
|
||||
goto err_intfdata;
|
||||
}
|
||||
|
||||
kaweth_info("kaweth interface created at %s", kaweth->net->name);
|
||||
info("kaweth interface created at %s", kaweth->net->name);
|
||||
|
||||
kaweth_dbg("Kaweth probe returning.");
|
||||
dbg("Kaweth probe returning.");
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1121,16 +1175,16 @@ static void kaweth_disconnect(struct usb_interface *intf)
|
||||
struct kaweth_device *kaweth = usb_get_intfdata(intf);
|
||||
struct net_device *netdev;
|
||||
|
||||
kaweth_info("Unregistering");
|
||||
info("Unregistering");
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (!kaweth) {
|
||||
kaweth_warn("unregistering non-existant device");
|
||||
warn("unregistering non-existant device");
|
||||
return;
|
||||
}
|
||||
netdev = kaweth->net;
|
||||
|
||||
kaweth_dbg("Unregistering net device");
|
||||
dbg("Unregistering net device");
|
||||
unregister_netdev(netdev);
|
||||
|
||||
usb_free_urb(kaweth->rx_urb);
|
||||
@ -1185,7 +1239,7 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
|
||||
|
||||
if (!wait_event_timeout(awd.wqh, awd.done, timeout)) {
|
||||
// timeout
|
||||
kaweth_warn("usb_control/bulk_msg: timeout");
|
||||
warn("usb_control/bulk_msg: timeout");
|
||||
usb_kill_urb(urb); // remove urb safely
|
||||
status = -ETIMEDOUT;
|
||||
}
|
||||
@ -1234,7 +1288,7 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,
|
||||
****************************************************************/
|
||||
static int __init kaweth_init(void)
|
||||
{
|
||||
kaweth_dbg("Driver loading");
|
||||
dbg("Driver loading");
|
||||
return usb_register(&kaweth_driver);
|
||||
}
|
||||
|
||||
|
534
drivers/usb/net/mcs7830.c
Normal file
534
drivers/usb/net/mcs7830.c
Normal file
@ -0,0 +1,534 @@
|
||||
/*
|
||||
* MosChips MCS7830 based USB 2.0 Ethernet Devices
|
||||
*
|
||||
* based on usbnet.c, asix.c and the vendor provided mcs7830 driver
|
||||
*
|
||||
* Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>
|
||||
* Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
|
||||
* Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
|
||||
* Copyright (c) 2002-2003 TiVo Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
/* requests */
|
||||
#define MCS7830_RD_BMREQ (USB_DIR_IN | USB_TYPE_VENDOR | \
|
||||
USB_RECIP_DEVICE)
|
||||
#define MCS7830_WR_BMREQ (USB_DIR_OUT | USB_TYPE_VENDOR | \
|
||||
USB_RECIP_DEVICE)
|
||||
#define MCS7830_RD_BREQ 0x0E
|
||||
#define MCS7830_WR_BREQ 0x0D
|
||||
|
||||
#define MCS7830_CTRL_TIMEOUT 1000
|
||||
#define MCS7830_MAX_MCAST 64
|
||||
|
||||
#define MCS7830_VENDOR_ID 0x9710
|
||||
#define MCS7830_PRODUCT_ID 0x7830
|
||||
|
||||
#define MCS7830_MII_ADVERTISE (ADVERTISE_PAUSE_CAP | ADVERTISE_100FULL | \
|
||||
ADVERTISE_100HALF | ADVERTISE_10FULL | \
|
||||
ADVERTISE_10HALF | ADVERTISE_CSMA)
|
||||
|
||||
/* HIF_REG_XX coressponding index value */
|
||||
enum {
|
||||
HIF_REG_MULTICAST_HASH = 0x00,
|
||||
HIF_REG_PACKET_GAP1 = 0x08,
|
||||
HIF_REG_PACKET_GAP2 = 0x09,
|
||||
HIF_REG_PHY_DATA = 0x0a,
|
||||
HIF_REG_PHY_CMD1 = 0x0c,
|
||||
HIF_REG_PHY_CMD1_READ = 0x40,
|
||||
HIF_REG_PHY_CMD1_WRITE = 0x20,
|
||||
HIF_REG_PHY_CMD1_PHYADDR = 0x01,
|
||||
HIF_REG_PHY_CMD2 = 0x0d,
|
||||
HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80,
|
||||
HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40,
|
||||
HIF_REG_CONFIG = 0x0e,
|
||||
HIF_REG_CONFIG_CFG = 0x80,
|
||||
HIF_REG_CONFIG_SPEED100 = 0x40,
|
||||
HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20,
|
||||
HIF_REG_CONFIG_RXENABLE = 0x10,
|
||||
HIF_REG_CONFIG_TXENABLE = 0x08,
|
||||
HIF_REG_CONFIG_SLEEPMODE = 0x04,
|
||||
HIF_REG_CONFIG_ALLMULTICAST = 0x02,
|
||||
HIF_REG_CONFIG_PROMISCIOUS = 0x01,
|
||||
HIF_REG_ETHERNET_ADDR = 0x0f,
|
||||
HIF_REG_22 = 0x15,
|
||||
HIF_REG_PAUSE_THRESHOLD = 0x16,
|
||||
HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0,
|
||||
};
|
||||
|
||||
struct mcs7830_data {
|
||||
u8 multi_filter[8];
|
||||
u8 config;
|
||||
};
|
||||
|
||||
static const char driver_name[] = "MOSCHIP usb-ethernet driver";
|
||||
|
||||
static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
|
||||
{
|
||||
struct usb_device *xdev = dev->udev;
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(xdev, usb_rcvctrlpipe(xdev, 0), MCS7830_RD_BREQ,
|
||||
MCS7830_RD_BMREQ, 0x0000, index, data,
|
||||
size, msecs_to_jiffies(MCS7830_CTRL_TIMEOUT));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data)
|
||||
{
|
||||
struct usb_device *xdev = dev->udev;
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(xdev, usb_sndctrlpipe(xdev, 0), MCS7830_WR_BREQ,
|
||||
MCS7830_WR_BMREQ, 0x0000, index, data,
|
||||
size, msecs_to_jiffies(MCS7830_CTRL_TIMEOUT));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mcs7830_async_cmd_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
|
||||
|
||||
if (urb->status < 0)
|
||||
printk(KERN_DEBUG "mcs7830_async_cmd_callback() failed with %d",
|
||||
urb->status);
|
||||
|
||||
kfree(req);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
int ret;
|
||||
struct urb *urb;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (!urb) {
|
||||
dev_dbg(&dev->udev->dev, "Error allocating URB "
|
||||
"in write_cmd_async!");
|
||||
return;
|
||||
}
|
||||
|
||||
req = kmalloc(sizeof *req, GFP_ATOMIC);
|
||||
if (!req) {
|
||||
dev_err(&dev->udev->dev, "Failed to allocate memory for "
|
||||
"control request");
|
||||
goto out;
|
||||
}
|
||||
req->bRequestType = MCS7830_WR_BMREQ;
|
||||
req->bRequest = MCS7830_WR_BREQ;
|
||||
req->wValue = 0;
|
||||
req->wIndex = cpu_to_le16(index);
|
||||
req->wLength = cpu_to_le16(size);
|
||||
|
||||
usb_fill_control_urb(urb, dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
(void *)req, data, size,
|
||||
mcs7830_async_cmd_callback, req);
|
||||
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->udev->dev, "Error submitting the control "
|
||||
"message: ret=%d", ret);
|
||||
goto out;
|
||||
}
|
||||
return;
|
||||
out:
|
||||
kfree(req);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
static int mcs7830_get_address(struct usbnet *dev)
|
||||
{
|
||||
int ret;
|
||||
ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN,
|
||||
dev->net->dev_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs7830_read_phy(struct usbnet *dev, u8 index)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
__le16 val;
|
||||
|
||||
u8 cmd[2] = {
|
||||
HIF_REG_PHY_CMD1_READ | HIF_REG_PHY_CMD1_PHYADDR,
|
||||
HIF_REG_PHY_CMD2_PEND_FLAG_BIT | index,
|
||||
};
|
||||
|
||||
mutex_lock(&dev->phy_mutex);
|
||||
/* write the MII command */
|
||||
ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* wait for the data to become valid, should be within < 1ms */
|
||||
for (i = 0; i < 10; i++) {
|
||||
ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
|
||||
if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
|
||||
break;
|
||||
ret = -EIO;
|
||||
msleep(1);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* read actual register contents */
|
||||
ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = le16_to_cpu(val);
|
||||
dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)\n",
|
||||
index, val, i);
|
||||
out:
|
||||
mutex_unlock(&dev->phy_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
__le16 le_val;
|
||||
|
||||
u8 cmd[2] = {
|
||||
HIF_REG_PHY_CMD1_WRITE | HIF_REG_PHY_CMD1_PHYADDR,
|
||||
HIF_REG_PHY_CMD2_PEND_FLAG_BIT | (index & 0x1F),
|
||||
};
|
||||
|
||||
mutex_lock(&dev->phy_mutex);
|
||||
|
||||
/* write the new register contents */
|
||||
le_val = cpu_to_le16(val);
|
||||
ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* write the MII command */
|
||||
ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* wait for the command to be accepted by the PHY */
|
||||
for (i = 0; i < 10; i++) {
|
||||
ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
|
||||
if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
|
||||
break;
|
||||
ret = -EIO;
|
||||
msleep(1);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
dev_dbg(&dev->udev->dev, "write PHY reg %02x: %04x (%d tries)\n",
|
||||
index, val, i);
|
||||
out:
|
||||
mutex_unlock(&dev->phy_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This algorithm comes from the original mcs7830 version 1.4 driver,
|
||||
* not sure if it is needed.
|
||||
*/
|
||||
static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode)
|
||||
{
|
||||
int ret;
|
||||
/* Enable all media types */
|
||||
ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE);
|
||||
|
||||
/* First reset BMCR */
|
||||
if (!ret)
|
||||
ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000);
|
||||
/* Enable Auto Neg */
|
||||
if (!ret)
|
||||
ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE);
|
||||
/* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */
|
||||
if (!ret)
|
||||
ret = mcs7830_write_phy(dev, MII_BMCR,
|
||||
BMCR_ANENABLE | BMCR_ANRESTART );
|
||||
return ret < 0 ? : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* if we can read register 22, the chip revision is C or higher
|
||||
*/
|
||||
static int mcs7830_get_rev(struct usbnet *dev)
|
||||
{
|
||||
u8 dummy[2];
|
||||
int ret;
|
||||
ret = mcs7830_get_reg(dev, HIF_REG_22, 2, dummy);
|
||||
if (ret > 0)
|
||||
return 2; /* Rev C or later */
|
||||
return 1; /* earlier revision */
|
||||
}
|
||||
|
||||
/*
|
||||
* On rev. C we need to set the pause threshold
|
||||
*/
|
||||
static void mcs7830_rev_C_fixup(struct usbnet *dev)
|
||||
{
|
||||
u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT;
|
||||
int retry;
|
||||
|
||||
for (retry = 0; retry < 2; retry++) {
|
||||
if (mcs7830_get_rev(dev) == 2) {
|
||||
dev_info(&dev->udev->dev, "applying rev.C fixup\n");
|
||||
mcs7830_set_reg(dev, HIF_REG_PAUSE_THRESHOLD,
|
||||
1, &pause_threshold);
|
||||
}
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int mcs7830_init_dev(struct usbnet *dev)
|
||||
{
|
||||
int ret;
|
||||
int retry;
|
||||
|
||||
/* Read MAC address from EEPROM */
|
||||
ret = -EINVAL;
|
||||
for (retry = 0; retry < 5 && ret; retry++)
|
||||
ret = mcs7830_get_address(dev);
|
||||
if (ret) {
|
||||
dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set up PHY */
|
||||
ret = mcs7830_set_autoneg(dev, 0);
|
||||
if (ret) {
|
||||
dev_info(&dev->udev->dev, "Cannot set autoneg\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
mcs7830_rev_C_fixup(dev);
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
|
||||
int location)
|
||||
{
|
||||
struct usbnet *dev = netdev->priv;
|
||||
return mcs7830_read_phy(dev, location);
|
||||
}
|
||||
|
||||
static void mcs7830_mdio_write(struct net_device *netdev, int phy_id,
|
||||
int location, int val)
|
||||
{
|
||||
struct usbnet *dev = netdev->priv;
|
||||
mcs7830_write_phy(dev, location, val);
|
||||
}
|
||||
|
||||
static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
|
||||
}
|
||||
|
||||
/* credits go to asix_set_multicast */
|
||||
static void mcs7830_set_multicast(struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
struct mcs7830_data *data = (struct mcs7830_data *)&dev->data;
|
||||
|
||||
data->config = HIF_REG_CONFIG_TXENABLE;
|
||||
|
||||
/* this should not be needed, but it doesn't work otherwise */
|
||||
data->config |= HIF_REG_CONFIG_ALLMULTICAST;
|
||||
|
||||
if (net->flags & IFF_PROMISC) {
|
||||
data->config |= HIF_REG_CONFIG_PROMISCIOUS;
|
||||
} else if (net->flags & IFF_ALLMULTI
|
||||
|| net->mc_count > MCS7830_MAX_MCAST) {
|
||||
data->config |= HIF_REG_CONFIG_ALLMULTICAST;
|
||||
} else if (net->mc_count == 0) {
|
||||
/* just broadcast and directed */
|
||||
} else {
|
||||
/* We use the 20 byte dev->data
|
||||
* for our 8 byte filter buffer
|
||||
* to avoid allocating memory that
|
||||
* is tricky to free later */
|
||||
struct dev_mc_list *mc_list = net->mc_list;
|
||||
u32 crc_bits;
|
||||
int i;
|
||||
|
||||
memset(data->multi_filter, 0, sizeof data->multi_filter);
|
||||
|
||||
/* Build the multicast hash filter. */
|
||||
for (i = 0; i < net->mc_count; i++) {
|
||||
crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
|
||||
data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7);
|
||||
mc_list = mc_list->next;
|
||||
}
|
||||
|
||||
mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH,
|
||||
sizeof data->multi_filter,
|
||||
data->multi_filter);
|
||||
}
|
||||
|
||||
mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config);
|
||||
}
|
||||
|
||||
static int mcs7830_get_regs_len(struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
switch (mcs7830_get_rev(dev)) {
|
||||
case 1:
|
||||
return 21;
|
||||
case 2:
|
||||
return 32;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
usbnet_get_drvinfo(net, drvinfo);
|
||||
drvinfo->regdump_len = mcs7830_get_regs_len(net);
|
||||
}
|
||||
|
||||
static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
regs->version = mcs7830_get_rev(dev);
|
||||
mcs7830_get_reg(dev, 0, regs->len, data);
|
||||
}
|
||||
|
||||
static struct ethtool_ops mcs7830_ethtool_ops = {
|
||||
.get_drvinfo = mcs7830_get_drvinfo,
|
||||
.get_regs_len = mcs7830_get_regs_len,
|
||||
.get_regs = mcs7830_get_regs,
|
||||
|
||||
/* common usbnet calls */
|
||||
.get_link = usbnet_get_link,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
.get_settings = usbnet_get_settings,
|
||||
.set_settings = usbnet_set_settings,
|
||||
.nway_reset = usbnet_nway_reset,
|
||||
};
|
||||
|
||||
static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev)
|
||||
{
|
||||
struct net_device *net = dev->net;
|
||||
int ret;
|
||||
|
||||
ret = mcs7830_init_dev(dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
net->do_ioctl = mcs7830_ioctl;
|
||||
net->ethtool_ops = &mcs7830_ethtool_ops;
|
||||
net->set_multicast_list = mcs7830_set_multicast;
|
||||
mcs7830_set_multicast(net);
|
||||
|
||||
/* reserve space for the status byte on rx */
|
||||
dev->rx_urb_size = ETH_FRAME_LEN + 1;
|
||||
|
||||
dev->mii.mdio_read = mcs7830_mdio_read;
|
||||
dev->mii.mdio_write = mcs7830_mdio_write;
|
||||
dev->mii.dev = net;
|
||||
dev->mii.phy_id_mask = 0x3f;
|
||||
dev->mii.reg_num_mask = 0x1f;
|
||||
dev->mii.phy_id = *((u8 *) net->dev_addr + 1);
|
||||
|
||||
ret = usbnet_get_endpoints(dev, udev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The chip always appends a status bytes that we need to strip */
|
||||
static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
if (skb->len == 0) {
|
||||
dev_err(&dev->udev->dev, "unexpected empty rx frame\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
skb_trim(skb, skb->len - 1);
|
||||
status = skb->data[skb->len];
|
||||
|
||||
if (status != 0x20)
|
||||
dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status);
|
||||
|
||||
return skb->len > 0;
|
||||
}
|
||||
|
||||
static const struct driver_info moschip_info = {
|
||||
.description = "MOSCHIP 7830 usb-NET adapter",
|
||||
.bind = mcs7830_bind,
|
||||
.rx_fixup = mcs7830_rx_fixup,
|
||||
.flags = FLAG_ETHER,
|
||||
.in = 1,
|
||||
.out = 2,
|
||||
};
|
||||
|
||||
static const struct usb_device_id products[] = {
|
||||
{
|
||||
USB_DEVICE(MCS7830_VENDOR_ID, MCS7830_PRODUCT_ID),
|
||||
.driver_info = (unsigned long) &moschip_info,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver mcs7830_driver = {
|
||||
.name = driver_name,
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
static int __init mcs7830_init(void)
|
||||
{
|
||||
return usb_register(&mcs7830_driver);
|
||||
}
|
||||
module_init(mcs7830_init);
|
||||
|
||||
static void __exit mcs7830_exit(void)
|
||||
{
|
||||
usb_deregister(&mcs7830_driver);
|
||||
}
|
||||
module_exit(mcs7830_exit);
|
||||
|
||||
MODULE_DESCRIPTION("USB to network adapter MCS7830)");
|
||||
MODULE_LICENSE("GPL");
|
@ -669,6 +669,37 @@ done:
|
||||
* they'll probably want to use this base set.
|
||||
*/
|
||||
|
||||
int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
if (!dev->mii.mdio_read)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return mii_ethtool_gset(&dev->mii, cmd);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_get_settings);
|
||||
|
||||
int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
int retval;
|
||||
|
||||
if (!dev->mii.mdio_write)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
retval = mii_ethtool_sset(&dev->mii, cmd);
|
||||
|
||||
/* link speed/duplex might have changed */
|
||||
if (dev->driver_info->link_reset)
|
||||
dev->driver_info->link_reset(dev);
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_set_settings);
|
||||
|
||||
|
||||
void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
@ -682,7 +713,7 @@ void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_get_drvinfo);
|
||||
|
||||
static u32 usbnet_get_link (struct net_device *net)
|
||||
u32 usbnet_get_link (struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
@ -690,9 +721,14 @@ static u32 usbnet_get_link (struct net_device *net)
|
||||
if (dev->driver_info->check_connect)
|
||||
return dev->driver_info->check_connect (dev) == 0;
|
||||
|
||||
/* if the device has mii operations, use those */
|
||||
if (dev->mii.mdio_read)
|
||||
return mii_link_ok(&dev->mii);
|
||||
|
||||
/* Otherwise, say we're up (to avoid breaking scripts) */
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_get_link);
|
||||
|
||||
u32 usbnet_get_msglevel (struct net_device *net)
|
||||
{
|
||||
@ -710,10 +746,24 @@ void usbnet_set_msglevel (struct net_device *net, u32 level)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_set_msglevel);
|
||||
|
||||
int usbnet_nway_reset(struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
if (!dev->mii.mdio_write)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return mii_nway_restart(&dev->mii);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_nway_reset);
|
||||
|
||||
/* drivers may override default ethtool_ops in their bind() routine */
|
||||
static struct ethtool_ops usbnet_ethtool_ops = {
|
||||
.get_settings = usbnet_get_settings,
|
||||
.set_settings = usbnet_set_settings,
|
||||
.get_drvinfo = usbnet_get_drvinfo,
|
||||
.get_link = usbnet_get_link,
|
||||
.nway_reset = usbnet_nway_reset,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
};
|
||||
@ -1094,6 +1144,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
|
||||
dev->delay.function = usbnet_bh;
|
||||
dev->delay.data = (unsigned long) dev;
|
||||
init_timer (&dev->delay);
|
||||
mutex_init (&dev->phy_mutex);
|
||||
|
||||
SET_MODULE_OWNER (net);
|
||||
dev->net = net;
|
||||
@ -1225,7 +1276,7 @@ EXPORT_SYMBOL_GPL(usbnet_resume);
|
||||
static int __init usbnet_init(void)
|
||||
{
|
||||
/* compiler should optimize this out */
|
||||
BUG_ON (sizeof (((struct sk_buff *)0)->cb)
|
||||
BUILD_BUG_ON (sizeof (((struct sk_buff *)0)->cb)
|
||||
< sizeof (struct skb_data));
|
||||
|
||||
random_ether_addr(node_id);
|
||||
|
@ -30,6 +30,7 @@ struct usbnet {
|
||||
struct usb_device *udev;
|
||||
struct driver_info *driver_info;
|
||||
wait_queue_head_t *wait;
|
||||
struct mutex phy_mutex;
|
||||
|
||||
/* i/o info: pipes etc */
|
||||
unsigned in, out;
|
||||
@ -168,9 +169,13 @@ extern void usbnet_defer_kevent (struct usbnet *, int);
|
||||
extern void usbnet_skb_return (struct usbnet *, struct sk_buff *);
|
||||
extern void usbnet_unlink_rx_urbs(struct usbnet *);
|
||||
|
||||
extern int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd);
|
||||
extern int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd);
|
||||
extern u32 usbnet_get_link (struct net_device *net);
|
||||
extern u32 usbnet_get_msglevel (struct net_device *);
|
||||
extern void usbnet_set_msglevel (struct net_device *, u32);
|
||||
extern void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
|
||||
extern int usbnet_nway_reset(struct net_device *net);
|
||||
|
||||
/* messaging support includes the interface name, so it must not be
|
||||
* used before it has one ... notably, in minidriver bind() calls.
|
||||
|
@ -422,6 +422,16 @@ config USB_SERIAL_MCT_U232
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mct_u232.
|
||||
|
||||
config USB_SERIAL_MOS7720
|
||||
tristate "USB Moschip 7720 Single Port Serial Driver"
|
||||
depends on USB_SERIAL
|
||||
---help---
|
||||
Say Y here if you want to use a USB Serial single port adapter from
|
||||
Moschip Semiconductor Tech.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mos7720.
|
||||
|
||||
config USB_SERIAL_MOS7840
|
||||
tristate "USB Moschip 7840/7820 USB Serial Driver"
|
||||
depends on USB_SERIAL
|
||||
@ -527,8 +537,7 @@ config USB_SERIAL_OPTION
|
||||
The USB bus on these cards is not accessible externally.
|
||||
|
||||
Supported devices include (some of?) those made by:
|
||||
Option, Huawei, Audiovox, Sierra Wireless, Novatel Wireless, or
|
||||
Anydata.
|
||||
Option, Huawei, Audiovox, Novatel Wireless, or Anydata.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called option.
|
||||
|
@ -34,6 +34,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
|
||||
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
|
||||
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
|
||||
obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o
|
||||
obj-$(CONFIG_USB_SERIAL_MOS7720) += mos7720.o
|
||||
obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
|
||||
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
|
||||
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
|
||||
|
@ -18,12 +18,8 @@
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
|
||||
{ USB_DEVICE(0x0f3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
|
||||
{ USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
|
||||
{ USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
|
||||
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless Aircard 580 */
|
||||
{ USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
|
||||
{ USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
|
||||
{ USB_DEVICE(0x1410, 0x1100) }, /* ExpressCard34 Qualcomm 3G CDMA */
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
@ -133,6 +129,7 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp)
|
||||
}
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb) {
|
||||
kfree(buffer);
|
||||
dev_err(&port->dev, "%s - no more urbs?\n",
|
||||
__FUNCTION__);
|
||||
result = -ENOMEM;
|
||||
|
@ -65,6 +65,7 @@ static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
|
||||
{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
|
||||
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
|
||||
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
|
||||
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
|
||||
{ } /* Terminating Entry */
|
||||
};
|
||||
|
@ -1,16 +1,16 @@
|
||||
/*
|
||||
* USB FTDI SIO driver
|
||||
*
|
||||
* Copyright (C) 1999 - 2001
|
||||
* Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Copyright (C) 1999 - 2001
|
||||
* Greg Kroah-Hartman (greg@kroah.com)
|
||||
* Bill Ryder (bryder@sgi.com)
|
||||
* Copyright (C) 2002
|
||||
* Kuba Ober (kuba@mareimbrium.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* See Documentation/usb/usb-serial.txt for more information on using this driver
|
||||
*
|
||||
@ -32,7 +32,7 @@
|
||||
* Changed full name of USB-UIRT device to avoid "/" character.
|
||||
* Added FTDI's alternate PID (0x6006) for FT232/245 devices.
|
||||
* Added PID for "ELV USB Module UO100" from Stefan Frings.
|
||||
*
|
||||
*
|
||||
* (21/Oct/2003) Ian Abbott
|
||||
* Renamed some VID/PID macros for Matrix Orbital and Perle Systems
|
||||
* devices. Removed Matrix Orbital and Perle Systems devices from the
|
||||
@ -69,7 +69,7 @@
|
||||
* does not incure any measurable overhead. This also relies on the fact
|
||||
* that we have proper reference counting logic for urbs. I nicked this
|
||||
* from Greg KH's Visor driver.
|
||||
*
|
||||
*
|
||||
* (23/Jun/2003) Ian Abbott
|
||||
* Reduced flip buffer pushes and corrected a data length test in
|
||||
* ftdi_read_bulk_callback.
|
||||
@ -77,7 +77,7 @@
|
||||
*
|
||||
* (21/Jun/2003) Erik Nygren
|
||||
* Added support for Home Electronics Tira-1 IR transceiver using FT232BM chip.
|
||||
* See <http://www.home-electro.com/tira1.htm>. Only operates properly
|
||||
* See <http://www.home-electro.com/tira1.htm>. Only operates properly
|
||||
* at 100000 and RTS-CTS, so set custom divisor mode on startup.
|
||||
* Also force the Tira-1 and USB-UIRT to only use their custom baud rates.
|
||||
*
|
||||
@ -137,17 +137,17 @@
|
||||
* (17/Feb/2003) Bill Ryder
|
||||
* Added write urb buffer pool on a per device basis
|
||||
* Added more checking for open file on callbacks (fixed OOPS)
|
||||
* Added CrystalFontz 632 and 634 PIDs
|
||||
* Added CrystalFontz 632 and 634 PIDs
|
||||
* (thanx to CrystalFontz for the sample devices - they flushed out
|
||||
* some driver bugs)
|
||||
* Minor debugging message changes
|
||||
* Added throttle, unthrottle and chars_in_buffer functions
|
||||
* Fixed FTDI_SIO (the original device) bug
|
||||
* Fixed some shutdown handling
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* (07/Jun/2002) Kuba Ober
|
||||
* Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor
|
||||
* function. It was getting too complex.
|
||||
@ -158,7 +158,7 @@
|
||||
*
|
||||
* (25/Jul/2002) Bill Ryder inserted Dmitri's TIOCMIWAIT patch
|
||||
* Not tested by me but it doesn't break anything I use.
|
||||
*
|
||||
*
|
||||
* (04/Jan/2002) Kuba Ober
|
||||
* Implemented 38400 baudrate kludge, where it can be substituted with other
|
||||
* values. That's the only way to set custom baudrates.
|
||||
@ -179,7 +179,7 @@
|
||||
* (the previous version caused panics)
|
||||
* Removed port iteration code since the device only has one I/O port and it
|
||||
* was wrong anyway.
|
||||
*
|
||||
*
|
||||
* (31/May/2001) gkh
|
||||
* Switched from using spinlock to a semaphore, which fixes lots of problems.
|
||||
*
|
||||
@ -188,16 +188,16 @@
|
||||
* Cleaned up comments for 8U232
|
||||
* Added parity, framing and overrun error handling
|
||||
* Added receive break handling.
|
||||
*
|
||||
*
|
||||
* (04/08/2001) gb
|
||||
* Identify version on module load.
|
||||
*
|
||||
*
|
||||
* (18/March/2001) Bill Ryder
|
||||
* (Not released)
|
||||
* Added send break handling. (requires kernel patch too)
|
||||
* Fixed 8U232AM hardware RTS/CTS etc status reporting.
|
||||
* Added flipbuf fix copied from generic device
|
||||
*
|
||||
*
|
||||
* (12/3/2000) Bill Ryder
|
||||
* Added support for 8U232AM device.
|
||||
* Moved PID and VIDs into header file only.
|
||||
@ -211,14 +211,14 @@
|
||||
* Cleaned up comments. Removed multiple PID/VID definitions.
|
||||
* Factorised cts/dtr code
|
||||
* Made use of __FUNCTION__ in dbg's
|
||||
*
|
||||
*
|
||||
* (11/01/2000) Adam J. Richter
|
||||
* usb_device_id table support
|
||||
*
|
||||
*
|
||||
* (10/05/2000) gkh
|
||||
* Fixed bug with urb->dev not being set properly, now that the usb
|
||||
* core needs it.
|
||||
*
|
||||
*
|
||||
* (09/11/2000) gkh
|
||||
* Removed DEBUG #ifdefs with call to usb_serial_debug_data
|
||||
*
|
||||
@ -226,11 +226,11 @@
|
||||
* Added module_init and module_exit functions to handle the fact that this
|
||||
* driver is a loadable module now.
|
||||
*
|
||||
* (04/04/2000) Bill Ryder
|
||||
* (04/04/2000) Bill Ryder
|
||||
* Fixed bugs in TCGET/TCSET ioctls (by removing them - they are
|
||||
* handled elsewhere in the tty io driver chain).
|
||||
*
|
||||
* (03/30/2000) Bill Ryder
|
||||
* (03/30/2000) Bill Ryder
|
||||
* Implemented lots of ioctls
|
||||
* Fixed a race condition in write
|
||||
* Changed some dbg's to errs
|
||||
@ -444,13 +444,13 @@ static struct usb_device_id id_table_combined [] = {
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, */
|
||||
{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_1_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_1_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) },
|
||||
{ USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
|
||||
{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
|
||||
@ -522,7 +522,7 @@ static struct usb_driver ftdi_driver = {
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table_combined,
|
||||
.no_dynamic_id = 1,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
static const char *ftdi_chip_name[] = {
|
||||
@ -548,13 +548,13 @@ struct ftdi_private {
|
||||
int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
|
||||
__u16 last_set_data_urb_value ;
|
||||
/* the last data state set - needed for doing a break */
|
||||
int write_offset; /* This is the offset in the usb data block to write the serial data -
|
||||
int write_offset; /* This is the offset in the usb data block to write the serial data -
|
||||
* it is different between devices
|
||||
*/
|
||||
int flags; /* some ASYNC_xxxx flags are supported */
|
||||
unsigned long last_dtr_rts; /* saved modem control outputs */
|
||||
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
|
||||
char prev_status, diff_status; /* Used for TIOCMIWAIT */
|
||||
char prev_status, diff_status; /* Used for TIOCMIWAIT */
|
||||
__u8 rx_flags; /* receive state flags (throttling) */
|
||||
spinlock_t rx_lock; /* spinlock for receive state */
|
||||
struct work_struct rx_work;
|
||||
@ -721,7 +721,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, unsigned
|
||||
urb_value |= FTDI_SIO_SET_RTS_HIGH;
|
||||
rv = usb_control_msg(port->serial->dev,
|
||||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_MODEM_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_MODEM_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
|
||||
urb_value, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT);
|
||||
@ -768,7 +768,7 @@ static int change_speed(struct usb_serial_port *port)
|
||||
if (priv->interface) { /* FT2232C */
|
||||
urb_index = (__u16)((urb_index << 8) | priv->interface);
|
||||
}
|
||||
|
||||
|
||||
rv = usb_control_msg(port->serial->dev,
|
||||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_BAUDRATE_REQUEST,
|
||||
@ -827,7 +827,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port)
|
||||
|
||||
/* 3. Convert baudrate to device-specific divisor */
|
||||
|
||||
if (!baud) baud = 9600;
|
||||
if (!baud) baud = 9600;
|
||||
switch(priv->chip_type) {
|
||||
case SIO: /* SIO chip */
|
||||
switch(baud) {
|
||||
@ -843,7 +843,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port)
|
||||
case 115200: div_value = ftdi_sio_b115200; break;
|
||||
} /* baud */
|
||||
if (div_value == 0) {
|
||||
dbg("%s - Baudrate (%d) requested is not supported", __FUNCTION__, baud);
|
||||
dbg("%s - Baudrate (%d) requested is not supported", __FUNCTION__, baud);
|
||||
div_value = ftdi_sio_b9600;
|
||||
div_okay = 0;
|
||||
}
|
||||
@ -925,7 +925,7 @@ static int set_serial_info(struct usb_serial_port * port, struct serial_struct _
|
||||
/* Make the changes - these are privileged changes! */
|
||||
|
||||
priv->flags = ((priv->flags & ~ASYNC_FLAGS) |
|
||||
(new_serial.flags & ASYNC_FLAGS));
|
||||
(new_serial.flags & ASYNC_FLAGS));
|
||||
priv->custom_divisor = new_serial.custom_divisor;
|
||||
|
||||
port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
|
||||
@ -950,7 +950,7 @@ check_and_exit:
|
||||
(old_priv.custom_divisor != priv->custom_divisor))) {
|
||||
change_speed(port);
|
||||
}
|
||||
|
||||
|
||||
return (0);
|
||||
|
||||
} /* set_serial_info */
|
||||
@ -1022,18 +1022,18 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a
|
||||
struct usb_device *udev;
|
||||
unsigned short latency = 0;
|
||||
int rv = 0;
|
||||
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
|
||||
dbg("%s",__FUNCTION__);
|
||||
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
0, priv->interface,
|
||||
(char*) &latency, 1, WDR_TIMEOUT);
|
||||
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "Unable to read latency timer: %i", rv);
|
||||
return -EIO;
|
||||
@ -1051,23 +1051,23 @@ static ssize_t store_latency_timer(struct device *dev, struct device_attribute *
|
||||
char buf[1];
|
||||
int v = simple_strtoul(valbuf, NULL, 10);
|
||||
int rv = 0;
|
||||
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
|
||||
dbg("%s: setting latency timer = %i", __FUNCTION__, v);
|
||||
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
v, priv->interface,
|
||||
v, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT);
|
||||
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "Unable to write latency timer: %i", rv);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1082,23 +1082,23 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
|
||||
char buf[1];
|
||||
int v = simple_strtoul(valbuf, NULL, 10);
|
||||
int rv = 0;
|
||||
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
|
||||
dbg("%s: setting event char = %i", __FUNCTION__, v);
|
||||
|
||||
|
||||
rv = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
FTDI_SIO_SET_EVENT_CHAR_REQUEST,
|
||||
FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
|
||||
v, priv->interface,
|
||||
v, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT);
|
||||
|
||||
|
||||
if (rv < 0) {
|
||||
dbg("Unable to write event character: %i", rv);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1135,11 +1135,11 @@ static void remove_sysfs_attrs(struct usb_serial *serial)
|
||||
struct ftdi_private *priv;
|
||||
struct usb_device *udev;
|
||||
|
||||
dbg("%s",__FUNCTION__);
|
||||
dbg("%s",__FUNCTION__);
|
||||
|
||||
priv = usb_get_serial_port_data(serial->port[0]);
|
||||
udev = serial->dev;
|
||||
|
||||
|
||||
/* XXX see create_sysfs_attrs */
|
||||
if (priv->chip_type != SIO) {
|
||||
device_remove_file(&udev->dev, &dev_attr_event_char);
|
||||
@ -1147,7 +1147,7 @@ static void remove_sysfs_attrs(struct usb_serial *serial)
|
||||
device_remove_file(&udev->dev, &dev_attr_latency_timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1258,7 +1258,7 @@ static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
|
||||
} /* ftdi_HE_TIRA1_setup */
|
||||
|
||||
|
||||
/* ftdi_shutdown is called from usbserial:usb_serial_disconnect
|
||||
/* ftdi_shutdown is called from usbserial:usb_serial_disconnect
|
||||
* it is called when the usb device is disconnected
|
||||
*
|
||||
* usbserial:usb_serial_disconnect
|
||||
@ -1269,16 +1269,16 @@ static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
|
||||
|
||||
static void ftdi_shutdown (struct usb_serial *serial)
|
||||
{ /* ftdi_shutdown */
|
||||
|
||||
|
||||
struct usb_serial_port *port = serial->port[0];
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
remove_sysfs_attrs(serial);
|
||||
|
||||
/* all open ports are closed at this point
|
||||
* (by usbserial.c:__serial_close, which calls ftdi_close)
|
||||
|
||||
/* all open ports are closed at this point
|
||||
* (by usbserial.c:__serial_close, which calls ftdi_close)
|
||||
*/
|
||||
|
||||
if (priv) {
|
||||
@ -1293,7 +1293,7 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp)
|
||||
struct usb_device *dev = port->serial->dev;
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
|
||||
|
||||
int result = 0;
|
||||
char buf[1]; /* Needed for the usb_control_msg I think */
|
||||
|
||||
@ -1312,8 +1312,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp)
|
||||
/* No error checking for this (will get errors later anyway) */
|
||||
/* See ftdi_sio.h for description of what is reset */
|
||||
usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
|
||||
FTDI_SIO_RESET_SIO,
|
||||
FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
|
||||
FTDI_SIO_RESET_SIO,
|
||||
priv->interface, buf, 0, WDR_TIMEOUT);
|
||||
|
||||
/* Termios defaults are set by usb_serial_init. We don't change
|
||||
@ -1350,12 +1350,12 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* usbserial:__serial_close only calls ftdi_close if the point is open
|
||||
*
|
||||
* This only gets called when it is the last close
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
static void ftdi_close (struct usb_serial_port *port, struct file *filp)
|
||||
@ -1368,14 +1368,14 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp)
|
||||
|
||||
if (c_cflag & HUPCL){
|
||||
/* Disable flow control */
|
||||
if (usb_control_msg(port->serial->dev,
|
||||
if (usb_control_msg(port->serial->dev,
|
||||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface, buf, 0,
|
||||
WDR_TIMEOUT) < 0) {
|
||||
err("error from flowcontrol urb");
|
||||
}
|
||||
}
|
||||
|
||||
/* drop RTS and DTR */
|
||||
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||
@ -1384,14 +1384,14 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp)
|
||||
/* cancel any scheduled reading */
|
||||
cancel_delayed_work(&priv->rx_work);
|
||||
flush_scheduled_work();
|
||||
|
||||
|
||||
/* shutdown our bulk read */
|
||||
if (port->read_urb)
|
||||
usb_kill_urb(port->read_urb);
|
||||
} /* ftdi_close */
|
||||
|
||||
|
||||
|
||||
|
||||
/* The SIO requires the first byte to have:
|
||||
* B0 1
|
||||
* B1 0
|
||||
@ -1423,7 +1423,7 @@ static int ftdi_write (struct usb_serial_port *port,
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->tx_lock, flags);
|
||||
|
||||
|
||||
data_offset = priv->write_offset;
|
||||
dbg("data_offset set to %d",data_offset);
|
||||
|
||||
@ -1462,7 +1462,7 @@ static int ftdi_write (struct usb_serial_port *port,
|
||||
user_pktsz = todo;
|
||||
}
|
||||
/* Write the control byte at the front of the packet*/
|
||||
*first_byte = 1 | ((user_pktsz) << 2);
|
||||
*first_byte = 1 | ((user_pktsz) << 2);
|
||||
/* Copy data for packet */
|
||||
memcpy (first_byte + data_offset,
|
||||
current_position, user_pktsz);
|
||||
@ -1479,7 +1479,7 @@ static int ftdi_write (struct usb_serial_port *port,
|
||||
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, buffer);
|
||||
|
||||
/* fill the buffer and send it */
|
||||
usb_fill_bulk_urb(urb, port->serial->dev,
|
||||
usb_fill_bulk_urb(urb, port->serial->dev,
|
||||
usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
|
||||
buffer, transfer_size,
|
||||
ftdi_write_bulk_callback, port);
|
||||
@ -1520,7 +1520,7 @@ static void ftdi_write_bulk_callback (struct urb *urb)
|
||||
kfree (urb->transfer_buffer);
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
||||
if (urb->status) {
|
||||
dbg("nonzero write bulk status received: %d", urb->status);
|
||||
return;
|
||||
@ -1651,7 +1651,7 @@ static void ftdi_process_read (void *param)
|
||||
struct tty_struct *tty;
|
||||
struct ftdi_private *priv;
|
||||
char error_flag;
|
||||
unsigned char *data;
|
||||
unsigned char *data;
|
||||
|
||||
int i;
|
||||
int result;
|
||||
@ -1759,7 +1759,7 @@ static void ftdi_process_read (void *param)
|
||||
}
|
||||
if (length > 0) {
|
||||
for (i = 2; i < length+2; i++) {
|
||||
/* Note that the error flag is duplicated for
|
||||
/* Note that the error flag is duplicated for
|
||||
every character received since we don't know
|
||||
which character it applied to */
|
||||
tty_insert_flip_char(tty, data[packet_offset+i], error_flag);
|
||||
@ -1773,7 +1773,7 @@ static void ftdi_process_read (void *param)
|
||||
This doesn't work well since the application receives a never
|
||||
ending stream of bad data - even though new data hasn't been sent.
|
||||
Therefore I (bill) have taken this out.
|
||||
However - this might make sense for framing errors and so on
|
||||
However - this might make sense for framing errors and so on
|
||||
so I am leaving the code in for now.
|
||||
*/
|
||||
else {
|
||||
@ -1827,7 +1827,7 @@ static void ftdi_process_read (void *param)
|
||||
/* if the port is closed stop trying to read */
|
||||
if (port->open_count > 0){
|
||||
/* Continue trying to always read */
|
||||
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
|
||||
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
|
||||
usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
|
||||
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
|
||||
ftdi_read_bulk_callback, port);
|
||||
@ -1844,9 +1844,9 @@ static void ftdi_process_read (void *param)
|
||||
static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
__u16 urb_value = 0;
|
||||
__u16 urb_value = 0;
|
||||
char buf[1];
|
||||
|
||||
|
||||
/* break_state = -1 to turn on break, and 0 to turn off break */
|
||||
/* see drivers/char/tty_io.c to see it used */
|
||||
/* last_set_data_urb_value NEVER has the break bit set in it */
|
||||
@ -1854,20 +1854,20 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
|
||||
if (break_state) {
|
||||
urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
|
||||
} else {
|
||||
urb_value = priv->last_set_data_urb_value;
|
||||
urb_value = priv->last_set_data_urb_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_DATA_REQUEST,
|
||||
FTDI_SIO_SET_DATA_REQUEST,
|
||||
FTDI_SIO_SET_DATA_REQUEST_TYPE,
|
||||
urb_value , priv->interface,
|
||||
buf, 0, WDR_TIMEOUT) < 0) {
|
||||
err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state);
|
||||
}
|
||||
}
|
||||
|
||||
dbg("%s break state is %d - urb is %d", __FUNCTION__,break_state, urb_value);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1883,12 +1883,12 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
__u16 urb_value; /* will hold the new flags */
|
||||
char buf[1]; /* Perhaps I should dynamically alloc this? */
|
||||
|
||||
|
||||
// Added for xon/xoff support
|
||||
unsigned int iflag = port->tty->termios->c_iflag;
|
||||
unsigned char vstop;
|
||||
unsigned char vstart;
|
||||
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
/* Force baud rate if this device requires it, unless it is set to B0. */
|
||||
@ -1906,20 +1906,20 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
|
||||
|
||||
cflag = port->tty->termios->c_cflag;
|
||||
|
||||
/* FIXME -For this cut I don't care if the line is really changing or
|
||||
not - so just do the change regardless - should be able to
|
||||
/* FIXME -For this cut I don't care if the line is really changing or
|
||||
not - so just do the change regardless - should be able to
|
||||
compare old_termios and tty->termios */
|
||||
/* NOTE These routines can get interrupted by
|
||||
ftdi_sio_read_bulk_callback - need to examine what this
|
||||
/* NOTE These routines can get interrupted by
|
||||
ftdi_sio_read_bulk_callback - need to examine what this
|
||||
means - don't see any problems yet */
|
||||
|
||||
|
||||
/* Set number of data bits, parity, stop bits */
|
||||
|
||||
|
||||
urb_value = 0;
|
||||
urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
|
||||
FTDI_SIO_SET_DATA_STOP_BITS_1);
|
||||
urb_value |= (cflag & PARENB ?
|
||||
(cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD :
|
||||
urb_value |= (cflag & PARENB ?
|
||||
(cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD :
|
||||
FTDI_SIO_SET_DATA_PARITY_EVEN) :
|
||||
FTDI_SIO_SET_DATA_PARITY_NONE);
|
||||
if (cflag & CSIZE) {
|
||||
@ -1936,25 +1936,25 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
|
||||
/* This is needed by the break command since it uses the same command - but is
|
||||
* or'ed with this value */
|
||||
priv->last_set_data_urb_value = urb_value;
|
||||
|
||||
|
||||
if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_DATA_REQUEST,
|
||||
FTDI_SIO_SET_DATA_REQUEST,
|
||||
FTDI_SIO_SET_DATA_REQUEST_TYPE,
|
||||
urb_value , priv->interface,
|
||||
buf, 0, WDR_SHORT_TIMEOUT) < 0) {
|
||||
err("%s FAILED to set databits/stopbits/parity", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now do the baudrate */
|
||||
if ((cflag & CBAUD) == B0 ) {
|
||||
/* Disable flow control */
|
||||
if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
0, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT) < 0) {
|
||||
err("%s error from disable flowcontrol urb", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
/* Drop RTS and DTR */
|
||||
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||
} else {
|
||||
@ -1972,16 +1972,16 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
|
||||
/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
|
||||
if (cflag & CRTSCTS) {
|
||||
dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
|
||||
if (usb_control_msg(dev,
|
||||
if (usb_control_msg(dev,
|
||||
usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
|
||||
buf, 0, WDR_TIMEOUT) < 0) {
|
||||
err("urb failed to set to rts/cts flow control");
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Xon/Xoff code
|
||||
*
|
||||
@ -2011,16 +2011,16 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
|
||||
/* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
|
||||
/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
|
||||
dbg("%s Turning off hardware flow control", __FUNCTION__);
|
||||
if (usb_control_msg(dev,
|
||||
if (usb_control_msg(dev,
|
||||
usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
0, priv->interface,
|
||||
buf, 0, WDR_TIMEOUT) < 0) {
|
||||
err("urb failed to clear flow control");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return;
|
||||
} /* ftdi_termios */
|
||||
@ -2036,11 +2036,11 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
|
||||
switch (priv->chip_type) {
|
||||
case SIO:
|
||||
/* Request the status from the device */
|
||||
if ((ret = usb_control_msg(port->serial->dev,
|
||||
if ((ret = usb_control_msg(port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
|
||||
0, 0,
|
||||
0, 0,
|
||||
buf, 1, WDR_TIMEOUT)) < 0 ) {
|
||||
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
|
||||
ret);
|
||||
@ -2052,11 +2052,11 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
|
||||
case FT2232C:
|
||||
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
|
||||
format as the data returned from the in point */
|
||||
if ((ret = usb_control_msg(port->serial->dev,
|
||||
if ((ret = usb_control_msg(port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
0, priv->interface,
|
||||
buf, 2, WDR_TIMEOUT)) < 0 ) {
|
||||
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
|
||||
ret);
|
||||
@ -2067,12 +2067,12 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
|
||||
(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
|
||||
(buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) |
|
||||
(buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0) |
|
||||
priv->last_dtr_rts;
|
||||
priv->last_dtr_rts;
|
||||
}
|
||||
|
||||
static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear)
|
||||
@ -2138,11 +2138,11 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* This is not necessarily an error - turns out the higher layers will do
|
||||
/* This is not necessarily an error - turns out the higher layers will do
|
||||
* some ioctls itself (see comment above)
|
||||
*/
|
||||
dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __FUNCTION__, cmd);
|
||||
@ -2199,7 +2199,7 @@ static int __init ftdi_init (void)
|
||||
if (retval)
|
||||
goto failed_sio_register;
|
||||
retval = usb_register(&ftdi_driver);
|
||||
if (retval)
|
||||
if (retval)
|
||||
goto failed_usb_register;
|
||||
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
|
1683
drivers/usb/serial/mos7720.c
Normal file
1683
drivers/usb/serial/mos7720.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -2413,11 +2413,12 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
}
|
||||
|
||||
mos7840_port = mos7840_get_port_private(port);
|
||||
tty = mos7840_port->port->tty;
|
||||
|
||||
if (mos7840_port == NULL)
|
||||
return -1;
|
||||
|
||||
tty = mos7840_port->port->tty;
|
||||
|
||||
dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -1,21 +1,35 @@
|
||||
/*
|
||||
* Sierra Wireless CDMA Wireless Serial USB driver
|
||||
*
|
||||
* Current Copy modified by: Kevin Lloyd <linux@sierrawireless.com>
|
||||
* Original Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
USB Driver for Sierra Wireless
|
||||
|
||||
Copyright (C) 2006 Kevin Lloyd <linux@sierrawireless.com>
|
||||
|
||||
IMPORTANT DISCLAIMER: This driver is not commercially supported by
|
||||
Sierra Wireless. Use at your own risk.
|
||||
|
||||
This driver is free software; you can redistribute it and/or modify
|
||||
it under the terms of Version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de>
|
||||
Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
|
||||
|
||||
History:
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "v.1.0.5"
|
||||
#define DRIVER_AUTHOR "Kevin Lloyd <linux@sierrawireless.com>"
|
||||
#define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
|
||||
|
||||
static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
|
||||
{ USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */
|
||||
@ -23,53 +37,674 @@ static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */
|
||||
{ USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */
|
||||
{ USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */
|
||||
{ USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 for Europe */
|
||||
{ USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 */
|
||||
{ USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */
|
||||
/* Following devices are supported in the airprime.c driver */
|
||||
/* { USB_DEVICE(0x1199, 0x0112) }, */ /* Sierra Wireless AirCard 580 */
|
||||
/* { USB_DEVICE(0x0F3D, 0x0112) }, */ /* AirPrime/Sierra PC 5220 */
|
||||
|
||||
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
|
||||
{ USB_DEVICE(0x0F3D, 0x0112) }, /* AirPrime/Sierra PC 5220 */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static struct usb_device_id id_table_1port [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
|
||||
{ USB_DEVICE(0x0F3D, 0x0112) }, /* AirPrime/Sierra PC 5220 */
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_device_id id_table_3port [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
|
||||
{ USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */
|
||||
{ USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
|
||||
{ USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */
|
||||
{ USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */
|
||||
{ USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */
|
||||
{ USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 */
|
||||
{ USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct usb_driver sierra_driver = {
|
||||
.name = "sierra_wireless",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
.name = "sierra",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver sierra_device = {
|
||||
|
||||
static int debug;
|
||||
|
||||
/* per port private data */
|
||||
#define N_IN_URB 4
|
||||
#define N_OUT_URB 1
|
||||
#define IN_BUFLEN 4096
|
||||
#define OUT_BUFLEN 128
|
||||
|
||||
struct sierra_port_private {
|
||||
/* Input endpoints and buffer for this port */
|
||||
struct urb *in_urbs[N_IN_URB];
|
||||
char in_buffer[N_IN_URB][IN_BUFLEN];
|
||||
/* Output endpoints and buffer for this port */
|
||||
struct urb *out_urbs[N_OUT_URB];
|
||||
char out_buffer[N_OUT_URB][OUT_BUFLEN];
|
||||
|
||||
/* Settings for the port */
|
||||
int rts_state; /* Handshaking pins (outputs) */
|
||||
int dtr_state;
|
||||
int cts_state; /* Handshaking pins (inputs) */
|
||||
int dsr_state;
|
||||
int dcd_state;
|
||||
int ri_state;
|
||||
|
||||
unsigned long tx_start_time[N_OUT_URB];
|
||||
};
|
||||
|
||||
static int sierra_send_setup(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
if (port->tty) {
|
||||
int val = 0;
|
||||
if (portdata->dtr_state)
|
||||
val |= 0x01;
|
||||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sierra_rx_throttle(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s", __FUNCTION__);
|
||||
}
|
||||
|
||||
static void sierra_rx_unthrottle(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s", __FUNCTION__);
|
||||
}
|
||||
|
||||
static void sierra_break_ctl(struct usb_serial_port *port, int break_state)
|
||||
{
|
||||
/* Unfortunately, I don't know how to send a break */
|
||||
dbg("%s", __FUNCTION__);
|
||||
}
|
||||
|
||||
static void sierra_set_termios(struct usb_serial_port *port,
|
||||
struct termios *old_termios)
|
||||
{
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
sierra_send_setup(port);
|
||||
}
|
||||
|
||||
static int sierra_tiocmget(struct usb_serial_port *port, struct file *file)
|
||||
{
|
||||
unsigned int value;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
|
||||
((portdata->dtr_state) ? TIOCM_DTR : 0) |
|
||||
((portdata->cts_state) ? TIOCM_CTS : 0) |
|
||||
((portdata->dsr_state) ? TIOCM_DSR : 0) |
|
||||
((portdata->dcd_state) ? TIOCM_CAR : 0) |
|
||||
((portdata->ri_state) ? TIOCM_RNG : 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int sierra_tiocmset(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
if (set & TIOCM_RTS)
|
||||
portdata->rts_state = 1;
|
||||
if (set & TIOCM_DTR)
|
||||
portdata->dtr_state = 1;
|
||||
|
||||
if (clear & TIOCM_RTS)
|
||||
portdata->rts_state = 0;
|
||||
if (clear & TIOCM_DTR)
|
||||
portdata->dtr_state = 0;
|
||||
return sierra_send_setup(port);
|
||||
}
|
||||
|
||||
static int sierra_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
/* Write */
|
||||
static int sierra_write(struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
struct sierra_port_private *portdata;
|
||||
int i;
|
||||
int left, todo;
|
||||
struct urb *this_urb = NULL; /* spurious */
|
||||
int err;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
dbg("%s: write (%d chars)", __FUNCTION__, count);
|
||||
|
||||
i = 0;
|
||||
left = count;
|
||||
for (i=0; left > 0 && i < N_OUT_URB; i++) {
|
||||
todo = left;
|
||||
if (todo > OUT_BUFLEN)
|
||||
todo = OUT_BUFLEN;
|
||||
|
||||
this_urb = portdata->out_urbs[i];
|
||||
if (this_urb->status == -EINPROGRESS) {
|
||||
if (time_before(jiffies,
|
||||
portdata->tx_start_time[i] + 10 * HZ))
|
||||
continue;
|
||||
usb_unlink_urb(this_urb);
|
||||
continue;
|
||||
}
|
||||
if (this_urb->status != 0)
|
||||
dbg("usb_write %p failed (err=%d)",
|
||||
this_urb, this_urb->status);
|
||||
|
||||
dbg("%s: endpoint %d buf %d", __FUNCTION__,
|
||||
usb_pipeendpoint(this_urb->pipe), i);
|
||||
|
||||
/* send the data */
|
||||
memcpy (this_urb->transfer_buffer, buf, todo);
|
||||
this_urb->transfer_buffer_length = todo;
|
||||
|
||||
this_urb->dev = port->serial->dev;
|
||||
err = usb_submit_urb(this_urb, GFP_ATOMIC);
|
||||
if (err) {
|
||||
dbg("usb_submit_urb %p (write bulk) failed "
|
||||
"(%d, has %d)", this_urb,
|
||||
err, this_urb->status);
|
||||
continue;
|
||||
}
|
||||
portdata->tx_start_time[i] = jiffies;
|
||||
buf += todo;
|
||||
left -= todo;
|
||||
}
|
||||
|
||||
count -= left;
|
||||
dbg("%s: wrote (did %d)", __FUNCTION__, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void sierra_indat_callback(struct urb *urb)
|
||||
{
|
||||
int err;
|
||||
int endpoint;
|
||||
struct usb_serial_port *port;
|
||||
struct tty_struct *tty;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
|
||||
dbg("%s: %p", __FUNCTION__, urb);
|
||||
|
||||
endpoint = usb_pipeendpoint(urb->pipe);
|
||||
port = (struct usb_serial_port *) urb->context;
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s: nonzero status: %d on endpoint %02x.",
|
||||
__FUNCTION__, urb->status, endpoint);
|
||||
} else {
|
||||
tty = port->tty;
|
||||
if (urb->actual_length) {
|
||||
tty_buffer_request_room(tty, urb->actual_length);
|
||||
tty_insert_flip_string(tty, data, urb->actual_length);
|
||||
tty_flip_buffer_push(tty);
|
||||
} else {
|
||||
dbg("%s: empty read urb received", __FUNCTION__);
|
||||
}
|
||||
|
||||
/* Resubmit urb so we continue receiving */
|
||||
if (port->open_count && urb->status != -ESHUTDOWN) {
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err)
|
||||
printk(KERN_ERR "%s: resubmit read urb failed. "
|
||||
"(%d)", __FUNCTION__, err);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void sierra_outdat_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
port = (struct usb_serial_port *) urb->context;
|
||||
|
||||
usb_serial_port_softint(port);
|
||||
}
|
||||
|
||||
static void sierra_instat_callback(struct urb *urb)
|
||||
{
|
||||
int err;
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
|
||||
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
|
||||
|
||||
if (urb->status == 0) {
|
||||
struct usb_ctrlrequest *req_pkt =
|
||||
(struct usb_ctrlrequest *)urb->transfer_buffer;
|
||||
|
||||
if (!req_pkt) {
|
||||
dbg("%s: NULL req_pkt\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
if ((req_pkt->bRequestType == 0xA1) &&
|
||||
(req_pkt->bRequest == 0x20)) {
|
||||
int old_dcd_state;
|
||||
unsigned char signals = *((unsigned char *)
|
||||
urb->transfer_buffer +
|
||||
sizeof(struct usb_ctrlrequest));
|
||||
|
||||
dbg("%s: signal x%x", __FUNCTION__, signals);
|
||||
|
||||
old_dcd_state = portdata->dcd_state;
|
||||
portdata->cts_state = 1;
|
||||
portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
|
||||
portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
|
||||
portdata->ri_state = ((signals & 0x08) ? 1 : 0);
|
||||
|
||||
if (port->tty && !C_CLOCAL(port->tty) &&
|
||||
old_dcd_state && !portdata->dcd_state)
|
||||
tty_hangup(port->tty);
|
||||
} else {
|
||||
dbg("%s: type %x req %x", __FUNCTION__,
|
||||
req_pkt->bRequestType,req_pkt->bRequest);
|
||||
}
|
||||
} else
|
||||
dbg("%s: error %d", __FUNCTION__, urb->status);
|
||||
|
||||
/* Resubmit urb so we continue receiving IRQ data */
|
||||
if (urb->status != -ESHUTDOWN) {
|
||||
urb->dev = serial->dev;
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err)
|
||||
dbg("%s: resubmit intr urb failed. (%d)",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
}
|
||||
|
||||
static int sierra_write_room(struct usb_serial_port *port)
|
||||
{
|
||||
struct sierra_port_private *portdata;
|
||||
int i;
|
||||
int data_len = 0;
|
||||
struct urb *this_urb;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
for (i=0; i < N_OUT_URB; i++) {
|
||||
this_urb = portdata->out_urbs[i];
|
||||
if (this_urb && this_urb->status != -EINPROGRESS)
|
||||
data_len += OUT_BUFLEN;
|
||||
}
|
||||
|
||||
dbg("%s: %d", __FUNCTION__, data_len);
|
||||
return data_len;
|
||||
}
|
||||
|
||||
static int sierra_chars_in_buffer(struct usb_serial_port *port)
|
||||
{
|
||||
struct sierra_port_private *portdata;
|
||||
int i;
|
||||
int data_len = 0;
|
||||
struct urb *this_urb;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
for (i=0; i < N_OUT_URB; i++) {
|
||||
this_urb = portdata->out_urbs[i];
|
||||
if (this_urb && this_urb->status == -EINPROGRESS)
|
||||
data_len += this_urb->transfer_buffer_length;
|
||||
}
|
||||
dbg("%s: %d", __FUNCTION__, data_len);
|
||||
return data_len;
|
||||
}
|
||||
|
||||
static int sierra_open(struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
struct sierra_port_private *portdata;
|
||||
struct usb_serial *serial = port->serial;
|
||||
int i, err;
|
||||
struct urb *urb;
|
||||
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
/* Set some sane defaults */
|
||||
portdata->rts_state = 1;
|
||||
portdata->dtr_state = 1;
|
||||
|
||||
/* Reset low level data toggle and start reading from endpoints */
|
||||
for (i = 0; i < N_IN_URB; i++) {
|
||||
urb = portdata->in_urbs[i];
|
||||
if (! urb)
|
||||
continue;
|
||||
if (urb->dev != serial->dev) {
|
||||
dbg("%s: dev %p != %p", __FUNCTION__,
|
||||
urb->dev, serial->dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure endpoint data toggle is synchronized with the
|
||||
* device
|
||||
*/
|
||||
usb_clear_halt(urb->dev, urb->pipe);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (err) {
|
||||
dbg("%s: submit urb %d failed (%d) %d",
|
||||
__FUNCTION__, i, err,
|
||||
urb->transfer_buffer_length);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset low level data toggle on out endpoints */
|
||||
for (i = 0; i < N_OUT_URB; i++) {
|
||||
urb = portdata->out_urbs[i];
|
||||
if (! urb)
|
||||
continue;
|
||||
urb->dev = serial->dev;
|
||||
/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
|
||||
usb_pipeout(urb->pipe), 0); */
|
||||
}
|
||||
|
||||
port->tty->low_latency = 1;
|
||||
|
||||
sierra_send_setup(port);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static inline void stop_urb(struct urb *urb)
|
||||
{
|
||||
if (urb && urb->status == -EINPROGRESS)
|
||||
usb_kill_urb(urb);
|
||||
}
|
||||
|
||||
static void sierra_close(struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
portdata->rts_state = 0;
|
||||
portdata->dtr_state = 0;
|
||||
|
||||
if (serial->dev) {
|
||||
sierra_send_setup(port);
|
||||
|
||||
/* Stop reading/writing urbs */
|
||||
for (i = 0; i < N_IN_URB; i++)
|
||||
stop_urb(portdata->in_urbs[i]);
|
||||
for (i = 0; i < N_OUT_URB; i++)
|
||||
stop_urb(portdata->out_urbs[i]);
|
||||
}
|
||||
port->tty = NULL;
|
||||
}
|
||||
|
||||
/* Helper functions used by sierra_setup_urbs */
|
||||
static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
|
||||
int dir, void *ctx, char *buf, int len,
|
||||
usb_complete_t callback)
|
||||
{
|
||||
struct urb *urb;
|
||||
|
||||
if (endpoint == -1)
|
||||
return NULL; /* endpoint not needed */
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
|
||||
if (urb == NULL) {
|
||||
dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fill URB using supplied data. */
|
||||
usb_fill_bulk_urb(urb, serial->dev,
|
||||
usb_sndbulkpipe(serial->dev, endpoint) | dir,
|
||||
buf, len, callback, ctx);
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
/* Setup urbs */
|
||||
static void sierra_setup_urbs(struct usb_serial *serial)
|
||||
{
|
||||
int i,j;
|
||||
struct usb_serial_port *port;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
for (i = 0; i < serial->num_ports; i++) {
|
||||
port = serial->port[i];
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
/* Do indat endpoints first */
|
||||
for (j = 0; j < N_IN_URB; ++j) {
|
||||
portdata->in_urbs[j] = sierra_setup_urb (serial,
|
||||
port->bulk_in_endpointAddress, USB_DIR_IN, port,
|
||||
portdata->in_buffer[j], IN_BUFLEN, sierra_indat_callback);
|
||||
}
|
||||
|
||||
/* outdat endpoints */
|
||||
for (j = 0; j < N_OUT_URB; ++j) {
|
||||
portdata->out_urbs[j] = sierra_setup_urb (serial,
|
||||
port->bulk_out_endpointAddress, USB_DIR_OUT, port,
|
||||
portdata->out_buffer[j], OUT_BUFLEN, sierra_outdat_callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int sierra_startup(struct usb_serial *serial)
|
||||
{
|
||||
int i, err;
|
||||
struct usb_serial_port *port;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
/* Now setup per port private data */
|
||||
for (i = 0; i < serial->num_ports; i++) {
|
||||
port = serial->port[i];
|
||||
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
|
||||
if (!portdata) {
|
||||
dbg("%s: kmalloc for sierra_port_private (%d) failed!.",
|
||||
__FUNCTION__, i);
|
||||
return (1);
|
||||
}
|
||||
|
||||
usb_set_serial_port_data(port, portdata);
|
||||
|
||||
if (! port->interrupt_in_urb)
|
||||
continue;
|
||||
err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
if (err)
|
||||
dbg("%s: submit irq_in urb failed %d",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
|
||||
sierra_setup_urbs(serial);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void sierra_shutdown(struct usb_serial *serial)
|
||||
{
|
||||
int i, j;
|
||||
struct usb_serial_port *port;
|
||||
struct sierra_port_private *portdata;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
/* Stop reading/writing urbs */
|
||||
for (i = 0; i < serial->num_ports; ++i) {
|
||||
port = serial->port[i];
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
for (j = 0; j < N_IN_URB; j++)
|
||||
stop_urb(portdata->in_urbs[j]);
|
||||
for (j = 0; j < N_OUT_URB; j++)
|
||||
stop_urb(portdata->out_urbs[j]);
|
||||
}
|
||||
|
||||
/* Now free them */
|
||||
for (i = 0; i < serial->num_ports; ++i) {
|
||||
port = serial->port[i];
|
||||
portdata = usb_get_serial_port_data(port);
|
||||
|
||||
for (j = 0; j < N_IN_URB; j++) {
|
||||
if (portdata->in_urbs[j]) {
|
||||
usb_free_urb(portdata->in_urbs[j]);
|
||||
portdata->in_urbs[j] = NULL;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < N_OUT_URB; j++) {
|
||||
if (portdata->out_urbs[j]) {
|
||||
usb_free_urb(portdata->out_urbs[j]);
|
||||
portdata->out_urbs[j] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now free per port private data */
|
||||
for (i = 0; i < serial->num_ports; i++) {
|
||||
port = serial->port[i];
|
||||
kfree(usb_get_serial_port_data(port));
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_serial_driver sierra_1port_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "Sierra_Wireless",
|
||||
.owner = THIS_MODULE,
|
||||
.name = "sierra1",
|
||||
},
|
||||
.id_table = id_table,
|
||||
.num_interrupt_in = NUM_DONT_CARE,
|
||||
.num_bulk_in = NUM_DONT_CARE,
|
||||
.num_bulk_out = NUM_DONT_CARE,
|
||||
.num_ports = 3,
|
||||
.description = "Sierra USB modem (1 port)",
|
||||
.id_table = id_table_1port,
|
||||
.num_interrupt_in = NUM_DONT_CARE,
|
||||
.num_bulk_in = 1,
|
||||
.num_bulk_out = 1,
|
||||
.num_ports = 1,
|
||||
.open = sierra_open,
|
||||
.close = sierra_close,
|
||||
.write = sierra_write,
|
||||
.write_room = sierra_write_room,
|
||||
.chars_in_buffer = sierra_chars_in_buffer,
|
||||
.throttle = sierra_rx_throttle,
|
||||
.unthrottle = sierra_rx_unthrottle,
|
||||
.ioctl = sierra_ioctl,
|
||||
.set_termios = sierra_set_termios,
|
||||
.break_ctl = sierra_break_ctl,
|
||||
.tiocmget = sierra_tiocmget,
|
||||
.tiocmset = sierra_tiocmset,
|
||||
.attach = sierra_startup,
|
||||
.shutdown = sierra_shutdown,
|
||||
.read_int_callback = sierra_instat_callback,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver sierra_3port_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "sierra3",
|
||||
},
|
||||
.description = "Sierra USB modem (3 port)",
|
||||
.id_table = id_table_3port,
|
||||
.num_interrupt_in = NUM_DONT_CARE,
|
||||
.num_bulk_in = 3,
|
||||
.num_bulk_out = 3,
|
||||
.num_ports = 3,
|
||||
.open = sierra_open,
|
||||
.close = sierra_close,
|
||||
.write = sierra_write,
|
||||
.write_room = sierra_write_room,
|
||||
.chars_in_buffer = sierra_chars_in_buffer,
|
||||
.throttle = sierra_rx_throttle,
|
||||
.unthrottle = sierra_rx_unthrottle,
|
||||
.ioctl = sierra_ioctl,
|
||||
.set_termios = sierra_set_termios,
|
||||
.break_ctl = sierra_break_ctl,
|
||||
.tiocmget = sierra_tiocmget,
|
||||
.tiocmset = sierra_tiocmset,
|
||||
.attach = sierra_startup,
|
||||
.shutdown = sierra_shutdown,
|
||||
.read_int_callback = sierra_instat_callback,
|
||||
};
|
||||
|
||||
/* Functions used by new usb-serial code. */
|
||||
static int __init sierra_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_serial_register(&sierra_device);
|
||||
retval = usb_serial_register(&sierra_1port_device);
|
||||
if (retval)
|
||||
return retval;
|
||||
goto failed_1port_device_register;
|
||||
retval = usb_serial_register(&sierra_3port_device);
|
||||
if (retval)
|
||||
goto failed_3port_device_register;
|
||||
|
||||
|
||||
retval = usb_register(&sierra_driver);
|
||||
if (retval)
|
||||
usb_serial_deregister(&sierra_device);
|
||||
goto failed_driver_register;
|
||||
|
||||
info(DRIVER_DESC ": " DRIVER_VERSION);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_driver_register:
|
||||
usb_serial_deregister(&sierra_3port_device);
|
||||
failed_3port_device_register:
|
||||
usb_serial_deregister(&sierra_1port_device);
|
||||
failed_1port_device_register:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit sierra_exit(void)
|
||||
{
|
||||
usb_deregister(&sierra_driver);
|
||||
usb_serial_deregister(&sierra_device);
|
||||
usb_deregister (&sierra_driver);
|
||||
usb_serial_deregister(&sierra_1port_device);
|
||||
usb_serial_deregister(&sierra_3port_device);
|
||||
}
|
||||
|
||||
module_init(sierra_init);
|
||||
module_exit(sierra_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug messages");
|
||||
#endif
|
||||
|
||||
|
@ -55,7 +55,8 @@ UNUSUAL_DEV( 0x03eb, 0x2002, 0x0100, 0x0100,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_IGNORE_RESIDUE),
|
||||
|
||||
UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100,
|
||||
/* modified by Tobias Lorenz <tobias.lorenz@gmx.net> */
|
||||
UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0200,
|
||||
"Mitsumi",
|
||||
"USB FDD",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
@ -182,6 +183,20 @@ UNUSUAL_DEV( 0x0421, 0x044e, 0x0100, 0x0100,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ),
|
||||
|
||||
/* Reported by Bardur Arantsson <bardur@scientician.net> */
|
||||
UNUSUAL_DEV( 0x0421, 0x047c, 0x0370, 0x0370,
|
||||
"Nokia",
|
||||
"6131",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_MAX_SECTORS_64 ),
|
||||
|
||||
/* Reported by Alex Corcoles <alex@corcoles.net> */
|
||||
UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370,
|
||||
"Nokia",
|
||||
"6234",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_MAX_SECTORS_64 ),
|
||||
|
||||
/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */
|
||||
UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210,
|
||||
"SMSC",
|
||||
@ -1291,6 +1306,13 @@ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY ),
|
||||
|
||||
/* Reported by Jan Mate <mate@fiit.stuba.sk> */
|
||||
UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
|
||||
"Sony Ericsson",
|
||||
"P990i",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY ),
|
||||
|
||||
/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
|
||||
* Tested on hardware version 1.10.
|
||||
* Entry is needed only for the initializer function override.
|
||||
|
Loading…
Reference in New Issue
Block a user