Explorar o código

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
  ...
Linus Torvalds %!s(int64=18) %!d(string=hai) anos
pai
achega
699ddda6ec
Modificáronse 50 ficheiros con 3894 adicións e 639 borrados
  1. 91 24
      Documentation/input/xpad.txt
  2. 1 1
      drivers/usb/Makefile
  3. 3 0
      drivers/usb/atm/cxacru.c
  4. 77 16
      drivers/usb/atm/speedtch.c
  5. 41 42
      drivers/usb/atm/ueagle-atm.c
  6. 1 4
      drivers/usb/atm/usbatm.c
  7. 16 13
      drivers/usb/class/cdc-acm.c
  8. 71 8
      drivers/usb/class/usblp.c
  9. 6 2
      drivers/usb/core/devio.c
  10. 9 13
      drivers/usb/core/endpoint.c
  11. 16 4
      drivers/usb/gadget/net2280.c
  12. 3 1
      drivers/usb/host/ehci-dbg.c
  13. 21 49
      drivers/usb/host/ehci-hcd.c
  14. 1 1
      drivers/usb/host/ehci-hub.c
  15. 1 1
      drivers/usb/host/ehci-pci.c
  16. 4 2
      drivers/usb/host/ehci-q.c
  17. 7 15
      drivers/usb/host/ehci.h
  18. 6 3
      drivers/usb/host/ohci-pnx4008.c
  19. 39 5
      drivers/usb/host/uhci-hcd.c
  20. 0 10
      drivers/usb/input/Kconfig
  21. 1 2
      drivers/usb/input/Makefile
  22. 17 7
      drivers/usb/input/hid-core.c
  23. 2 2
      drivers/usb/input/usbtouchscreen.c
  24. 2 0
      drivers/usb/input/wacom.h
  25. 12 8
      drivers/usb/input/wacom_sys.c
  26. 66 55
      drivers/usb/input/wacom_wac.c
  27. 1 1
      drivers/usb/input/wacom_wac.h
  28. 95 44
      drivers/usb/input/xpad.c
  29. 10 0
      drivers/usb/misc/Kconfig
  30. 1 0
      drivers/usb/misc/Makefile
  31. 2 1
      drivers/usb/misc/adutux.c
  32. 11 25
      drivers/usb/misc/ftdi-elan.c
  33. 0 0
      drivers/usb/misc/trancevibrator.c
  34. 8 0
      drivers/usb/net/Kconfig
  35. 1 0
      drivers/usb/net/Makefile
  36. 13 35
      drivers/usb/net/asix.c
  37. 1 1
      drivers/usb/net/cdc_ether.c
  38. 129 75
      drivers/usb/net/kaweth.c
  39. 534 0
      drivers/usb/net/mcs7830.c
  40. 53 2
      drivers/usb/net/usbnet.c
  41. 5 0
      drivers/usb/net/usbnet.h
  42. 11 2
      drivers/usb/serial/Kconfig
  43. 1 0
      drivers/usb/serial/Makefile
  44. 2 5
      drivers/usb/serial/airprime.c
  45. 1 0
      drivers/usb/serial/cp2101.c
  46. 127 127
      drivers/usb/serial/ftdi_sio.c
  47. 1683 0
      drivers/usb/serial/mos7720.c
  48. 2 1
      drivers/usb/serial/mos7840.c
  49. 666 31
      drivers/usb/serial/sierra.c
  50. 23 1
      drivers/usb/storage/unusual_devs.h

+ 91 - 24
Documentation/input/xpad.txt

@@ -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.
 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
 Basically, this was hacked away in just a few hours, so don't expect
 miracles.
 miracles.
+
 In particular, there is currently NO support for the rumble pack.
 In particular, there is currently NO support for the rumble pack.
 You won't find many ff-aware linux applications anyway.
 You won't find many ff-aware linux applications anyway.
 
 
 
 
-0. Status
----------
+0. Notes
+--------
+
+Driver updated for kernel 2.6.17.11. (Based on a patch for 2.6.11.4.)
 
 
-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.
+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)
 
 
-The jstest-program from joystick-1.2.15 (jstest-version 2.1.0) reports
-8 axes and 10 buttons.
+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.
 
 
-Alls 8 axes work, though they all have the same range (-32768..32767)
+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
 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
 is some limitation of jstest, since the input device setup should be fine. I
 didn't have a look at jstest itself yet).
 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.
 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
 1. USB adapter
 --------------
 --------------
 
 
 Before you can actually use the driver, you need to get yourself an
 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
 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
 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 ;)
 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
 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.
 to load your USB subsystem and should cat /proc/bus/usb/devices.
 There should be an entry like the one at the end [4].
 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
  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
  original Microsoft XBOX controller (Japan), vendor=0x045e, product=0x0285
  InterAct PowerPad Pro (Germany), vendor=0x05fd, product=0x107a
  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:
 If you compiled and installed the driver, test the functionality:
 > modprobe xpad
 > modprobe xpad
 > modprobe joydev
 > modprobe joydev
 > jstest /dev/js0
 > 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
 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=81(I) Atr=03(Int.) MxPS=  32 Ivl= 10ms
 E:  Ad=02(O) 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>
 Marko Friedemann <mfr@bmx-chemnitz.de>
 2002-07-16
 2002-07-16
+ - original doc
+
+Dominic Cerquetti <binary1230@yahoo.com>
+2005-03-19
+ - added stuff for dance pads, new d-pad->axes mappings

+ 1 - 1
drivers/usb/Makefile

@@ -33,7 +33,6 @@ obj-$(CONFIG_USB_KBTAB)		+= input/
 obj-$(CONFIG_USB_MOUSE)		+= input/
 obj-$(CONFIG_USB_MOUSE)		+= input/
 obj-$(CONFIG_USB_MTOUCH)	+= input/
 obj-$(CONFIG_USB_MTOUCH)	+= input/
 obj-$(CONFIG_USB_POWERMATE)	+= input/
 obj-$(CONFIG_USB_POWERMATE)	+= input/
-obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
 obj-$(CONFIG_USB_XPAD)		+= input/
 obj-$(CONFIG_USB_XPAD)		+= input/
 
 
@@ -66,6 +65,7 @@ obj-$(CONFIG_USB_PHIDGETSERVO)	+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
 obj-$(CONFIG_USB_SISUSBVGA)	+= misc/
 obj-$(CONFIG_USB_SISUSBVGA)	+= misc/
 obj-$(CONFIG_USB_TEST)		+= misc/
 obj-$(CONFIG_USB_TEST)		+= misc/
+obj-$(CONFIG_USB_TRANCEVIBRATOR)+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
 
 
 obj-$(CONFIG_USB_ATM)		+= atm/
 obj-$(CONFIG_USB_ATM)		+= atm/

+ 3 - 0
drivers/usb/atm/cxacru.c

@@ -793,6 +793,9 @@ static const struct usb_device_id cxacru_usb_ids[] = {
 	{ /* V = Conexant			P = ADSL modem				*/
 	{ /* V = Conexant			P = ADSL modem				*/
 		USB_DEVICE(0x0572, 0xcb06),	.driver_info = (unsigned long) &cxacru_cb00
 		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		*/
 	{ /* V = Olitec				P = ADSL modem version 2		*/
 		USB_DEVICE(0x08e3, 0x0100),	.driver_info = (unsigned long) &cxacru_cafe
 		USB_DEVICE(0x08e3, 0x0100),	.driver_info = (unsigned long) &cxacru_cafe
 	},
 	},

+ 77 - 16
drivers/usb/atm/speedtch.c

@@ -55,7 +55,6 @@ static const char speedtch_driver_name[] = "speedtch";
 #define OFFSET_d	9		/* size 4 */
 #define OFFSET_d	9		/* size 4 */
 #define OFFSET_e	13		/* size 1 */
 #define OFFSET_e	13		/* size 1 */
 #define OFFSET_f	14		/* size 1 */
 #define OFFSET_f	14		/* size 1 */
-#define TOTAL		15
 
 
 #define SIZE_7		1
 #define SIZE_7		1
 #define SIZE_b		8
 #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 enable_isoc = DEFAULT_ENABLE_ISOC;
 static int sw_buffering = DEFAULT_SW_BUFFERING;
 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_param(altsetting, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(altsetting,
 MODULE_PARM_DESC(altsetting,
 		"Alternative setting for data interface (bulk_default: "
 		"Alternative setting for data interface (bulk_default: "
@@ -100,6 +111,17 @@ MODULE_PARM_DESC(sw_buffering,
 		 "Enable software buffering (default: "
 		 "Enable software buffering (default: "
 		 __MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
 		 __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 INTERFACE_DATA		1
 #define ENDPOINT_INT		0x81
 #define ENDPOINT_INT		0x81
 #define ENDPOINT_BULK_DATA	0x07
 #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) )
 #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 speedtch_instance_data {
 	struct usbatm_data *usbatm;
 	struct usbatm_data *usbatm;
 
 
-	unsigned int altsetting;
+	struct speedtch_params params; /* set in probe, constant afterwards */
 
 
 	struct work_struct status_checker;
 	struct work_struct status_checker;
 
 
@@ -123,7 +152,7 @@ struct speedtch_instance_data {
 	struct urb *int_urb;
 	struct urb *int_urb;
 	unsigned char int_data[16];
 	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);
 			      0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT);
 	if (ret < 0)
 	if (ret < 0)
 		usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret);
 		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,
 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. */
 	   because we're in our own kernel thread anyway. */
 	msleep_interruptible(1000);
 	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;
 		goto out_free;
 	}
 	}
 
 
@@ -372,7 +429,7 @@ static int speedtch_read_status(struct speedtch_instance_data *instance)
 	unsigned char *buf = instance->scratch_buffer;
 	unsigned char *buf = instance->scratch_buffer;
 	int ret;
 	int ret;
 
 
-	memset(buf, 0, TOTAL);
+	memset(buf, 0, 16);
 
 
 	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
 	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
 			      0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
 			      0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
@@ -746,17 +803,21 @@ static int speedtch_bind(struct usbatm_data *usbatm,
 
 
 	instance->usbatm = 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;
 	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) {
 		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);
 			usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret);
 			use_isoc = 0; /* fall back to bulk */
 			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");
 			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) {
 		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);
 			usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret);
 			goto fail_free;
 			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);
 	usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0);
 
 

+ 41 - 42
drivers/usb/atm/ueagle-atm.c

@@ -68,7 +68,7 @@
 
 
 #include "usbatm.h"
 #include "usbatm.h"
 
 
-#define EAGLEUSBVERSION "ueagle 1.3"
+#define EAGLEUSBVERSION "ueagle 1.4"
 
 
 
 
 /*
 /*
@@ -80,14 +80,14 @@
 			dev_dbg(&(usb_dev)->dev, \
 			dev_dbg(&(usb_dev)->dev, \
 				"[ueagle-atm dbg] %s: " format, \
 				"[ueagle-atm dbg] %s: " format, \
 					__FUNCTION__, ##args); \
 					__FUNCTION__, ##args); \
-       } while (0)
+	} while (0)
 
 
 #define uea_vdbg(usb_dev, format, args...)	\
 #define uea_vdbg(usb_dev, format, args...)	\
 	do { \
 	do { \
 		if (debug >= 2) \
 		if (debug >= 2) \
 			dev_dbg(&(usb_dev)->dev, \
 			dev_dbg(&(usb_dev)->dev, \
 				"[ueagle-atm vdbg]  " format, ##args); \
 				"[ueagle-atm vdbg]  " format, ##args); \
-       } while (0)
+	} while (0)
 
 
 #define uea_enters(usb_dev) \
 #define uea_enters(usb_dev) \
 	uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
 	uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
@@ -218,8 +218,8 @@ enum {
 #define UEA_CHIP_VERSION(x) \
 #define UEA_CHIP_VERSION(x) \
 	((x)->driver_info & 0xf)
 	((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
 #define INS_TO_USBDEV(ins) ins->usb_dev
 
 
@@ -625,12 +625,12 @@ static int request_dsp(struct uea_softc *sc)
 	char *dsp_name;
 	char *dsp_name;
 
 
 	if (UEA_CHIP_VERSION(sc) == ADI930) {
 	if (UEA_CHIP_VERSION(sc) == ADI930) {
-		if (IS_ISDN(sc))
+		if (IS_ISDN(sc->usb_dev))
 			dsp_name = FW_DIR "DSP9i.bin";
 			dsp_name = FW_DIR "DSP9i.bin";
 		else
 		else
 			dsp_name = FW_DIR "DSP9p.bin";
 			dsp_name = FW_DIR "DSP9p.bin";
 	} else {
 	} else {
-		if (IS_ISDN(sc))
+		if (IS_ISDN(sc->usb_dev))
 			dsp_name = FW_DIR "DSPei.bin";
 			dsp_name = FW_DIR "DSPei.bin";
 		else
 		else
 			dsp_name = FW_DIR "DSPep.bin";
 			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)
 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, ACK_TIMEOUT);
 	sc->cmv_ack = 0;
 	sc->cmv_ack = 0;
 
 
@@ -885,7 +885,8 @@ static int uea_stat(struct uea_softc *sc)
 		break;
 		break;
 
 
 	case 3:		/* fail ... */
 	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;
 		return -EAGAIN;
 
 
 	case 4 ... 6:	/* test state */
 	case 4 ... 6:	/* test state */
@@ -913,12 +914,6 @@ static int uea_stat(struct uea_softc *sc)
 			release_firmware(sc->dsp_firm);
 			release_firmware(sc->dsp_firm);
 			sc->dsp_firm = NULL;
 			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
 	/* 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 (cmv_file[sc->modem_index] == NULL) {
 		if (UEA_CHIP_VERSION(sc) == ADI930)
 		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
 		else
-			file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
+			file = (IS_ISDN(sc->usb_dev)) ? "CMVei.bin" : "CMVep.bin";
 	} else
 	} else
 		file = cmv_file[sc->modem_index];
 		file = cmv_file[sc->modem_index];
 
 
@@ -1131,6 +1126,13 @@ static int uea_start_reset(struct uea_softc *sc)
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		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 */
 	/* get options */
  	ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
  	ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
 	if (ret < 0)
 	if (ret < 0)
@@ -1147,6 +1149,8 @@ static int uea_start_reset(struct uea_softc *sc)
 	/* Enter in R-ACT-REQ */
 	/* Enter in R-ACT-REQ */
 	ret = uea_write_cmv(sc, SA_CNTL, 0, 2);
 	ret = uea_write_cmv(sc, SA_CNTL, 0, 2);
 	uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
 	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:
 out:
 	release_firmware(cmvs_fw);
 	release_firmware(cmvs_fw);
 	sc->reset = 0;
 	sc->reset = 0;
@@ -1172,7 +1176,10 @@ static int uea_kthread(void *data)
 		if (!ret)
 		if (!ret)
 			ret = uea_stat(sc);
 			ret = uea_stat(sc);
 		if (ret != -EAGAIN)
 		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));
 	uea_leaves(INS_TO_USBDEV(sc));
 	return ret;
 	return ret;
@@ -1566,6 +1573,7 @@ UEA_ATTR(uscorr, 0);
 UEA_ATTR(dscorr, 0);
 UEA_ATTR(dscorr, 0);
 UEA_ATTR(usunc, 0);
 UEA_ATTR(usunc, 0);
 UEA_ATTR(dsunc, 0);
 UEA_ATTR(dsunc, 0);
+UEA_ATTR(firmid, 0);
 
 
 /* Retrieve the device End System Identifier (MAC) */
 /* 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;
 	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;
 	return 0;
 
 
@@ -1639,16 +1647,13 @@ static struct attribute *attrs[] = {
 	&dev_attr_stat_dscorr.attr,
 	&dev_attr_stat_dscorr.attr,
 	&dev_attr_stat_usunc.attr,
 	&dev_attr_stat_usunc.attr,
 	&dev_attr_stat_dsunc.attr,
 	&dev_attr_stat_dsunc.attr,
+	&dev_attr_stat_firmid.attr,
+	NULL,
 };
 };
 static struct attribute_group attr_grp = {
 static struct attribute_group attr_grp = {
 	.attrs = attrs,
 	.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,
 static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
 		   const struct usb_device_id *id)
 		   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);
 	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;
 	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)
 static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 {
 {
 	struct uea_softc *sc = usbatm->driver_data;
 	struct uea_softc *sc = usbatm->driver_data;
 
 
-	destroy_fs_entries(intf);
+	sysfs_remove_group(&intf->dev.kobj, &attr_grp);
 	uea_stop(sc);
 	uea_stop(sc);
 	kfree(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);
 	struct usb_device *usb = interface_to_usbdev(intf);
 
 
 	uea_enters(usb);
 	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.idVendor),
 	       le16_to_cpu(usb->descriptor.idProduct),
 	       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);
 	usb_reset_device(usb);
 
 

+ 1 - 4
drivers/usb/atm/usbatm.c

@@ -1001,6 +1001,7 @@ static int usbatm_do_heavy_init(void *arg)
 
 
 	daemonize(instance->driver->driver_name);
 	daemonize(instance->driver->driver_name);
 	allow_signal(SIGTERM);
 	allow_signal(SIGTERM);
+	instance->thread_pid = current->pid;
 
 
 	complete(&instance->thread_started);
 	complete(&instance->thread_started);
 
 
@@ -1025,10 +1026,6 @@ static int usbatm_heavy_init(struct usbatm_data *instance)
 		return ret;
 		return ret;
 	}
 	}
 
 
-	mutex_lock(&instance->serialize);
-	instance->thread_pid = ret;
-	mutex_unlock(&instance->serialize);
-
 	wait_for_completion(&instance->thread_started);
 	wait_for_completion(&instance->thread_started);
 
 
 	return 0;
 	return 0;

+ 16 - 13
drivers/usb/class/cdc-acm.c

@@ -325,7 +325,7 @@ static void acm_rx_tasklet(unsigned long _acm)
 	struct acm_rb *buf;
 	struct acm_rb *buf;
 	struct tty_struct *tty = acm->tty;
 	struct tty_struct *tty = acm->tty;
 	struct acm_ru *rcv;
 	struct acm_ru *rcv;
-	//unsigned long flags;
+	unsigned long flags;
 	int i = 0;
 	int i = 0;
 	dbg("Entering acm_rx_tasklet");
 	dbg("Entering acm_rx_tasklet");
 
 
@@ -333,15 +333,15 @@ static void acm_rx_tasklet(unsigned long _acm)
 		return;
 		return;
 
 
 next_buffer:
 next_buffer:
-	spin_lock(&acm->read_lock);
+	spin_lock_irqsave(&acm->read_lock, flags);
 	if (list_empty(&acm->filled_read_bufs)) {
 	if (list_empty(&acm->filled_read_bufs)) {
-		spin_unlock(&acm->read_lock);
+		spin_unlock_irqrestore(&acm->read_lock, flags);
 		goto urbs;
 		goto urbs;
 	}
 	}
 	buf = list_entry(acm->filled_read_bufs.next,
 	buf = list_entry(acm->filled_read_bufs.next,
 			 struct acm_rb, list);
 			 struct acm_rb, list);
 	list_del(&buf->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);
 	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);
 		memmove(buf->base, buf->base + i, buf->size - i);
 		buf->size -= i;
 		buf->size -= i;
 		spin_unlock(&acm->throttle_lock);
 		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);
 		list_add(&buf->list, &acm->filled_read_bufs);
-		spin_unlock(&acm->read_lock);
+		spin_unlock_irqrestore(&acm->read_lock, flags);
 		return;
 		return;
 	}
 	}
 	spin_unlock(&acm->throttle_lock);
 	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);
 	list_add(&buf->list, &acm->spare_read_bufs);
-	spin_unlock(&acm->read_lock);
+	spin_unlock_irqrestore(&acm->read_lock, flags);
 	goto next_buffer;
 	goto next_buffer;
 
 
 urbs:
 urbs:
 	while (!list_empty(&acm->spare_read_bufs)) {
 	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)) {
 		if (list_empty(&acm->spare_read_urbs)) {
-			spin_unlock(&acm->read_lock);
+			spin_unlock_irqrestore(&acm->read_lock, flags);
 			return;
 			return;
 		}
 		}
 		rcv = list_entry(acm->spare_read_urbs.next,
 		rcv = list_entry(acm->spare_read_urbs.next,
 				 struct acm_ru, list);
 				 struct acm_ru, list);
 		list_del(&rcv->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,
 		buf = list_entry(acm->spare_read_bufs.next,
 				 struct acm_rb, list);
 				 struct acm_rb, list);
@@ -400,9 +400,9 @@ urbs:
 		   free-urbs-pool and resubmited ASAP */
 		   free-urbs-pool and resubmited ASAP */
 		if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
 		if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
 			list_add(&buf->list, &acm->spare_read_bufs);
 			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);
 			list_add(&rcv->list, &acm->spare_read_urbs);
-			spin_unlock(&acm->read_lock);
+			spin_unlock_irqrestore(&acm->read_lock, flags);
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -1083,6 +1083,9 @@ static struct usb_device_id acm_ids[] = {
 	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
 	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
 	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
 	.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 */
 	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
 	.driver_info = SINGLE_RX_URB, /* firmware bug */
 	.driver_info = SINGLE_RX_URB, /* firmware bug */
 	},
 	},

+ 71 - 8
drivers/usb/class/usblp.c

@@ -154,6 +154,7 @@ struct usblp {
 	unsigned char		used;			/* True if open */
 	unsigned char		used;			/* True if open */
 	unsigned char		present;		/* True if not disconnected */
 	unsigned char		present;		/* True if not disconnected */
 	unsigned char		bidir;			/* interface is bidirectional */
 	unsigned char		bidir;			/* interface is bidirectional */
+	unsigned char		sleeping;		/* interface is suspended */
 	unsigned char		*device_id_string;	/* IEEE 1284 DEVICE ID string (ptr) */
 	unsigned char		*device_id_string;	/* IEEE 1284 DEVICE ID string (ptr) */
 							/* first 2 bytes are (big-endian) length */
 							/* first 2 bytes are (big-endian) length */
 };
 };
@@ -183,6 +184,7 @@ static void usblp_dump(struct usblp *usblp) {
 	dbg("quirks=%d", usblp->quirks);
 	dbg("quirks=%d", usblp->quirks);
 	dbg("used=%d", usblp->used);
 	dbg("used=%d", usblp->used);
 	dbg("bidir=%d", usblp->bidir);
 	dbg("bidir=%d", usblp->bidir);
+	dbg("sleeping=%d", usblp->sleeping);
 	dbg("device_id_string=\"%s\"",
 	dbg("device_id_string=\"%s\"",
 		usblp->device_id_string ?
 		usblp->device_id_string ?
 			usblp->device_id_string + 2 :
 			usblp->device_id_string + 2 :
@@ -338,6 +340,20 @@ static int usblp_check_status(struct usblp *usblp, int err)
 	return newerr;
 	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.
  * File op functions.
  */
  */
@@ -390,14 +406,9 @@ static int usblp_open(struct inode *inode, struct file *file)
 	usblp->writeurb->status = 0;
 	usblp->writeurb->status = 0;
 	usblp->readurb->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:
 out:
 	mutex_unlock (&usblp_mutex);
 	mutex_unlock (&usblp_mutex);
@@ -460,6 +471,11 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		goto done;
 		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),
 	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) );
 		_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;
 			return -ENODEV;
 		}
 		}
 
 
+		if (usblp->sleeping) {
+			up (&usblp->sem);
+			return writecount ? writecount : -ENODEV;
+		}
+
 		if (usblp->writeurb->status != 0) {
 		if (usblp->writeurb->status != 0) {
 			if (usblp->quirks & USBLP_QUIRK_BIDIR) {
 			if (usblp->quirks & USBLP_QUIRK_BIDIR) {
 				if (!usblp->wcomplete)
 				if (!usblp->wcomplete)
@@ -749,6 +770,11 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
 		goto done;
 		goto done;
 	}
 	}
 
 
+	if (usblp->sleeping) {
+		count = -ENODEV;
+		goto done;
+	}
+
 	if (usblp->readurb->status) {
 	if (usblp->readurb->status) {
 		err("usblp%d: error %d reading from printer",
 		err("usblp%d: error %d reading from printer",
 			usblp->minor, usblp->readurb->status);
 			usblp->minor, usblp->readurb->status);
@@ -1167,6 +1193,41 @@ static void usblp_disconnect(struct usb_interface *intf)
 	mutex_unlock (&usblp_mutex);
 	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 [] = {
 static struct usb_device_id usblp_ids [] = {
 	{ USB_DEVICE_INFO(7, 1, 1) },
 	{ USB_DEVICE_INFO(7, 1, 1) },
 	{ USB_DEVICE_INFO(7, 1, 2) },
 	{ USB_DEVICE_INFO(7, 1, 2) },
@@ -1183,6 +1244,8 @@ static struct usb_driver usblp_driver = {
 	.name =		"usblp",
 	.name =		"usblp",
 	.probe =	usblp_probe,
 	.probe =	usblp_probe,
 	.disconnect =	usblp_disconnect,
 	.disconnect =	usblp_disconnect,
+	.suspend =	usblp_suspend,
+	.resume =	usblp_resume,
 	.id_table =	usblp_ids,
 	.id_table =	usblp_ids,
 };
 };
 
 

+ 6 - 2
drivers/usb/core/devio.c

@@ -1588,15 +1588,18 @@ const struct file_operations usbfs_device_file_operations = {
 	.release =	usbdev_release,
 	.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);
 	int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
 
 
 	dev->class_dev = class_device_create(usb_device_class, NULL,
 	dev->class_dev = class_device_create(usb_device_class, NULL,
 				MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
 				MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
 				"usbdev%d.%d", dev->bus->busnum, dev->devnum);
 				"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;
 	dev->class_dev->class_data = dev;
+	return 0;
 }
 }
 
 
 static void usbdev_remove(struct usb_device *dev)
 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) {
 	switch (action) {
 	case USB_DEVICE_ADD:
 	case USB_DEVICE_ADD:
-		usbdev_add(dev);
+		if (usbdev_add(dev))
+			return NOTIFY_BAD;
 		break;
 		break;
 	case USB_DEVICE_REMOVE:
 	case USB_DEVICE_REMOVE:
 		usbdev_remove(dev);
 		usbdev_remove(dev);

+ 9 - 13
drivers/usb/core/endpoint.c

@@ -223,7 +223,7 @@ int usb_create_ep_files(struct device *parent,
 	ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
 	ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
 	if (!ep_dev) {
 	if (!ep_dev) {
 		retval = -ENOMEM;
 		retval = -ENOMEM;
-		goto exit;
+		goto error_alloc;
 	}
 	}
 
 
 	/* fun calculation to determine the minor of this endpoint */
 	/* 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);
 	retval = device_register(&ep_dev->dev);
 	if (retval)
 	if (retval)
-		goto error;
+		goto error_register;
 	retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
 	retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
 	if (retval)
 	if (retval)
 		goto error_group;
 		goto error_group;
 
 
-	endpoint->ep_dev = ep_dev;
-
 	/* create the symlink to the old-style "ep_XX" directory */
 	/* create the symlink to the old-style "ep_XX" directory */
 	sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
 	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)
 	if (retval)
 		goto error_link;
 		goto error_link;
-exit:
+	endpoint->ep_dev = ep_dev;
 	return retval;
 	return retval;
 
 
 error_link:
 error_link:
 	sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
 	sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
-
 error_group:
 error_group:
 	device_unregister(&ep_dev->dev);
 	device_unregister(&ep_dev->dev);
-	endpoint->ep_dev = NULL;
 	destroy_endpoint_class();
 	destroy_endpoint_class();
 	return retval;
 	return retval;
-error:
+
+error_register:
 	kfree(ep_dev);
 	kfree(ep_dev);
+error_alloc:
 	destroy_endpoint_class();
 	destroy_endpoint_class();
+exit:
 	return retval;
 	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);
 		sysfs_remove_group(&endpoint->ep_dev->dev.kobj, &ep_dev_attr_grp);
 		device_unregister(&endpoint->ep_dev->dev);
 		device_unregister(&endpoint->ep_dev->dev);
 		endpoint->ep_dev = NULL;
 		endpoint->ep_dev = NULL;
+		destroy_endpoint_class();
 	}
 	}
-	destroy_endpoint_class();
 }
 }
-
-

+ 16 - 4
drivers/usb/gadget/net2280.c

@@ -2044,8 +2044,10 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
 		return retval;
 		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
 	/* ... then enable host detection and ep0; and we're ready
 	 * for set_configuration as well as eventual disconnect.
 	 * 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 */
 	/* pci writes may still be posted */
 	return 0;
 	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);
 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");
 				: "disabled");
 	the_controller = dev;
 	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;
 	return 0;
 
 

+ 3 - 1
drivers/usb/host/ehci-dbg.c

@@ -754,7 +754,9 @@ show_registers (struct class_device *class_dev, char *buf)
 	}
 	}
 
 
 	if (ehci->reclaim) {
 	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;
 		size -= temp;
 		next += temp;
 		next += temp;
 	}
 	}

+ 21 - 49
drivers/usb/host/ehci-hcd.c

@@ -111,7 +111,7 @@ static const char	hcd_name [] = "ehci_hcd";
 #define	EHCI_TUNE_MULT_TT	1
 #define	EHCI_TUNE_MULT_TT	1
 #define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
 #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_IO_JIFFIES		(HZ/10)		/* io watchdog > irq_thresh */
 #define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #define EHCI_SHRINK_JIFFIES	(HZ/200)	/* async qh unlink delay */
 #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);
 static void ehci_work(struct ehci_hcd *ehci);
 
 
 #include "ehci-hub.c"
 #include "ehci-hub.c"
@@ -264,37 +263,25 @@ static void ehci_work(struct ehci_hcd *ehci);
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static void ehci_iaa_watchdog (unsigned long param)
+static void ehci_watchdog (unsigned long param)
 {
 {
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
 	unsigned long		flags;
 	unsigned long		flags;
-	u32			status;
 
 
 	spin_lock_irqsave (&ehci->lock, flags);
 	spin_lock_irqsave (&ehci->lock, flags);
-	WARN_ON(!ehci->reclaim);
 
 
-	/* lost IAA irqs wedge things badly; seen first with a vt8235 */
+	/* lost IAA irqs wedge things badly; seen with a vt8235 */
 	if (ehci->reclaim) {
 	if (ehci->reclaim) {
-		status = readl (&ehci->regs->status);
+		u32		status = readl (&ehci->regs->status);
 		if (status & STS_IAA) {
 		if (status & STS_IAA) {
 			ehci_vdbg (ehci, "lost IAA\n");
 			ehci_vdbg (ehci, "lost IAA\n");
 			COUNT (ehci->stats.lost_iaa);
 			COUNT (ehci->stats.lost_iaa);
 			writel (STS_IAA, &ehci->regs->status);
 			writel (STS_IAA, &ehci->regs->status);
-			end_unlink_async (ehci);
+			ehci->reclaim_ready = 1;
 		}
 		}
 	}
 	}
 
 
-	spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
-static void ehci_watchdog (unsigned long param)
-{
-	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
-	unsigned long		flags;
-
-	spin_lock_irqsave (&ehci->lock, flags);
-
-	/* stop async processing after it's idled a bit */
+ 	/* stop async processing after it's idled a bit */
 	if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
 	if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
 		start_unlink_async (ehci, ehci->async);
 		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)
 static void ehci_work (struct ehci_hcd *ehci)
 {
 {
 	timer_action_done (ehci, TIMER_IO_WATCHDOG);
 	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
 	/* another CPU may drop ehci->lock during a schedule scan while
 	 * it reports urb completions.  this flag guards against bogus
 	 * it reports urb completions.  this flag guards against bogus
@@ -379,7 +368,6 @@ static void ehci_stop (struct usb_hcd *hcd)
 
 
 	/* no more interrupts ... */
 	/* no more interrupts ... */
 	del_timer_sync (&ehci->watchdog);
 	del_timer_sync (&ehci->watchdog);
-	del_timer_sync (&ehci->iaa_watchdog);
 
 
 	spin_lock_irq(&ehci->lock);
 	spin_lock_irq(&ehci->lock);
 	if (HC_IS_RUNNING (hcd->state))
 	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.function = ehci_watchdog;
 	ehci->watchdog.data = (unsigned long) ehci;
 	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.
 	 * hw default: 1K periodic list heads, one per frame.
 	 * periodic_size can shrink by USBCMD update if hcc_params allows.
 	 * 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->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
 
 	ehci->reclaim = NULL;
 	ehci->reclaim = NULL;
+	ehci->reclaim_ready = 0;
 	ehci->next_uframe = -1;
 	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] */
 	/* complete the unlinking of some qh [4.15.2.3] */
 	if (status & STS_IAA) {
 	if (status & STS_IAA) {
 		COUNT (ehci->stats.reclaim);
 		COUNT (ehci->stats.reclaim);
-		end_unlink_async (ehci);
+		ehci->reclaim_ready = 1;
 		bh = 1;
 		bh = 1;
 	}
 	}
 
 
@@ -723,14 +708,10 @@ static int ehci_urb_enqueue (
 
 
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 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;
 		struct ehci_qh		*last;
 
 
 		for (last = ehci->reclaim;
 		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;
 		qh->qh_state = QH_STATE_UNLINK_WAIT;
 		last->reclaim = qh;
 		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);
 		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;
 		qh = (struct ehci_qh *) urb->hcpriv;
 		if (!qh)
 		if (!qh)
 			break;
 			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;
 		break;
 
 
 	case PIPE_INTERRUPT:
 	case PIPE_INTERRUPT:
@@ -867,7 +840,6 @@ rescan:
 		unlink_async (ehci, qh);
 		unlink_async (ehci, qh);
 		/* FALL THROUGH */
 		/* FALL THROUGH */
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
-	case QH_STATE_UNLINK_WAIT:
 idle_timeout:
 idle_timeout:
 		spin_unlock_irqrestore (&ehci->lock, flags);
 		spin_unlock_irqrestore (&ehci->lock, flags);
 		schedule_timeout_uninterruptible(1);
 		schedule_timeout_uninterruptible(1);

+ 1 - 1
drivers/usb/host/ehci-hub.c

@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 	}
 	}
 	ehci->command = readl (&ehci->regs->command);
 	ehci->command = readl (&ehci->regs->command);
 	if (ehci->reclaim)
 	if (ehci->reclaim)
-		end_unlink_async (ehci);
+		ehci->reclaim_ready = 1;
 	ehci_work(ehci);
 	ehci_work(ehci);
 
 
 	/* suspend any active/unsuspended ports, maybe allow wakeup */
 	/* suspend any active/unsuspended ports, maybe allow wakeup */

+ 1 - 1
drivers/usb/host/ehci-pci.c

@@ -303,7 +303,7 @@ restart:
 	/* emptying the schedule aborts any urbs */
 	/* emptying the schedule aborts any urbs */
 	spin_lock_irq(&ehci->lock);
 	spin_lock_irq(&ehci->lock);
 	if (ehci->reclaim)
 	if (ehci->reclaim)
-		end_unlink_async (ehci);
+		ehci->reclaim_ready = 1;
 	ehci_work(ehci);
 	ehci_work(ehci);
 	spin_unlock_irq(&ehci->lock);
 	spin_unlock_irq(&ehci->lock);
 
 

+ 4 - 2
drivers/usb/host/ehci-q.c

@@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
 	struct ehci_qh		*qh = ehci->reclaim;
 	struct ehci_qh		*qh = ehci->reclaim;
 	struct ehci_qh		*next;
 	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->hw_next = cpu_to_le32 (qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
 	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) */
 	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
 	next = qh->reclaim;
 	next = qh->reclaim;
 	ehci->reclaim = next;
 	ehci->reclaim = next;
+	ehci->reclaim_ready = 0;
 	qh->reclaim = NULL;
 	qh->reclaim = NULL;
 
 
 	qh_completions (ehci, qh);
 	qh_completions (ehci, qh);
@@ -1051,10 +1052,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 		return;
 		return;
 	}
 	}
 
 
+	ehci->reclaim_ready = 0;
 	cmd |= CMD_IAAD;
 	cmd |= CMD_IAAD;
 	writel (cmd, &ehci->regs->command);
 	writel (cmd, &ehci->regs->command);
 	(void) readl (&ehci->regs->command);
 	(void) readl (&ehci->regs->command);
-	iaa_watchdog_start (ehci);
+	timer_action (ehci, TIMER_IAA_WATCHDOG);
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/

+ 7 - 15
drivers/usb/host/ehci.h

@@ -58,6 +58,7 @@ struct ehci_hcd {			/* one per controller */
 	/* async schedule support */
 	/* async schedule support */
 	struct ehci_qh		*async;
 	struct ehci_qh		*async;
 	struct ehci_qh		*reclaim;
 	struct ehci_qh		*reclaim;
+	unsigned		reclaim_ready : 1;
 	unsigned		scanning : 1;
 	unsigned		scanning : 1;
 
 
 	/* periodic schedule support */
 	/* 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		*itd_pool;	/* itd per iso urb */
 	struct dma_pool		*sitd_pool;	/* sitd per split iso urb */
 	struct dma_pool		*sitd_pool;	/* sitd per split iso urb */
 
 
-	struct timer_list	iaa_watchdog;
 	struct timer_list	watchdog;
 	struct timer_list	watchdog;
 	unsigned long		actions;
 	unsigned long		actions;
 	unsigned		stamp;
 	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 {
 enum ehci_timer_action {
 	TIMER_IO_WATCHDOG,
 	TIMER_IO_WATCHDOG,
+	TIMER_IAA_WATCHDOG,
 	TIMER_ASYNC_SHRINK,
 	TIMER_ASYNC_SHRINK,
 	TIMER_ASYNC_OFF,
 	TIMER_ASYNC_OFF,
 };
 };
@@ -146,6 +134,9 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
 		unsigned long t;
 		unsigned long t;
 
 
 		switch (action) {
 		switch (action) {
+		case TIMER_IAA_WATCHDOG:
+			t = EHCI_IAA_JIFFIES;
+			break;
 		case TIMER_IO_WATCHDOG:
 		case TIMER_IO_WATCHDOG:
 			t = EHCI_IO_JIFFIES;
 			t = EHCI_IO_JIFFIES;
 			break;
 			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
 		// async queue SHRINK often precedes IAA.  while it's ready
 		// to go OFF neither can matter, and afterwards the IO
 		// to go OFF neither can matter, and afterwards the IO
 		// watchdog stops unless there's still periodic traffic.
 		// 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))
 				&& timer_pending (&ehci->watchdog))
 			return;
 			return;
 		mod_timer (&ehci->watchdog, t);
 		mod_timer (&ehci->watchdog, t);

+ 6 - 3
drivers/usb/host/ohci-pnx4008.c

@@ -262,6 +262,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
 	 */
 	 */
 	.start = ohci_pnx4008_start,
 	.start = ohci_pnx4008_start,
 	.stop = ohci_stop,
 	.stop = ohci_stop,
+	.shutdown = ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * 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_status_data = ohci_hub_status_data,
 	.hub_control = ohci_hub_control,
 	.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,
 	.start_port_reset = ohci_start_port_reset,
 };
 };
 
 
@@ -410,8 +415,6 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
 		goto out4;
 		goto out4;
 	}
 	}
 
 
-	hcd->self.hcpriv = (void *)hcd;
-
 	pnx4008_start_hc();
 	pnx4008_start_hc();
 	platform_set_drvdata(pdev, hcd);
 	platform_set_drvdata(pdev, hcd);
 	ohci = hcd_to_ohci(hcd);
 	ohci = hcd_to_ohci(hcd);

+ 39 - 5
drivers/usb/host/uhci-hcd.c

@@ -40,6 +40,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
 #include <linux/usb.h>
 #include <linux/usb.h>
 #include <linux/bitops.h>
 #include <linux/bitops.h>
+#include <linux/dmi.h>
 
 
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/io.h>
@@ -196,12 +197,42 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
 	return 0;
 	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)
 static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
 __releases(uhci->lock)
 __releases(uhci->lock)
 __acquires(uhci->lock)
 __acquires(uhci->lock)
 {
 {
 	int auto_stop;
 	int auto_stop;
-	int int_enable;
+	int int_enable, egsm_enable;
 
 
 	auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
 	auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
 	dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
 	dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
@@ -217,15 +248,18 @@ __acquires(uhci->lock)
 	}
 	}
 
 
 	/* Enable resume-detect interrupts if they work.
 	/* 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;
 	uhci->working_RD = 1;
 	int_enable = USBINTR_RESUME;
 	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;
 		uhci->working_RD = int_enable = 0;
-	}
+
 	outw(int_enable, uhci->io_addr + USBINTR);
 	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();
 	mb();
 	udelay(5);
 	udelay(5);
 
 

+ 0 - 10
drivers/usb/input/Kconfig

@@ -348,13 +348,3 @@ config USB_APPLETOUCH
 
 
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called appletouch.
 	  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.

+ 1 - 2
drivers/usb/input/Makefile

@@ -3,7 +3,7 @@
 #
 #
 
 
 # Multipart objects.
 # Multipart objects.
-wacom-objs	:= wacom_sys.o wacom_wac.o
+wacom-objs	:= wacom_wac.o wacom_sys.o
 usbhid-objs	:= hid-core.o
 usbhid-objs	:= hid-core.o
 
 
 # Optional parts of multipart objects.
 # Optional parts of multipart objects.
@@ -48,7 +48,6 @@ obj-$(CONFIG_USB_ACECAD)	+= acecad.o
 obj-$(CONFIG_USB_YEALINK)	+= yealink.o
 obj-$(CONFIG_USB_YEALINK)	+= yealink.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
 obj-$(CONFIG_USB_APPLETOUCH)	+= appletouch.o
 obj-$(CONFIG_USB_APPLETOUCH)	+= appletouch.o
-obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
 
 
 ifeq ($(CONFIG_USB_DEBUG),y)
 ifeq ($(CONFIG_USB_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
 EXTRA_CFLAGS += -DDEBUG

+ 17 - 7
drivers/usb/input/hid-core.c

@@ -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)
 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)
 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);
 }
 }
 
 
 /*
 /*

+ 2 - 2
drivers/usb/input/usbtouchscreen.c

@@ -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);
 	*x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
 	*y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 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;
 	*touch = ~pkt[7] & 0x20;
 
 
-	return 1;
+	return *touch;
 }
 }
 #endif
 #endif
 
 

+ 2 - 0
drivers/usb/input/wacom.h

@@ -63,6 +63,7 @@
  *      v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
  *      v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
  *		   - where wacom_sys.c deals with system specific code,
  *		   - where wacom_sys.c deals with system specific code,
  * 		   - and wacom_wac.c deals with Wacom 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 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_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_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_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_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);
 extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);

+ 12 - 8
drivers/usb/input/wacom_sys.c

@@ -110,7 +110,7 @@ __u16 wacom_be16_to_cpu(unsigned char *data)
 __u16 wacom_le16_to_cpu(unsigned char *data)
 __u16 wacom_le16_to_cpu(unsigned char *data)
 {
 {
 	__u16 value;
 	__u16 value;
-	value = be16_to_cpu(*(__be16 *) data);
+	value = le16_to_cpu(*(__le16 *) data);
 	return value;
 	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->evbit[0] |= BIT(EV_MSC);
 	input_dev->mscbit[0] |= BIT(MSC_SERIAL);
 	input_dev->mscbit[0] |= BIT(MSC_SERIAL);
 	input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
 	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)
 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);
 	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_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);
 	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);
 	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));
 	strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
 
 
 	wacom_wac->features = get_wacom_feature(id);
 	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;
 	input_dev->name = wacom_wac->features->name;
 	wacom->wacom_wac = wacom_wac;
 	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_fill_int_urb(wacom->irq, dev,
 			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
 			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
 			 wacom_wac->data, wacom_wac->features->pktlen,
 			 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_dma = wacom->data_dma;
 	wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 	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);
 		input_unregister_device(wacom->dev);
 		usb_free_urb(wacom->irq);
 		usb_free_urb(wacom->irq);
 		usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
 		usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
-		kfree(wacom);
 		kfree(wacom->wacom_wac);
 		kfree(wacom->wacom_wac);
+		kfree(wacom);
 	}
 	}
 }
 }
 
 

+ 66 - 55
drivers/usb/input/wacom_wac.c

@@ -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_LEFT, data[1] & 0x01);
 				wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
 				wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
 				if (wacom->features->type == WACOM_G4)
 				if (wacom->features->type == WACOM_G4)
-					wacom_report_abs(wcombo, ABS_DISTANCE, data[6]);
+					wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f);
 				else
 				else
-					wacom_report_abs(wcombo, ABS_DISTANCE, data[7]);
+					wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
 				break;
 				break;
 		}
 		}
 	}
 	}
@@ -303,8 +303,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
 				wacom->tool[idx] = BTN_TOOL_PEN;
 				wacom->tool[idx] = BTN_TOOL_PEN;
 		}
 		}
 		/* only large I3 support Lens Cursor */
 		/* 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_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
 			wacom_report_key(wcombo, wacom->tool[idx], 1);
 			wacom_report_key(wcombo, wacom->tool[idx], 1);
 			wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
 			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 */
 	/* Exit report */
 	if ((data[1] & 0xfe) == 0x80) {
 	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;
 	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_RX, ((data[1] & 0x1f) << 8) | data[2]);
 		wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
 		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);
 			wacom_report_key(wcombo, wacom->tool[1], 1);
 		else
 		else
 			wacom_report_key(wcombo, wacom->tool[1], 0);
 			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);
 					((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 */
 			/* 4D mouse packet */
 			wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x01);
 			wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x01);
 			wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
 			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));
 						 - ((data[8] & 0x02) >> 1));
 
 
 			/* I3 2D mouse side buttons */
 			/* 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_SIDE,   data[8] & 0x40);
 				wacom_report_key(wcombo, BTN_EXTRA,  data[8] & 0x20);
 				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 */
 			/* Lens cursor packets */
 			wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x01);
 			wacom_report_key(wcombo, BTN_LEFT,   data[8] & 0x01);
 			wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
 			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));
 			return (wacom_ptu_irq(wacom_wac, wcombo));
 			break;
 			break;
 		case INTUOS:
 		case INTUOS:
+		case INTUOS3S:
 		case INTUOS3:
 		case INTUOS3:
 		case INTUOS3L:
 		case INTUOS3L:
 		case CINTIQ:
 		case CINTIQ:
@@ -515,6 +522,8 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
 		case CINTIQ:
 		case CINTIQ:
 			input_dev_i3(input_dev, wacom_wac);
 			input_dev_i3(input_dev, wacom_wac);
 			/* fall through */
 			/* fall through */
+		case INTUOS3S:
+			input_dev_i3s(input_dev, wacom_wac);
 		case INTUOS:
 		case INTUOS:
 			input_dev_i(input_dev, wacom_wac);
 			input_dev_i(input_dev, wacom_wac);
 			break;
 			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[] = {
 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, 0xB3) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
 	{ 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, 0x3F) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
 	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
 	{ }
 	{ }

+ 1 - 1
drivers/usb/input/wacom_wac.h

@@ -20,6 +20,7 @@ enum {
 	PTU,
 	PTU,
 	PL,
 	PL,
 	INTUOS,
 	INTUOS,
+	INTUOS3S,
 	INTUOS3,
 	INTUOS3,
 	INTUOS3L,
 	INTUOS3L,
 	CINTIQ,
 	CINTIQ,
@@ -34,7 +35,6 @@ struct wacom_features {
 	int pressure_max;
 	int pressure_max;
 	int distance_max;
 	int distance_max;
 	int type;
 	int type;
-	usb_complete_t irq;
 };
 };
 
 
 struct wacom_wac {
 struct wacom_wac {

+ 95 - 44
drivers/usb/input/xpad.c

@@ -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>
  * 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
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * modify it under the terms of the GNU General Public License as
@@ -30,9 +31,10 @@
  *  - Greg Kroah-Hartman - usb-skeleton driver
  *  - Greg Kroah-Hartman - usb-skeleton driver
  *
  *
  * TODO:
  * TODO:
- *  - fine tune axes
+ *  - fine tune axes (especially trigger axes)
  *  - fix "analog" buttons (reported as digital now)
  *  - fix "analog" buttons (reported as digital now)
  *  - get rumble working
  *  - get rumble working
+ *  - need USB IDs for other dance pads
  *
  *
  * History:
  * History:
  *
  *
@@ -57,25 +59,40 @@
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/stat.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/smp_lock.h>
 #include <linux/smp_lock.h>
 #include <linux/usb/input.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_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
 #define DRIVER_DESC "X-Box pad driver"
 #define DRIVER_DESC "X-Box pad driver"
 
 
 #define XPAD_PKT_LEN 32
 #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 {
 static const struct xpad_device {
 	u16 idVendor;
 	u16 idVendor;
 	u16 idProduct;
 	u16 idProduct;
 	char *name;
 	char *name;
+	u8 dpad_mapping;
 } xpad_device[] = {
 } 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[] = {
 static const signed short xpad_btn[] = {
@@ -84,11 +101,23 @@ static const signed short xpad_btn[] = {
 	-1						/* terminating entry */
 	-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[] = {
 static const signed short xpad_abs[] = {
 	ABS_X, ABS_Y,		/* left stick */
 	ABS_X, ABS_Y,		/* left stick */
 	ABS_RX, ABS_RY,		/* right stick */
 	ABS_RX, ABS_RY,		/* right stick */
 	ABS_Z, ABS_RZ,		/* triggers left/right */
 	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 */
 	-1			/* terminating entry */
 };
 };
 
 
@@ -100,14 +129,16 @@ static struct usb_device_id xpad_table [] = {
 MODULE_DEVICE_TABLE (usb, xpad_table);
 MODULE_DEVICE_TABLE (usb, xpad_table);
 
 
 struct usb_xpad {
 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;
 	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]);
 	input_report_abs(dev, ABS_RZ, data[11]);
 
 
 	/* digital pad */
 	/* 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 */
 	/* 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 */
 	/* "analog" buttons A, B, X, Y */
 	input_report_key(dev, BTN_A, data[4]);
 	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);
 	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)
 static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
 {
 	struct usb_device *udev = interface_to_usbdev (intf);
 	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;
 		goto fail2;
 
 
 	xpad->udev = udev;
 	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;
 	xpad->dev = input_dev;
 	usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
 	usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
 	strlcat(xpad->phys, "/input0", 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);
 	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
 
 
+	/* set up buttons */
 	for (i = 0; xpad_btn[i] >= 0; i++)
 	for (i = 0; xpad_btn[i] >= 0; i++)
 		set_bit(xpad_btn[i], input_dev->keybit);
 		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;
 	ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
 	usb_fill_int_urb(xpad->irq_in, udev,
 	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);
 		usb_kill_urb(xpad->irq_in);
 		input_unregister_device(xpad->dev);
 		input_unregister_device(xpad->dev);
 		usb_free_urb(xpad->irq_in);
 		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);
 		kfree(xpad);
 	}
 	}
 }
 }

+ 10 - 0
drivers/usb/misc/Kconfig

@@ -223,6 +223,16 @@ config USB_LD
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called ldusb.
 	  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
 config USB_TEST
 	tristate "USB testing driver (DEVELOPMENT)"
 	tristate "USB testing driver (DEVELOPMENT)"
 	depends on USB && USB_DEVICEFS && EXPERIMENTAL
 	depends on USB && USB_DEVICEFS && EXPERIMENTAL

+ 1 - 0
drivers/usb/misc/Makefile

@@ -21,6 +21,7 @@ obj-$(CONFIG_USB_PHIDGETMOTORCONTROL)	+= phidgetmotorcontrol.o
 obj-$(CONFIG_USB_PHIDGETSERVO)	+= phidgetservo.o
 obj-$(CONFIG_USB_PHIDGETSERVO)	+= phidgetservo.o
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
 obj-$(CONFIG_USB_TEST)		+= usbtest.o
 obj-$(CONFIG_USB_TEST)		+= usbtest.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 
 
 obj-$(CONFIG_USB_SISUSBVGA)	+= sisusbvga/
 obj-$(CONFIG_USB_SISUSBVGA)	+= sisusbvga/

+ 2 - 1
drivers/usb/misc/adutux.c

@@ -370,7 +370,8 @@ static int adu_release(struct inode *inode, struct file *file)
 	retval = adu_release_internal(dev);
 	retval = adu_release_internal(dev);
 
 
 exit:
 exit:
-	up(&dev->sem);
+	if (dev)
+		up(&dev->sem);
 	dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
 	dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
 	return retval;
 	return retval;
 }
 }

+ 11 - 25
drivers/usb/misc/ftdi-elan.c

@@ -513,8 +513,6 @@ static void ftdi_elan_respond_work(void *data)
                         ftdi->disconnected += 1;
                         ftdi->disconnected += 1;
                 } else if (retval == -ENODEV) {
                 } else if (retval == -ENODEV) {
                         ftdi->disconnected += 1;
                         ftdi->disconnected += 1;
-                } else if (retval == -ENODEV) {
-                        ftdi->disconnected += 1;
                 } else if (retval == -EILSEQ) {
                 } else if (retval == -EILSEQ) {
                         ftdi->disconnected += 1;
                         ftdi->disconnected += 1;
                 } else {
                 } else {
@@ -1186,11 +1184,8 @@ static ssize_t ftdi_elan_write(struct file *file,
         int retval = 0;
         int retval = 0;
         struct urb *urb;
         struct urb *urb;
         char *buf;
         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) {
         if (ftdi->disconnected > 0) {
                 return -ENODEV;
                 return -ENODEV;
         }
         }
@@ -1220,27 +1215,18 @@ static ssize_t ftdi_elan_write(struct file *file,
         if (retval) {
         if (retval) {
                 dev_err(&ftdi->udev->dev, "failed submitting write urb, error %"
                 dev_err(&ftdi->udev->dev, "failed submitting write urb, error %"
                         "d\n", retval);
                         "d\n", retval);
-                goto error_4;
+                goto error_3;
         }
         }
         usb_free_urb(urb);
         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;
         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 = {
 static struct file_operations ftdi_elan_fops = {

+ 0 - 0
drivers/usb/input/trancevibrator.c → drivers/usb/misc/trancevibrator.c


+ 8 - 0
drivers/usb/net/Kconfig

@@ -207,6 +207,14 @@ config USB_NET_PLUSB
 	  Choose this option if you're using a host-to-host cable
 	  Choose this option if you're using a host-to-host cable
 	  with one of these chips.
 	  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
 config USB_NET_RNDIS_HOST
 	tristate "Host for RNDIS devices (EXPERIMENTAL)"
 	tristate "Host for RNDIS devices (EXPERIMENTAL)"
 	depends on USB_USBNET && EXPERIMENTAL
 	depends on USB_USBNET && EXPERIMENTAL

+ 1 - 0
drivers/usb/net/Makefile

@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_NET_PLUSB)	+= plusb.o
 obj-$(CONFIG_USB_NET_RNDIS_HOST)	+= rndis_host.o
 obj-$(CONFIG_USB_NET_RNDIS_HOST)	+= rndis_host.o
 obj-$(CONFIG_USB_NET_CDC_SUBSET)	+= cdc_subset.o
 obj-$(CONFIG_USB_NET_CDC_SUBSET)	+= cdc_subset.o
 obj-$(CONFIG_USB_NET_ZAURUS)	+= zaurus.o
 obj-$(CONFIG_USB_NET_ZAURUS)	+= zaurus.o
+obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 
 
 ifeq ($(CONFIG_USB_DEBUG),y)
 ifeq ($(CONFIG_USB_DEBUG),y)

+ 13 - 35
drivers/usb/net/asix.c

@@ -569,10 +569,12 @@ static int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
 	struct usbnet *dev = netdev_priv(netdev);
 	struct usbnet *dev = netdev_priv(netdev);
 	u16 res;
 	u16 res;
 
 
+	mutex_lock(&dev->phy_mutex);
 	asix_set_sw_mii(dev);
 	asix_set_sw_mii(dev);
 	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
 	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
 				(__u16)loc, 2, (u16 *)&res);
 				(__u16)loc, 2, (u16 *)&res);
 	asix_set_hw_mii(dev);
 	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));
 	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);
 	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);
 	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_set_sw_mii(dev);
 	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
 	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
 				(__u16)loc, 2, (u16 *)&res);
 				(__u16)loc, 2, (u16 *)&res);
 	asix_set_hw_mii(dev);
 	asix_set_hw_mii(dev);
+	mutex_unlock(&dev->phy_mutex);
 }
 }
 
 
 /* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
 /* 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;
 	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)
 static u32 asix_get_link(struct net_device *net)
 {
 {
 	struct usbnet *dev = netdev_priv(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 = {
 static struct ethtool_ops ax88172_ethtool_ops = {
 	.get_drvinfo		= asix_get_drvinfo,
 	.get_drvinfo		= asix_get_drvinfo,
 	.get_link		= asix_get_link,
 	.get_link		= asix_get_link,
-	.nway_reset		= asix_nway_reset,
 	.get_msglevel		= usbnet_get_msglevel,
 	.get_msglevel		= usbnet_get_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.get_wol		= asix_get_wol,
 	.get_wol		= asix_get_wol,
 	.set_wol		= asix_set_wol,
 	.set_wol		= asix_set_wol,
 	.get_eeprom_len		= asix_get_eeprom_len,
 	.get_eeprom_len		= asix_get_eeprom_len,
 	.get_eeprom		= asix_get_eeprom,
 	.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)
 static void ax88172_set_multicast(struct net_device *net)
@@ -885,15 +863,15 @@ out1:
 static struct ethtool_ops ax88772_ethtool_ops = {
 static struct ethtool_ops ax88772_ethtool_ops = {
 	.get_drvinfo		= asix_get_drvinfo,
 	.get_drvinfo		= asix_get_drvinfo,
 	.get_link		= asix_get_link,
 	.get_link		= asix_get_link,
-	.nway_reset		= asix_nway_reset,
 	.get_msglevel		= usbnet_get_msglevel,
 	.get_msglevel		= usbnet_get_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.get_wol		= asix_get_wol,
 	.get_wol		= asix_get_wol,
 	.set_wol		= asix_set_wol,
 	.set_wol		= asix_set_wol,
 	.get_eeprom_len		= asix_get_eeprom_len,
 	.get_eeprom_len		= asix_get_eeprom_len,
 	.get_eeprom		= asix_get_eeprom,
 	.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)
 static int ax88772_link_reset(struct usbnet *dev)
@@ -1046,15 +1024,15 @@ out1:
 static struct ethtool_ops ax88178_ethtool_ops = {
 static struct ethtool_ops ax88178_ethtool_ops = {
 	.get_drvinfo		= asix_get_drvinfo,
 	.get_drvinfo		= asix_get_drvinfo,
 	.get_link		= asix_get_link,
 	.get_link		= asix_get_link,
-	.nway_reset		= asix_nway_reset,
 	.get_msglevel		= usbnet_get_msglevel,
 	.get_msglevel		= usbnet_get_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.set_msglevel		= usbnet_set_msglevel,
 	.get_wol		= asix_get_wol,
 	.get_wol		= asix_get_wol,
 	.set_wol		= asix_set_wol,
 	.set_wol		= asix_set_wol,
 	.get_eeprom_len		= asix_get_eeprom_len,
 	.get_eeprom_len		= asix_get_eeprom_len,
 	.get_eeprom		= asix_get_eeprom,
 	.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)
 static int marvell_phy_init(struct usbnet *dev)

+ 1 - 1
drivers/usb/net/cdc_ether.c

@@ -498,7 +498,7 @@ static struct usb_driver cdc_driver = {
 
 
 static int __init cdc_init(void)
 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)));
 			< sizeof(struct cdc_state)));
 
 
  	return usb_register(&cdc_driver);
  	return usb_register(&cdc_driver);

+ 129 - 75
drivers/usb/net/kaweth.c

@@ -65,16 +65,6 @@
 
 
 #undef DEBUG
 #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"
 #include "kawethfw.h"
 
 
 #define KAWETH_MTU			1514
 #define KAWETH_MTU			1514
@@ -86,6 +76,9 @@
 
 
 #define KAWETH_STATUS_BROKEN		0x0000001
 #define KAWETH_STATUS_BROKEN		0x0000001
 #define KAWETH_STATUS_CLOSING		0x0000002
 #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_PROMISCUOUS	0x01
 #define KAWETH_PACKET_FILTER_ALL_MULTICAST	0x02
 #define KAWETH_PACKET_FILTER_ALL_MULTICAST	0x02
@@ -112,6 +105,8 @@
 #define STATE_MASK				0x40
 #define STATE_MASK				0x40
 #define	STATE_SHIFT				5
 #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_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");
 MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver");
@@ -128,6 +123,8 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,
 				       unsigned int pipe,
 				       unsigned int pipe,
 				       struct usb_ctrlrequest *cmd, void *data,
 				       struct usb_ctrlrequest *cmd, void *data,
 				       int len, int timeout);
 				       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
  *     usb_device_id
@@ -179,6 +176,8 @@ static struct usb_driver kaweth_driver = {
 	.name =		driver_name,
 	.name =		driver_name,
 	.probe =	kaweth_probe,
 	.probe =	kaweth_probe,
 	.disconnect =	kaweth_disconnect,
 	.disconnect =	kaweth_disconnect,
+	.suspend =	kaweth_suspend,
+	.resume =	kaweth_resume,
 	.id_table =     usb_klsi_table,
 	.id_table =     usb_klsi_table,
 };
 };
 
 
@@ -222,6 +221,7 @@ struct kaweth_device
 	int suspend_lowmem_rx;
 	int suspend_lowmem_rx;
 	int suspend_lowmem_ctrl;
 	int suspend_lowmem_ctrl;
 	int linkstate;
 	int linkstate;
+	int opened;
 	struct work_struct lowmem_work;
 	struct work_struct lowmem_work;
 
 
 	struct usb_device *dev;
 	struct usb_device *dev;
@@ -265,17 +265,17 @@ static int kaweth_control(struct kaweth_device *kaweth,
 {
 {
 	struct usb_ctrlrequest *dr;
 	struct usb_ctrlrequest *dr;
 
 
-	kaweth_dbg("kaweth_control()");
+	dbg("kaweth_control()");
 
 
 	if(in_interrupt()) {
 	if(in_interrupt()) {
-		kaweth_dbg("in_interrupt()");
+		dbg("in_interrupt()");
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 
 
 	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
 	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
 
 
 	if (!dr) {
 	if (!dr) {
-		kaweth_dbg("kmalloc() failed");
+		dbg("kmalloc() failed");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
@@ -300,7 +300,7 @@ static int kaweth_read_configuration(struct kaweth_device *kaweth)
 {
 {
 	int retval;
 	int retval;
 
 
-	kaweth_dbg("Reading kaweth configuration");
+	dbg("Reading kaweth configuration");
 
 
 	retval = kaweth_control(kaweth,
 	retval = kaweth_control(kaweth,
 				usb_rcvctrlpipe(kaweth->dev, 0),
 				usb_rcvctrlpipe(kaweth->dev, 0),
@@ -322,7 +322,7 @@ static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size)
 {
 {
 	int retval;
 	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,
 	retval = kaweth_control(kaweth,
 				usb_sndctrlpipe(kaweth->dev, 0),
 				usb_sndctrlpipe(kaweth->dev, 0),
@@ -344,7 +344,7 @@ static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait)
 {
 {
 	int retval;
 	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,
 	retval = kaweth_control(kaweth,
 				usb_sndctrlpipe(kaweth->dev, 0),
 				usb_sndctrlpipe(kaweth->dev, 0),
@@ -367,7 +367,7 @@ static int kaweth_set_receive_filter(struct kaweth_device *kaweth,
 {
 {
 	int retval;
 	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,
 	retval = kaweth_control(kaweth,
 				usb_sndctrlpipe(kaweth->dev, 0),
 				usb_sndctrlpipe(kaweth->dev, 0),
@@ -392,7 +392,7 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth,
 				    __u8 type)
 				    __u8 type)
 {
 {
 	if(data_len > KAWETH_FIRMWARE_BUF_SIZE)	{
 	if(data_len > KAWETH_FIRMWARE_BUF_SIZE)	{
-		kaweth_err("Firmware too big: %d", data_len);
+		err("Firmware too big: %d", data_len);
 		return -ENOSPC;
 		return -ENOSPC;
 	}
 	}
 
 
@@ -403,13 +403,13 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth,
 	kaweth->firmware_buf[4] = type;
 	kaweth->firmware_buf[4] = type;
 	kaweth->firmware_buf[5] = interrupt;
 	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->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,
 	    data,
 	    kaweth);
 	    kaweth);
-	kaweth_dbg("Firmware length: %d", data_len);
+	dbg("Firmware length: %d", data_len);
 
 
 	return kaweth_control(kaweth,
 	return kaweth_control(kaweth,
 		              usb_sndctrlpipe(kaweth->dev, 0),
 		              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[6] = 0x00;
 	kaweth->firmware_buf[7] = 0x00;
 	kaweth->firmware_buf[7] = 0x00;
 
 
-	kaweth_dbg("Triggering firmware");
+	dbg("Triggering firmware");
 
 
 	return kaweth_control(kaweth,
 	return kaweth_control(kaweth,
 			      usb_sndctrlpipe(kaweth->dev, 0),
 			      usb_sndctrlpipe(kaweth->dev, 0),
@@ -457,7 +457,7 @@ static int kaweth_reset(struct kaweth_device *kaweth)
 {
 {
 	int result;
 	int result;
 
 
-	kaweth_dbg("kaweth_reset(%p)", kaweth);
+	dbg("kaweth_reset(%p)", kaweth);
 	result = kaweth_control(kaweth,
 	result = kaweth_control(kaweth,
 				usb_sndctrlpipe(kaweth->dev, 0),
 				usb_sndctrlpipe(kaweth->dev, 0),
 				USB_REQ_SET_CONFIGURATION,
 				USB_REQ_SET_CONFIGURATION,
@@ -470,7 +470,7 @@ static int kaweth_reset(struct kaweth_device *kaweth)
 
 
 	mdelay(10);
 	mdelay(10);
 
 
-	kaweth_dbg("kaweth_reset() returns %d.",result);
+	dbg("kaweth_reset() returns %d.",result);
 
 
 	return result;
 	return result;
 }
 }
@@ -534,7 +534,7 @@ static void kaweth_resubmit_tl(void *d)
 {
 {
 	struct kaweth_device *kaweth = (struct kaweth_device *)d;
 	struct kaweth_device *kaweth = (struct kaweth_device *)d;
 
 
-	if (kaweth->status | KAWETH_STATUS_CLOSING)
+	if (IS_BLOCKED(kaweth->status))
 		return;
 		return;
 
 
 	if (kaweth->suspend_lowmem_rx)
 	if (kaweth->suspend_lowmem_rx)
@@ -568,7 +568,7 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
 			kaweth->suspend_lowmem_rx = 1;
 			kaweth->suspend_lowmem_rx = 1;
 			schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
 			schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
 		}
 		}
-		kaweth_err("resubmitting rx_urb %d failed", result);
+		err("resubmitting rx_urb %d failed", result);
 	} else {
 	} else {
 		kaweth->suspend_lowmem_rx = 0;
 		kaweth->suspend_lowmem_rx = 0;
 	}
 	}
@@ -601,11 +601,15 @@ static void kaweth_usb_receive(struct urb *urb)
 		return;
 		return;
 	}
 	}
 
 
-	if (kaweth->status & KAWETH_STATUS_CLOSING)
+	spin_lock(&kaweth->device_lock);
+	if (IS_BLOCKED(kaweth->status)) {
+		spin_unlock(&kaweth->device_lock);
 		return;
 		return;
+	}
+	spin_unlock(&kaweth->device_lock);
 
 
 	if(urb->status && urb->status != -EREMOTEIO && count != 1) {
 	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,
                            net->name,
 			   urb->status,
 			   urb->status,
 			   count,
 			   count,
@@ -616,9 +620,9 @@ static void kaweth_usb_receive(struct urb *urb)
 
 
 	if(kaweth->net && (count > 2)) {
 	if(kaweth->net && (count > 2)) {
 		if(pkt_len > (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);
 		        kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
                         return;
                         return;
                 }
                 }
@@ -655,7 +659,7 @@ static int kaweth_open(struct net_device *net)
 	struct kaweth_device *kaweth = netdev_priv(net);
 	struct kaweth_device *kaweth = netdev_priv(net);
 	int res;
 	int res;
 
 
-	kaweth_dbg("Opening network device.");
+	dbg("Opening network device.");
 
 
 	res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL);
 	res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL);
 	if (res)
 	if (res)
@@ -678,6 +682,7 @@ static int kaweth_open(struct net_device *net)
 		usb_kill_urb(kaweth->rx_urb);
 		usb_kill_urb(kaweth->rx_urb);
 		return -EIO;
 		return -EIO;
 	}
 	}
+	kaweth->opened = 1;
 
 
 	netif_start_queue(net);
 	netif_start_queue(net);
 
 
@@ -688,14 +693,8 @@ static int kaweth_open(struct net_device *net)
 /****************************************************************
 /****************************************************************
  *     kaweth_close
  *     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->irq_urb);
 	usb_kill_urb(kaweth->rx_urb);
 	usb_kill_urb(kaweth->rx_urb);
 	usb_kill_urb(kaweth->tx_urb);
 	usb_kill_urb(kaweth->tx_urb);
@@ -706,6 +705,21 @@ static int kaweth_close(struct net_device *net)
 	   we hit them again */
 	   we hit them again */
 	usb_kill_urb(kaweth->irq_urb);
 	usb_kill_urb(kaweth->irq_urb);
 	usb_kill_urb(kaweth->rx_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;
 	kaweth->status &= ~KAWETH_STATUS_CLOSING;
 
 
@@ -732,7 +746,7 @@ static void kaweth_usb_transmit_complete(struct urb *urb)
 
 
 	if (unlikely(urb->status != 0))
 	if (unlikely(urb->status != 0))
 		if (urb->status != -ENOENT)
 		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);
 	netif_wake_queue(kaweth->net);
 	dev_kfree_skb_irq(skb);
 	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);
 	kaweth_async_set_rx_mode(kaweth);
 	netif_stop_queue(net);
 	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 */
 	/* We now decide whether we can put our special header into the sk_buff */
 	if (skb_cloned(skb) || skb_headroom(skb) < 2) {
 	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)))
 	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++;
 		kaweth->stats.tx_errors++;
 
 
 		netif_start_queue(net);
 		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_BROADCAST |
 		                     KAWETH_PACKET_FILTER_MULTICAST;
 		                     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);
 	netif_stop_queue(net);
 
 
@@ -850,10 +868,10 @@ static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)
 				KAWETH_CONTROL_TIMEOUT);
 				KAWETH_CONTROL_TIMEOUT);
 
 
 	if(result < 0) {
 	if(result < 0) {
-		kaweth_err("Failed to set Rx mode: %d", result);
+		err("Failed to set Rx mode: %d", result);
 	}
 	}
 	else {
 	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);
 	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++;
 	kaweth->stats.tx_errors++;
 	net->trans_start = jiffies;
 	net->trans_start = jiffies;
 
 
 	usb_unlink_urb(kaweth->tx_urb);
 	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
  *     kaweth_probe
  ****************************************************************/
  ****************************************************************/
@@ -895,15 +949,15 @@ static int kaweth_probe(
 	const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 	const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 	int result = 0;
 	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,
 		 dev->devnum,
 		 le16_to_cpu(dev->descriptor.idVendor),
 		 le16_to_cpu(dev->descriptor.idVendor),
 		 le16_to_cpu(dev->descriptor.idProduct),
 		 le16_to_cpu(dev->descriptor.idProduct),
 		 le16_to_cpu(dev->descriptor.bcdDevice));
 		 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.bLength,
 		 (int)dev->descriptor.bDescriptorType);
 		 (int)dev->descriptor.bDescriptorType);
 
 
@@ -918,7 +972,7 @@ static int kaweth_probe(
 	spin_lock_init(&kaweth->device_lock);
 	spin_lock_init(&kaweth->device_lock);
 	init_waitqueue_head(&kaweth->term_wait);
 	init_waitqueue_head(&kaweth->term_wait);
 
 
-	kaweth_dbg("Resetting.");
+	dbg("Resetting.");
 
 
 	kaweth_reset(kaweth);
 	kaweth_reset(kaweth);
 
 
@@ -928,17 +982,17 @@ static int kaweth_probe(
 	 */
 	 */
 
 
 	if (le16_to_cpu(dev->descriptor.bcdDevice) >> 8) {
 	if (le16_to_cpu(dev->descriptor.bcdDevice) >> 8) {
-		kaweth_info("Firmware present in device.");
+		info("Firmware present in device.");
 	} else {
 	} else {
 		/* Download the firmware */
 		/* Download the firmware */
-		kaweth_info("Downloading firmware...");
+		info("Downloading firmware...");
 		kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL);
 		kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL);
 		if ((result = kaweth_download_firmware(kaweth,
 		if ((result = kaweth_download_firmware(kaweth,
 						      kaweth_new_code,
 						      kaweth_new_code,
 						      len_kaweth_new_code,
 						      len_kaweth_new_code,
 						      100,
 						      100,
 						      2)) < 0) {
 						      2)) < 0) {
-			kaweth_err("Error downloading firmware (%d)", result);
+			err("Error downloading firmware (%d)", result);
 			goto err_fw;
 			goto err_fw;
 		}
 		}
 
 
@@ -947,7 +1001,7 @@ static int kaweth_probe(
 						      len_kaweth_new_code_fix,
 						      len_kaweth_new_code_fix,
 						      100,
 						      100,
 						      3)) < 0) {
 						      3)) < 0) {
-			kaweth_err("Error downloading firmware fix (%d)", result);
+			err("Error downloading firmware fix (%d)", result);
 			goto err_fw;
 			goto err_fw;
 		}
 		}
 
 
@@ -956,7 +1010,7 @@ static int kaweth_probe(
 						      len_kaweth_trigger_code,
 						      len_kaweth_trigger_code,
 						      126,
 						      126,
 						      2)) < 0) {
 						      2)) < 0) {
-			kaweth_err("Error downloading trigger code (%d)", result);
+			err("Error downloading trigger code (%d)", result);
 			goto err_fw;
 			goto err_fw;
 
 
 		}
 		}
@@ -966,18 +1020,18 @@ static int kaweth_probe(
 						      len_kaweth_trigger_code_fix,
 						      len_kaweth_trigger_code_fix,
 						      126,
 						      126,
 						      3)) < 0) {
 						      3)) < 0) {
-			kaweth_err("Error downloading trigger code fix (%d)", result);
+			err("Error downloading trigger code fix (%d)", result);
 			goto err_fw;
 			goto err_fw;
 		}
 		}
 
 
 
 
 		if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) {
 		if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) {
-			kaweth_err("Error triggering firmware (%d)", result);
+			err("Error triggering firmware (%d)", result);
 			goto err_fw;
 			goto err_fw;
 		}
 		}
 
 
 		/* Device will now disappear for a moment...  */
 		/* Device will now disappear for a moment...  */
-		kaweth_info("Firmware loaded.  I'll be back...");
+		info("Firmware loaded.  I'll be back...");
 err_fw:
 err_fw:
 		free_page((unsigned long)kaweth->firmware_buf);
 		free_page((unsigned long)kaweth->firmware_buf);
 		free_netdev(netdev);
 		free_netdev(netdev);
@@ -987,14 +1041,14 @@ err_fw:
 	result = kaweth_read_configuration(kaweth);
 	result = kaweth_read_configuration(kaweth);
 
 
 	if(result < 0) {
 	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;
 		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[0],
 		 (int)kaweth->configuration.hw_addr[1],
 		 (int)kaweth->configuration.hw_addr[1],
 		 (int)kaweth->configuration.hw_addr[2],
 		 (int)kaweth->configuration.hw_addr[2],
@@ -1005,17 +1059,17 @@ err_fw:
 	if(!memcmp(&kaweth->configuration.hw_addr,
 	if(!memcmp(&kaweth->configuration.hw_addr,
                    &bcast_addr,
                    &bcast_addr,
 		   sizeof(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;
 		goto err_free_netdev;
 	}
 	}
 
 
 	if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) {
 	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;
 		goto err_free_netdev;
 	}
 	}
 
 
 	if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) {
 	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;
 		goto err_free_netdev;
 	}
 	}
 
 
@@ -1025,11 +1079,11 @@ err_fw:
                                            KAWETH_PACKET_FILTER_MULTICAST);
                                            KAWETH_PACKET_FILTER_MULTICAST);
 
 
 	if(result < 0) {
 	if(result < 0) {
-		kaweth_err("Error setting receive filter");
+		err("Error setting receive filter");
 		goto err_free_netdev;
 		goto err_free_netdev;
 	}
 	}
 
 
-	kaweth_dbg("Initializing net device.");
+	dbg("Initializing net device.");
 
 
 	kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 	kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!kaweth->tx_urb)
 	if (!kaweth->tx_urb)
@@ -1086,13 +1140,13 @@ err_fw:
 
 
 	SET_NETDEV_DEV(netdev, &intf->dev);
 	SET_NETDEV_DEV(netdev, &intf->dev);
 	if (register_netdev(netdev) != 0) {
 	if (register_netdev(netdev) != 0) {
-		kaweth_err("Error registering netdev.");
+		err("Error registering netdev.");
 		goto err_intfdata;
 		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;
 	return 0;
 
 
@@ -1121,16 +1175,16 @@ static void kaweth_disconnect(struct usb_interface *intf)
 	struct kaweth_device *kaweth = usb_get_intfdata(intf);
 	struct kaweth_device *kaweth = usb_get_intfdata(intf);
 	struct net_device *netdev;
 	struct net_device *netdev;
 
 
-	kaweth_info("Unregistering");
+	info("Unregistering");
 
 
 	usb_set_intfdata(intf, NULL);
 	usb_set_intfdata(intf, NULL);
 	if (!kaweth) {
 	if (!kaweth) {
-		kaweth_warn("unregistering non-existant device");
+		warn("unregistering non-existant device");
 		return;
 		return;
 	}
 	}
 	netdev = kaweth->net;
 	netdev = kaweth->net;
 
 
-	kaweth_dbg("Unregistering net device");
+	dbg("Unregistering net device");
 	unregister_netdev(netdev);
 	unregister_netdev(netdev);
 
 
 	usb_free_urb(kaweth->rx_urb);
 	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)) {
 	if (!wait_event_timeout(awd.wqh, awd.done, timeout)) {
                 // timeout
                 // timeout
-                kaweth_warn("usb_control/bulk_msg: timeout");
+                warn("usb_control/bulk_msg: timeout");
                 usb_kill_urb(urb);  // remove urb safely
                 usb_kill_urb(urb);  // remove urb safely
                 status = -ETIMEDOUT;
                 status = -ETIMEDOUT;
         }
         }
@@ -1234,7 +1288,7 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,
  ****************************************************************/
  ****************************************************************/
 static int __init kaweth_init(void)
 static int __init kaweth_init(void)
 {
 {
-	kaweth_dbg("Driver loading");
+	dbg("Driver loading");
 	return usb_register(&kaweth_driver);
 	return usb_register(&kaweth_driver);
 }
 }
 
 

+ 534 - 0
drivers/usb/net/mcs7830.c

@@ -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");

+ 53 - 2
drivers/usb/net/usbnet.c

@@ -669,6 +669,37 @@ done:
  * they'll probably want to use this base set.
  * 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)
 void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
 {
 {
 	struct usbnet *dev = netdev_priv(net);
 	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);
 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);
 	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)
 	if (dev->driver_info->check_connect)
 		return dev->driver_info->check_connect (dev) == 0;
 		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) */
 	/* Otherwise, say we're up (to avoid breaking scripts) */
 	return 1;
 	return 1;
 }
 }
+EXPORT_SYMBOL_GPL(usbnet_get_link);
 
 
 u32 usbnet_get_msglevel (struct net_device *net)
 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);
 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 */
 /* drivers may override default ethtool_ops in their bind() routine */
 static struct ethtool_ops usbnet_ethtool_ops = {
 static struct ethtool_ops usbnet_ethtool_ops = {
+	.get_settings		= usbnet_get_settings,
+	.set_settings		= usbnet_set_settings,
 	.get_drvinfo		= usbnet_get_drvinfo,
 	.get_drvinfo		= usbnet_get_drvinfo,
 	.get_link		= usbnet_get_link,
 	.get_link		= usbnet_get_link,
+	.nway_reset		= usbnet_nway_reset,
 	.get_msglevel		= usbnet_get_msglevel,
 	.get_msglevel		= usbnet_get_msglevel,
 	.set_msglevel		= usbnet_set_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.function = usbnet_bh;
 	dev->delay.data = (unsigned long) dev;
 	dev->delay.data = (unsigned long) dev;
 	init_timer (&dev->delay);
 	init_timer (&dev->delay);
+	mutex_init (&dev->phy_mutex);
 
 
 	SET_MODULE_OWNER (net);
 	SET_MODULE_OWNER (net);
 	dev->net = net;
 	dev->net = net;
@@ -1225,7 +1276,7 @@ EXPORT_SYMBOL_GPL(usbnet_resume);
 static int __init usbnet_init(void)
 static int __init usbnet_init(void)
 {
 {
 	/* compiler should optimize this out */
 	/* 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));
 			< sizeof (struct skb_data));
 
 
 	random_ether_addr(node_id);
 	random_ether_addr(node_id);

+ 5 - 0
drivers/usb/net/usbnet.h

@@ -30,6 +30,7 @@ struct usbnet {
 	struct usb_device	*udev;
 	struct usb_device	*udev;
 	struct driver_info	*driver_info;
 	struct driver_info	*driver_info;
 	wait_queue_head_t	*wait;
 	wait_queue_head_t	*wait;
+	struct mutex		phy_mutex;
 
 
 	/* i/o info: pipes etc */
 	/* i/o info: pipes etc */
 	unsigned		in, out;
 	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_skb_return (struct usbnet *, struct sk_buff *);
 extern void usbnet_unlink_rx_urbs(struct usbnet *);
 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 u32 usbnet_get_msglevel (struct net_device *);
 extern void usbnet_set_msglevel (struct net_device *, u32);
 extern void usbnet_set_msglevel (struct net_device *, u32);
 extern void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
 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
 /* messaging support includes the interface name, so it must not be
  * used before it has one ... notably, in minidriver bind() calls.
  * used before it has one ... notably, in minidriver bind() calls.

+ 11 - 2
drivers/usb/serial/Kconfig

@@ -422,6 +422,16 @@ config USB_SERIAL_MCT_U232
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called mct_u232.
 	  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
 config USB_SERIAL_MOS7840
 	tristate "USB Moschip 7840/7820 USB Serial Driver"
 	tristate "USB Moschip 7840/7820 USB Serial Driver"
 	depends on USB_SERIAL
 	depends on USB_SERIAL
@@ -527,8 +537,7 @@ config USB_SERIAL_OPTION
 	  The USB bus on these cards is not accessible externally.
 	  The USB bus on these cards is not accessible externally.
 
 
 	  Supported devices include (some of?) those made by:
 	  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
 	  To compile this driver as a module, choose M here: the
 	  module will be called option.
 	  module will be called option.

+ 1 - 0
drivers/usb/serial/Makefile

@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
 obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
 obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
 obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)		+= kobil_sct.o
 obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)		+= kobil_sct.o
 obj-$(CONFIG_USB_SERIAL_MCT_U232)		+= mct_u232.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_MOS7840)		+= mos7840.o
 obj-$(CONFIG_USB_SERIAL_NAVMAN)			+= navman.o
 obj-$(CONFIG_USB_SERIAL_NAVMAN)			+= navman.o
 obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
 obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o

+ 2 - 5
drivers/usb/serial/airprime.c

@@ -18,12 +18,8 @@
 
 
 static struct usb_device_id id_table [] = {
 static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
 	{ 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, 0x1110) }, /* Novatel Wireless Merlin CDMA */
+	{ USB_DEVICE(0x1410, 0x1100) }, /* ExpressCard34 Qualcomm 3G CDMA */
 	{ },
 	{ },
 };
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 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);
 		urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!urb) {
 		if (!urb) {
+			kfree(buffer);
 			dev_err(&port->dev, "%s - no more urbs?\n",
 			dev_err(&port->dev, "%s - no more urbs?\n",
 				__FUNCTION__);
 				__FUNCTION__);
 			result = -ENOMEM;
 			result = -ENOMEM;

+ 1 - 0
drivers/usb/serial/cp2101.c

@@ -65,6 +65,7 @@ static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
 	{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
 	{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
 	{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
 	{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
 	{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+	{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
 	{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
 	{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
 	{ } /* Terminating Entry */
 	{ } /* Terminating Entry */
 };
 };

+ 127 - 127
drivers/usb/serial/ftdi_sio.c

@@ -1,16 +1,16 @@
 /*
 /*
  * USB FTDI SIO driver
  * 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)
  *          Bill Ryder (bryder@sgi.com)
  *	Copyright (C) 2002
  *	Copyright (C) 2002
  *	    Kuba Ober (kuba@mareimbrium.org)
  *	    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
  * 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.
  *      Changed full name of USB-UIRT device to avoid "/" character.
  *      Added FTDI's alternate PID (0x6006) for FT232/245 devices.
  *      Added FTDI's alternate PID (0x6006) for FT232/245 devices.
  *      Added PID for "ELV USB Module UO100" from Stefan Frings.
  *      Added PID for "ELV USB Module UO100" from Stefan Frings.
- * 
+ *
  * (21/Oct/2003) Ian Abbott
  * (21/Oct/2003) Ian Abbott
  *      Renamed some VID/PID macros for Matrix Orbital and Perle Systems
  *      Renamed some VID/PID macros for Matrix Orbital and Perle Systems
  *      devices.  Removed Matrix Orbital and Perle Systems devices from the
  *      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
  *	does not incure any measurable overhead.  This also relies on the fact
  *	that we have proper reference counting logic for urbs.  I nicked this
  *	that we have proper reference counting logic for urbs.  I nicked this
  *	from Greg KH's Visor driver.
  *	from Greg KH's Visor driver.
- *      
+ *
  * (23/Jun/2003) Ian Abbott
  * (23/Jun/2003) Ian Abbott
  *      Reduced flip buffer pushes and corrected a data length test in
  *      Reduced flip buffer pushes and corrected a data length test in
  *      ftdi_read_bulk_callback.
  *      ftdi_read_bulk_callback.
@@ -77,7 +77,7 @@
  *
  *
  * (21/Jun/2003) Erik Nygren
  * (21/Jun/2003) Erik Nygren
  *      Added support for Home Electronics Tira-1 IR transceiver using FT232BM chip.
  *      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.
  *      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.
  *      Also force the Tira-1 and USB-UIRT to only use their custom baud rates.
  *
  *
@@ -137,17 +137,17 @@
  * (17/Feb/2003) Bill Ryder
  * (17/Feb/2003) Bill Ryder
  *      Added write urb buffer pool on a per device basis
  *      Added write urb buffer pool on a per device basis
  *      Added more checking for open file on callbacks (fixed OOPS)
  *      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
  *         (thanx to CrystalFontz for the sample devices - they flushed out
  *           some driver bugs)
  *           some driver bugs)
  *      Minor debugging message changes
  *      Minor debugging message changes
  *      Added throttle, unthrottle and chars_in_buffer functions
  *      Added throttle, unthrottle and chars_in_buffer functions
  *      Fixed FTDI_SIO (the original device) bug
  *      Fixed FTDI_SIO (the original device) bug
  *      Fixed some shutdown handling
  *      Fixed some shutdown handling
- *      
- * 
- * 
- * 
+ *
+ *
+ *
+ *
  * (07/Jun/2002) Kuba Ober
  * (07/Jun/2002) Kuba Ober
  *	Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor
  *	Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor
  *	function. It was getting too complex.
  *	function. It was getting too complex.
@@ -158,7 +158,7 @@
  *
  *
  * (25/Jul/2002) Bill Ryder inserted Dmitri's TIOCMIWAIT patch
  * (25/Jul/2002) Bill Ryder inserted Dmitri's TIOCMIWAIT patch
  *      Not tested by me but it doesn't break anything I use.
  *      Not tested by me but it doesn't break anything I use.
- * 
+ *
  * (04/Jan/2002) Kuba Ober
  * (04/Jan/2002) Kuba Ober
  *	Implemented 38400 baudrate kludge, where it can be substituted with other
  *	Implemented 38400 baudrate kludge, where it can be substituted with other
  *	  values. That's the only way to set custom baudrates.
  *	  values. That's the only way to set custom baudrates.
@@ -179,7 +179,7 @@
  *        (the previous version caused panics)
  *        (the previous version caused panics)
  *	Removed port iteration code since the device only has one I/O port and it
  *	Removed port iteration code since the device only has one I/O port and it
  *	  was wrong anyway.
  *	  was wrong anyway.
- * 
+ *
  * (31/May/2001) gkh
  * (31/May/2001) gkh
  *	Switched from using spinlock to a semaphore, which fixes lots of problems.
  *	Switched from using spinlock to a semaphore, which fixes lots of problems.
  *
  *
@@ -188,16 +188,16 @@
  *	Cleaned up comments for 8U232
  *	Cleaned up comments for 8U232
  *	Added parity, framing and overrun error handling
  *	Added parity, framing and overrun error handling
  *	Added receive break handling.
  *	Added receive break handling.
- * 
+ *
  * (04/08/2001) gb
  * (04/08/2001) gb
  *	Identify version on module load.
  *	Identify version on module load.
- *       
+ *
  * (18/March/2001) Bill Ryder
  * (18/March/2001) Bill Ryder
  *	(Not released)
  *	(Not released)
  *	Added send break handling. (requires kernel patch too)
  *	Added send break handling. (requires kernel patch too)
  *	Fixed 8U232AM hardware RTS/CTS etc status reporting.
  *	Fixed 8U232AM hardware RTS/CTS etc status reporting.
  *	Added flipbuf fix copied from generic device
  *	Added flipbuf fix copied from generic device
- * 
+ *
  * (12/3/2000) Bill Ryder
  * (12/3/2000) Bill Ryder
  *	Added support for 8U232AM device.
  *	Added support for 8U232AM device.
  *	Moved PID and VIDs into header file only.
  *	Moved PID and VIDs into header file only.
@@ -211,14 +211,14 @@
  *	Cleaned up comments. Removed multiple PID/VID definitions.
  *	Cleaned up comments. Removed multiple PID/VID definitions.
  *	Factorised cts/dtr code
  *	Factorised cts/dtr code
  *	Made use of __FUNCTION__ in dbg's
  *	Made use of __FUNCTION__ in dbg's
- *      
+ *
  * (11/01/2000) Adam J. Richter
  * (11/01/2000) Adam J. Richter
  *	usb_device_id table support
  *	usb_device_id table support
- * 
+ *
  * (10/05/2000) gkh
  * (10/05/2000) gkh
  *	Fixed bug with urb->dev not being set properly, now that the usb
  *	Fixed bug with urb->dev not being set properly, now that the usb
  *	core needs it.
  *	core needs it.
- * 
+ *
  * (09/11/2000) gkh
  * (09/11/2000) gkh
  *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
  *	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
  *	Added module_init and module_exit functions to handle the fact that this
  *	driver is a loadable module now.
  *	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
  *	Fixed bugs in TCGET/TCSET ioctls (by removing them - they are
  *        handled elsewhere in the tty io driver chain).
  *        handled elsewhere in the tty io driver chain).
  *
  *
- * (03/30/2000) Bill Ryder 
+ * (03/30/2000) Bill Ryder
  *	Implemented lots of ioctls
  *	Implemented lots of ioctls
  *	Fixed a race condition in write
  *	Fixed a race condition in write
  *	Changed some dbg's to errs
  *	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_WS300PC_PID) }, */
 	/* { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, */
 	/* { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, */
 	/* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_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(FTDI_VID, INSIDE_ACCESSO) },
 	{ USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
 	{ USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
 	{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
 	{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
@@ -522,7 +522,7 @@ static struct usb_driver ftdi_driver = {
 	.probe =	usb_serial_probe,
 	.probe =	usb_serial_probe,
 	.disconnect =	usb_serial_disconnect,
 	.disconnect =	usb_serial_disconnect,
 	.id_table =	id_table_combined,
 	.id_table =	id_table_combined,
-	.no_dynamic_id = 	1,
+	.no_dynamic_id =	1,
 };
 };
 
 
 static const char *ftdi_chip_name[] = {
 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!) */
 	int custom_divisor;	/* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
 	__u16 last_set_data_urb_value ;
 	__u16 last_set_data_urb_value ;
 				/* the last data state set - needed for doing a break */
 				/* 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
 				 * it is different between devices
 				 */
 				 */
 	int flags;		/* some ASYNC_xxxx flags are supported */
 	int flags;		/* some ASYNC_xxxx flags are supported */
 	unsigned long last_dtr_rts;	/* saved modem control outputs */
 	unsigned long last_dtr_rts;	/* saved modem control outputs */
         wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
         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) */
 	__u8 rx_flags;		/* receive state flags (throttling) */
 	spinlock_t rx_lock;	/* spinlock for receive state */
 	spinlock_t rx_lock;	/* spinlock for receive state */
 	struct work_struct rx_work;
 	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;
 		urb_value |= FTDI_SIO_SET_RTS_HIGH;
 	rv = usb_control_msg(port->serial->dev,
 	rv = usb_control_msg(port->serial->dev,
 			       usb_sndctrlpipe(port->serial->dev, 0),
 			       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,
 			       FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
 			       urb_value, priv->interface,
 			       urb_value, priv->interface,
 			       buf, 0, WDR_TIMEOUT);
 			       buf, 0, WDR_TIMEOUT);
@@ -768,7 +768,7 @@ static int change_speed(struct usb_serial_port *port)
 	if (priv->interface) {	/* FT2232C */
 	if (priv->interface) {	/* FT2232C */
 		urb_index = (__u16)((urb_index << 8) | priv->interface);
 		urb_index = (__u16)((urb_index << 8) | priv->interface);
 	}
 	}
-	
+
 	rv = usb_control_msg(port->serial->dev,
 	rv = usb_control_msg(port->serial->dev,
 			    usb_sndctrlpipe(port->serial->dev, 0),
 			    usb_sndctrlpipe(port->serial->dev, 0),
 			    FTDI_SIO_SET_BAUDRATE_REQUEST,
 			    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 */
 	/* 3. Convert baudrate to device-specific divisor */
 
 
-	if (!baud) baud = 9600;	
+	if (!baud) baud = 9600;
 	switch(priv->chip_type) {
 	switch(priv->chip_type) {
 	case SIO: /* SIO chip */
 	case SIO: /* SIO chip */
 		switch(baud) {
 		switch(baud) {
@@ -843,7 +843,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port)
 		case 115200: div_value = ftdi_sio_b115200; break;
 		case 115200: div_value = ftdi_sio_b115200; break;
 		} /* baud */
 		} /* baud */
 		if (div_value == 0) {
 		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_value = ftdi_sio_b9600;
 			div_okay = 0;
 			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! */
 	/* Make the changes - these are privileged changes! */
 
 
 	priv->flags = ((priv->flags & ~ASYNC_FLAGS) |
 	priv->flags = ((priv->flags & ~ASYNC_FLAGS) |
-	               (new_serial.flags & ASYNC_FLAGS));	
+	               (new_serial.flags & ASYNC_FLAGS));
 	priv->custom_divisor = new_serial.custom_divisor;
 	priv->custom_divisor = new_serial.custom_divisor;
 
 
 	port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 	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))) {
 	     (old_priv.custom_divisor != priv->custom_divisor))) {
 		change_speed(port);
 		change_speed(port);
 	}
 	}
-	
+
 	return (0);
 	return (0);
 
 
 } /* set_serial_info */
 } /* set_serial_info */
@@ -1022,18 +1022,18 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a
 	struct usb_device *udev;
 	struct usb_device *udev;
 	unsigned short latency = 0;
 	unsigned short latency = 0;
 	int rv = 0;
 	int rv = 0;
-	
+
 	udev = to_usb_device(dev);
 	udev = to_usb_device(dev);
-	
+
 	dbg("%s",__FUNCTION__);
 	dbg("%s",__FUNCTION__);
-	
+
 	rv = usb_control_msg(udev,
 	rv = usb_control_msg(udev,
 			     usb_rcvctrlpipe(udev, 0),
 			     usb_rcvctrlpipe(udev, 0),
 			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
 			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
 			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
 			     FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
-			     0, priv->interface, 
+			     0, priv->interface,
 			     (char*) &latency, 1, WDR_TIMEOUT);
 			     (char*) &latency, 1, WDR_TIMEOUT);
-	
+
 	if (rv < 0) {
 	if (rv < 0) {
 		dev_err(dev, "Unable to read latency timer: %i", rv);
 		dev_err(dev, "Unable to read latency timer: %i", rv);
 		return -EIO;
 		return -EIO;
@@ -1051,23 +1051,23 @@ static ssize_t store_latency_timer(struct device *dev, struct device_attribute *
 	char buf[1];
 	char buf[1];
 	int v = simple_strtoul(valbuf, NULL, 10);
 	int v = simple_strtoul(valbuf, NULL, 10);
 	int rv = 0;
 	int rv = 0;
-	
+
 	udev = to_usb_device(dev);
 	udev = to_usb_device(dev);
-	
+
 	dbg("%s: setting latency timer = %i", __FUNCTION__, v);
 	dbg("%s: setting latency timer = %i", __FUNCTION__, v);
-	
+
 	rv = usb_control_msg(udev,
 	rv = usb_control_msg(udev,
 			     usb_sndctrlpipe(udev, 0),
 			     usb_sndctrlpipe(udev, 0),
 			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
 			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
 			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
 			     FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
-			     v, priv->interface, 
+			     v, priv->interface,
 			     buf, 0, WDR_TIMEOUT);
 			     buf, 0, WDR_TIMEOUT);
-	
+
 	if (rv < 0) {
 	if (rv < 0) {
 		dev_err(dev, "Unable to write latency timer: %i", rv);
 		dev_err(dev, "Unable to write latency timer: %i", rv);
 		return -EIO;
 		return -EIO;
 	}
 	}
-	
+
 	return count;
 	return count;
 }
 }
 
 
@@ -1082,23 +1082,23 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
 	char buf[1];
 	char buf[1];
 	int v = simple_strtoul(valbuf, NULL, 10);
 	int v = simple_strtoul(valbuf, NULL, 10);
 	int rv = 0;
 	int rv = 0;
-	
+
 	udev = to_usb_device(dev);
 	udev = to_usb_device(dev);
-	
+
 	dbg("%s: setting event char = %i", __FUNCTION__, v);
 	dbg("%s: setting event char = %i", __FUNCTION__, v);
-	
+
 	rv = usb_control_msg(udev,
 	rv = usb_control_msg(udev,
 			     usb_sndctrlpipe(udev, 0),
 			     usb_sndctrlpipe(udev, 0),
 			     FTDI_SIO_SET_EVENT_CHAR_REQUEST,
 			     FTDI_SIO_SET_EVENT_CHAR_REQUEST,
 			     FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
 			     FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
-			     v, priv->interface, 
+			     v, priv->interface,
 			     buf, 0, WDR_TIMEOUT);
 			     buf, 0, WDR_TIMEOUT);
-	
+
 	if (rv < 0) {
 	if (rv < 0) {
 		dbg("Unable to write event character: %i", rv);
 		dbg("Unable to write event character: %i", rv);
 		return -EIO;
 		return -EIO;
 	}
 	}
-	
+
 	return count;
 	return count;
 }
 }
 
 
@@ -1135,11 +1135,11 @@ static void remove_sysfs_attrs(struct usb_serial *serial)
 	struct ftdi_private *priv;
 	struct ftdi_private *priv;
 	struct usb_device *udev;
 	struct usb_device *udev;
 
 
-	dbg("%s",__FUNCTION__);	
+	dbg("%s",__FUNCTION__);
 
 
 	priv = usb_get_serial_port_data(serial->port[0]);
 	priv = usb_get_serial_port_data(serial->port[0]);
 	udev = serial->dev;
 	udev = serial->dev;
-	
+
 	/* XXX see create_sysfs_attrs */
 	/* XXX see create_sysfs_attrs */
 	if (priv->chip_type != SIO) {
 	if (priv->chip_type != SIO) {
 		device_remove_file(&udev->dev, &dev_attr_event_char);
 		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);
 			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_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
  *   it is called when the usb device is disconnected
  *
  *
  *   usbserial:usb_serial_disconnect
  *   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)
 static void ftdi_shutdown (struct usb_serial *serial)
 { /* ftdi_shutdown */
 { /* ftdi_shutdown */
-	
+
 	struct usb_serial_port *port = serial->port[0];
 	struct usb_serial_port *port = serial->port[0];
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 
 
 	dbg("%s", __FUNCTION__);
 	dbg("%s", __FUNCTION__);
 
 
 	remove_sysfs_attrs(serial);
 	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) {
 	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 usb_device *dev = port->serial->dev;
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 	unsigned long flags;
 	unsigned long flags;
-	
+
 	int result = 0;
 	int result = 0;
 	char buf[1]; /* Needed for the usb_control_msg I think */
 	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) */
 	/* No error checking for this (will get errors later anyway) */
 	/* See ftdi_sio.h for description of what is reset */
 	/* See ftdi_sio.h for description of what is reset */
 	usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 	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);
 			priv->interface, buf, 0, WDR_TIMEOUT);
 
 
 	/* Termios defaults are set by usb_serial_init. We don't change
 	/* 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
  * usbserial:__serial_close  only calls ftdi_close if the point is open
  *
  *
  *   This only gets called when it is the last close
  *   This only gets called when it is the last close
- *   
- *   
+ *
+ *
  */
  */
 
 
 static void ftdi_close (struct usb_serial_port *port, struct file *filp)
 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){
 	if (c_cflag & HUPCL){
 		/* Disable flow control */
 		/* Disable flow control */
-		if (usb_control_msg(port->serial->dev, 
+		if (usb_control_msg(port->serial->dev,
 				    usb_sndctrlpipe(port->serial->dev, 0),
 				    usb_sndctrlpipe(port->serial->dev, 0),
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
 				    0, priv->interface, buf, 0,
 				    0, priv->interface, buf, 0,
 				    WDR_TIMEOUT) < 0) {
 				    WDR_TIMEOUT) < 0) {
 			err("error from flowcontrol urb");
 			err("error from flowcontrol urb");
-		}	    
+		}
 
 
 		/* drop RTS and DTR */
 		/* drop RTS and DTR */
 		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
 		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 any scheduled reading */
 	cancel_delayed_work(&priv->rx_work);
 	cancel_delayed_work(&priv->rx_work);
 	flush_scheduled_work();
 	flush_scheduled_work();
-	
+
 	/* shutdown our bulk read */
 	/* shutdown our bulk read */
 	if (port->read_urb)
 	if (port->read_urb)
 		usb_kill_urb(port->read_urb);
 		usb_kill_urb(port->read_urb);
 } /* ftdi_close */
 } /* ftdi_close */
 
 
 
 
-  
+
 /* The SIO requires the first byte to have:
 /* The SIO requires the first byte to have:
  *  B0 1
  *  B0 1
  *  B1 0
  *  B1 0
@@ -1423,7 +1423,7 @@ static int ftdi_write (struct usb_serial_port *port,
 		return 0;
 		return 0;
 	}
 	}
 	spin_unlock_irqrestore(&priv->tx_lock, flags);
 	spin_unlock_irqrestore(&priv->tx_lock, flags);
-	
+
 	data_offset = priv->write_offset;
 	data_offset = priv->write_offset;
         dbg("data_offset set to %d",data_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;
 				user_pktsz = todo;
 			}
 			}
 			/* Write the control byte at the front of the packet*/
 			/* 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 */
 			/* Copy data for packet */
 			memcpy (first_byte + data_offset,
 			memcpy (first_byte + data_offset,
 				current_position, user_pktsz);
 				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);
 	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, buffer);
 
 
 	/* fill the buffer and send it */
 	/* 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),
 		      usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
 		      buffer, transfer_size,
 		      buffer, transfer_size,
 		      ftdi_write_bulk_callback, port);
 		      ftdi_write_bulk_callback, port);
@@ -1520,7 +1520,7 @@ static void ftdi_write_bulk_callback (struct urb *urb)
 	kfree (urb->transfer_buffer);
 	kfree (urb->transfer_buffer);
 
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 	dbg("%s - port %d", __FUNCTION__, port->number);
-	
+
 	if (urb->status) {
 	if (urb->status) {
 		dbg("nonzero write bulk status received: %d", urb->status);
 		dbg("nonzero write bulk status received: %d", urb->status);
 		return;
 		return;
@@ -1651,7 +1651,7 @@ static void ftdi_process_read (void *param)
 	struct tty_struct *tty;
 	struct tty_struct *tty;
 	struct ftdi_private *priv;
 	struct ftdi_private *priv;
 	char error_flag;
 	char error_flag;
-       	unsigned char *data;
+	unsigned char *data;
 
 
 	int i;
 	int i;
 	int result;
 	int result;
@@ -1759,7 +1759,7 @@ static void ftdi_process_read (void *param)
 		}
 		}
 		if (length > 0) {
 		if (length > 0) {
 			for (i = 2; i < length+2; i++) {
 			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
 				   every character received since we don't know
 				   which character it applied to */
 				   which character it applied to */
 				tty_insert_flip_char(tty, data[packet_offset+i], error_flag);
 				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
 		   This doesn't work well since the application receives a never
 		   ending stream of bad data - even though new data hasn't been sent.
 		   ending stream of bad data - even though new data hasn't been sent.
 		   Therefore I (bill) have taken this out.
 		   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.
 		   so I am leaving the code in for now.
 		*/
 		*/
 		else {
 		else {
@@ -1827,7 +1827,7 @@ static void ftdi_process_read (void *param)
 	/* if the port is closed stop trying to read */
 	/* if the port is closed stop trying to read */
 	if (port->open_count > 0){
 	if (port->open_count > 0){
 		/* Continue trying to always read  */
 		/* 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),
 			      usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
 			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
 			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
 			      ftdi_read_bulk_callback, port);
 			      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 )
 static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
 {
 {
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
-	__u16 urb_value = 0; 
+	__u16 urb_value = 0;
 	char buf[1];
 	char buf[1];
-	
+
 	/* break_state = -1 to turn on break, and 0 to turn off break */
 	/* break_state = -1 to turn on break, and 0 to turn off break */
 	/* see drivers/char/tty_io.c to see it used */
 	/* see drivers/char/tty_io.c to see it used */
 	/* last_set_data_urb_value NEVER has the break bit set in it */
 	/* 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) {
 	if (break_state) {
 		urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
 		urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
 	} else {
 	} 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),
 	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,
 			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
 			    urb_value , priv->interface,
 			    urb_value , priv->interface,
 			    buf, 0, WDR_TIMEOUT) < 0) {
 			    buf, 0, WDR_TIMEOUT) < 0) {
 		err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state);
 		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);
 	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);
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 	__u16 urb_value; /* will hold the new flags */
 	__u16 urb_value; /* will hold the new flags */
 	char buf[1]; /* Perhaps I should dynamically alloc this? */
 	char buf[1]; /* Perhaps I should dynamically alloc this? */
-	
+
 	// Added for xon/xoff support
 	// Added for xon/xoff support
 	unsigned int iflag = port->tty->termios->c_iflag;
 	unsigned int iflag = port->tty->termios->c_iflag;
 	unsigned char vstop;
 	unsigned char vstop;
 	unsigned char vstart;
 	unsigned char vstart;
-	
+
 	dbg("%s", __FUNCTION__);
 	dbg("%s", __FUNCTION__);
 
 
 	/* Force baud rate if this device requires it, unless it is set to B0. */
 	/* 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;
 	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 */
 	   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 */
            means - don't see any problems yet */
-	
+
 	/* Set number of data bits, parity, stop bits */
 	/* Set number of data bits, parity, stop bits */
-	
+
 	urb_value = 0;
 	urb_value = 0;
 	urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
 	urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
 		      FTDI_SIO_SET_DATA_STOP_BITS_1);
 		      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_EVEN) :
 		      FTDI_SIO_SET_DATA_PARITY_NONE);
 		      FTDI_SIO_SET_DATA_PARITY_NONE);
 	if (cflag & CSIZE) {
 	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
 	/* This is needed by the break command since it uses the same command - but is
 	 *  or'ed with this value  */
 	 *  or'ed with this value  */
 	priv->last_set_data_urb_value = urb_value;
 	priv->last_set_data_urb_value = urb_value;
-	
+
 	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 	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,
 			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
 			    urb_value , priv->interface,
 			    urb_value , priv->interface,
 			    buf, 0, WDR_SHORT_TIMEOUT) < 0) {
 			    buf, 0, WDR_SHORT_TIMEOUT) < 0) {
 		err("%s FAILED to set databits/stopbits/parity", __FUNCTION__);
 		err("%s FAILED to set databits/stopbits/parity", __FUNCTION__);
-	}	   
+	}
 
 
 	/* Now do the baudrate */
 	/* Now do the baudrate */
 	if ((cflag & CBAUD) == B0 ) {
 	if ((cflag & CBAUD) == B0 ) {
 		/* Disable flow control */
 		/* Disable flow control */
 		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 		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,
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-				    0, priv->interface, 
+				    0, priv->interface,
 				    buf, 0, WDR_TIMEOUT) < 0) {
 				    buf, 0, WDR_TIMEOUT) < 0) {
 			err("%s error from disable flowcontrol urb", __FUNCTION__);
 			err("%s error from disable flowcontrol urb", __FUNCTION__);
-		}	    
+		}
 		/* Drop RTS and DTR */
 		/* Drop RTS and DTR */
 		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
 		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
 	} else {
 	} 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 */
 	/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
 	if (cflag & CRTSCTS) {
 	if (cflag & CRTSCTS) {
 		dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
 		dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
-		if (usb_control_msg(dev, 
+		if (usb_control_msg(dev,
 				    usb_sndctrlpipe(dev, 0),
 				    usb_sndctrlpipe(dev, 0),
-				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+				    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
 				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
 				    0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
 				    0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
 				    buf, 0, WDR_TIMEOUT) < 0) {
 				    buf, 0, WDR_TIMEOUT) < 0) {
 			err("urb failed to set to rts/cts flow control");
 			err("urb failed to set to rts/cts flow control");
-		}		
-		
-	} else { 
+		}
+
+	} else {
 		/*
 		/*
 		 * Xon/Xoff code
 		 * 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 */
 			/* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
 			/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
 			/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
 			dbg("%s Turning off hardware flow control", __FUNCTION__);
 			dbg("%s Turning off hardware flow control", __FUNCTION__);
-			if (usb_control_msg(dev, 
+			if (usb_control_msg(dev,
 					    usb_sndctrlpipe(dev, 0),
 					    usb_sndctrlpipe(dev, 0),
-					    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+					    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
 					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
 					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-					    0, priv->interface, 
+					    0, priv->interface,
 					    buf, 0, WDR_TIMEOUT) < 0) {
 					    buf, 0, WDR_TIMEOUT) < 0) {
 				err("urb failed to clear flow control");
 				err("urb failed to clear flow control");
-			}				
+			}
 		}
 		}
-		
+
 	}
 	}
 	return;
 	return;
 } /* ftdi_termios */
 } /* ftdi_termios */
@@ -2036,11 +2036,11 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
 	switch (priv->chip_type) {
 	switch (priv->chip_type) {
 	case SIO:
 	case SIO:
 		/* Request the status from the device */
 		/* 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),
 					   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,
 					   FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
-					   0, 0, 
+					   0, 0,
 					   buf, 1, WDR_TIMEOUT)) < 0 ) {
 					   buf, 1, WDR_TIMEOUT)) < 0 ) {
 			err("%s Could not get modem status of device - err: %d", __FUNCTION__,
 			err("%s Could not get modem status of device - err: %d", __FUNCTION__,
 			    ret);
 			    ret);
@@ -2052,11 +2052,11 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
 	case FT2232C:
 	case FT2232C:
 		/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
 		/* 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 */
 		   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),
 					   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,
 					   FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
-					   0, priv->interface, 
+					   0, priv->interface,
 					   buf, 2, WDR_TIMEOUT)) < 0 ) {
 					   buf, 2, WDR_TIMEOUT)) < 0 ) {
 			err("%s Could not get modem status of device - err: %d", __FUNCTION__,
 			err("%s Could not get modem status of device - err: %d", __FUNCTION__,
 			    ret);
 			    ret);
@@ -2067,12 +2067,12 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
 		return -EFAULT;
 		return -EFAULT;
 		break;
 		break;
 	}
 	}
-	
+
 	return  (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
 	return  (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
 		(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
 		(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
 		(buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
 		(buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
 		(buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 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)
 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;
 		break;
 	default:
 	default:
 		break;
 		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)
 	 *  some ioctls itself (see comment above)
 	 */
 	 */
 	dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __FUNCTION__, cmd);
 	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)
 	if (retval)
 		goto failed_sio_register;
 		goto failed_sio_register;
 	retval = usb_register(&ftdi_driver);
 	retval = usb_register(&ftdi_driver);
-	if (retval) 
+	if (retval)
 		goto failed_usb_register;
 		goto failed_usb_register;
 
 
 	info(DRIVER_VERSION ":" DRIVER_DESC);
 	info(DRIVER_VERSION ":" DRIVER_DESC);

+ 1683 - 0
drivers/usb/serial/mos7720.c

@@ -0,0 +1,1683 @@
+/*
+ * mos7720.c
+ *   Controls the Moschip 7720 usb to dual port serial convertor
+ *
+ * Copyright 2006 Moschip Semiconductor Tech. Ltd.
+ *
+ * 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, version 2 of the License.
+ *
+ * Developed by:
+ * 	VijayaKumar.G.N. <vijaykumar@aspirecom.net>
+ *	AjayKumar <ajay@aspirecom.net>
+ *	Gurudeva.N. <gurudev@aspirecom.net>
+ *
+ * Cleaned up from the original by:
+ *	Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Originally based on drivers/usb/serial/io_edgeport.c which is:
+ *	Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ *	Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/uaccess.h>
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "1.0.0.4F"
+#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
+#define DRIVER_DESC "Moschip USB Serial Driver"
+
+/* default urb timeout */
+#define MOS_WDR_TIMEOUT	(HZ * 5)
+
+#define MOS_PORT1	0x0200
+#define MOS_PORT2	0x0300
+#define MOS_VENREG	0x0000
+#define MOS_MAX_PORT	0x02
+#define MOS_WRITE	0x0E
+#define MOS_READ	0x0D
+
+/* Interrupt Rotinue Defines	*/
+#define SERIAL_IIR_RLS	0x06
+#define SERIAL_IIR_RDA	0x04
+#define SERIAL_IIR_CTI	0x0c
+#define SERIAL_IIR_THR	0x02
+#define SERIAL_IIR_MS	0x00
+
+#define NUM_URBS			16	/* URB Count */
+#define URB_TRANSFER_BUFFER_SIZE	32	/* URB Size */
+
+/* This structure holds all of the local port information */
+struct moschip_port
+{
+	__u8	shadowLCR;		/* last LCR value received */
+	__u8	shadowMCR;		/* last MCR value received */
+	__u8	shadowMSR;		/* last MSR value received */
+	char			open;
+	struct async_icount	icount;
+	struct usb_serial_port	*port;	/* loop back to the owner */
+	struct urb		*write_urb_pool[NUM_URBS];
+};
+
+/* This structure holds all of the individual serial device information */
+struct moschip_serial
+{
+	int interrupt_started;
+};
+
+static int debug;
+
+#define USB_VENDOR_ID_MOSCHIP		0x9710
+#define MOSCHIP_DEVICE_ID_7720		0x7720
+#define MOSCHIP_DEVICE_ID_7715		0x7715
+
+static struct usb_device_id moschip_port_id_table [] = {
+	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP,MOSCHIP_DEVICE_ID_7720) },
+	{ } /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, moschip_port_id_table);
+
+
+/*
+ * mos7720_interrupt_callback
+ *	this is the callback function for when we have received data on the
+ *	interrupt endpoint.
+ */
+static void mos7720_interrupt_callback(struct urb *urb)
+{
+	int result;
+	int length;
+	__u32 *data;
+	unsigned int status;
+	__u8 sp1;
+	__u8 sp2;
+	__u8 st;
+
+	dbg("%s"," : Entering\n");
+
+	if (!urb) {
+		dbg("%s","Invalid Pointer !!!!:\n");
+		return;
+	}
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+		    urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+		    urb->status);
+		goto exit;
+	}
+
+	length = urb->actual_length;
+	data = urb->transfer_buffer;
+
+	/* Moschip get 4 bytes
+	 * Byte 1 IIR Port 1 (port.number is 0)
+	 * Byte 2 IIR Port 2 (port.number is 1)
+	 * Byte 3 --------------
+	 * Byte 4 FIFO status for both */
+	if (length && length > 4) {
+		dbg("Wrong data !!!");
+		return;
+	}
+
+	status = *data;
+
+	sp1 = (status & 0xff000000)>>24;
+	sp2 = (status & 0x00ff0000)>>16;
+	st = status & 0x000000ff;
+
+	if ((sp1 & 0x01) || (sp2 & 0x01)) {
+		/* No Interrupt Pending in both the ports */
+		dbg("No Interrupt !!!");
+	} else {
+		switch (sp1 & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dbg("Serial Port 1: Receiver status error or address "
+			    "bit detected in 9-bit mode\n");
+			break;
+		case SERIAL_IIR_CTI:
+			dbg("Serial Port 1: Receiver time out");
+			break;
+		case SERIAL_IIR_MS:
+			dbg("Serial Port 1: Modem status change");
+			break;
+		}
+
+		switch (sp2 & 0x0f) {
+		case SERIAL_IIR_RLS:
+			dbg("Serial Port 2: Receiver status error or address "
+			    "bit detected in 9-bit mode");
+			break;
+		case SERIAL_IIR_CTI:
+			dbg("Serial Port 2: Receiver time out");
+			break;
+		case SERIAL_IIR_MS:
+			dbg("Serial Port 2: Modem status change");
+			break;
+		}
+	}
+
+exit:
+	result = usb_submit_urb(urb, GFP_ATOMIC);
+	if (result)
+		dev_err(&urb->dev->dev,
+			"%s - Error %d submitting control urb\n",
+			__FUNCTION__, result);
+	return;
+}
+
+/*
+ * mos7720_bulk_in_callback
+ *	this is the callback function for when we have received data on the
+ *	bulk in endpoint.
+ */
+static void mos7720_bulk_in_callback(struct urb *urb)
+{
+	int status;
+	unsigned char *data ;
+	struct usb_serial_port *port;
+	struct moschip_port *mos7720_port;
+	struct tty_struct *tty;
+
+	if (urb->status) {
+		dbg("nonzero read bulk status received: %d",urb->status);
+		return;
+	}
+
+	mos7720_port = urb->context;
+	if (!mos7720_port) {
+		dbg("%s","NULL mos7720_port pointer \n");
+		return ;
+	}
+
+	port = mos7720_port->port;
+
+	dbg("Entering...%s", __FUNCTION__);
+
+	data = urb->transfer_buffer;
+
+	tty = port->tty;
+	if (tty && 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);
+	}
+
+	if (!port->read_urb) {
+		dbg("URB KILLED !!!");
+		return;
+	}
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		port->read_urb->dev = port->serial->dev;
+
+		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (status)
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    status);
+	}
+}
+
+/*
+ * mos7720_bulk_out_data_callback
+ *	this is the callback function for when we have finished sending serial
+ *	data on the bulk out endpoint.
+ */
+static void mos7720_bulk_out_data_callback(struct urb *urb)
+{
+	struct moschip_port *mos7720_port;
+	struct tty_struct *tty;
+
+	if (urb->status) {
+		dbg("nonzero write bulk status received:%d", urb->status);
+		return;
+	}
+
+	mos7720_port = urb->context;
+	if (!mos7720_port) {
+		dbg("NULL mos7720_port pointer");
+		return ;
+	}
+
+	dbg("Entering .........");
+
+	tty = mos7720_port->port->tty;
+
+	if (tty && mos7720_port->open) {
+		/* let the tty driver wakeup if it has a special *
+		 * write_wakeup function */
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		     tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+
+		/* tell the tty driver that something has changed */
+		wake_up_interruptible(&tty->write_wait);
+	}
+
+	/* schedule_work(&mos7720_port->port->work); */
+}
+
+/*
+ * send_mos_cmd
+ *	this function will be used for sending command to device
+ */
+static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value,
+			__u16 index, void *data)
+{
+	int status;
+	unsigned int pipe;
+	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+	__u8 requesttype;
+	__u16 size = 0x0000;
+
+	if (value < MOS_MAX_PORT) {
+		if (product == MOSCHIP_DEVICE_ID_7715) {
+			value = value*0x100+0x100;
+		} else {
+			value = value*0x100+0x200;
+		}
+	} else {
+		value = 0x0000;
+		if ((product == MOSCHIP_DEVICE_ID_7715) &&
+		    (index != 0x08)) {
+			dbg("serial->product== MOSCHIP_DEVICE_ID_7715");
+			//index = 0x01 ;
+		}
+	}
+
+	if (request == MOS_WRITE) {
+		request = (__u8)MOS_WRITE;
+		requesttype = (__u8)0x40;
+		value  = value + (__u16)*((unsigned char *)data);
+		data = NULL;
+		pipe = usb_sndctrlpipe(serial->dev, 0);
+	} else {
+		request = (__u8)MOS_READ;
+		requesttype = (__u8)0xC0;
+		size = 0x01;
+		pipe = usb_rcvctrlpipe(serial->dev,0);
+	}
+
+	status = usb_control_msg(serial->dev, pipe, request, requesttype,
+				 value, index, data, size, MOS_WDR_TIMEOUT);
+
+	if (status < 0)
+		dbg("Command Write failed Value %x index %x\n",value,index);
+
+	return status;
+}
+
+static int mos7720_open(struct usb_serial_port *port, struct file * filp)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port0;
+	struct urb *urb;
+	struct moschip_serial *mos7720_serial;
+	struct moschip_port *mos7720_port;
+	int response;
+	int port_number;
+	char data;
+	int j;
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	port0 = serial->port[0];
+
+	mos7720_serial = usb_get_serial_data(serial);
+
+	if (mos7720_serial == NULL || port0 == NULL)
+		return -ENODEV;
+
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	/* Initialising the write urb pool */
+	for (j = 0; j < NUM_URBS; ++j) {
+		urb = usb_alloc_urb(0,SLAB_ATOMIC);
+		mos7720_port->write_urb_pool[j] = urb;
+
+		if (urb == NULL) {
+			err("No more urbs???");
+			continue;
+		}
+
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_KERNEL);
+		if (!urb->transfer_buffer) {
+			err("%s-out of memory for urb buffers.", __FUNCTION__);
+			continue;
+		}
+	}
+
+	 /* Initialize MCS7720 -- Write Init values to corresponding Registers
+	  *
+	  * Register Index
+	  * 1 : IER
+	  * 2 : FCR
+	  * 3 : LCR
+	  * 4 : MCR
+	  *
+	  * 0x08 : SP1/2 Control Reg
+	  */
+	port_number = port->number - port->serial->minor;
+	send_mos_cmd(port->serial, MOS_READ, port_number, UART_LSR, &data);
+	dbg("SS::%p LSR:%x\n",mos7720_port, data);
+
+	dbg("Check:Sending Command ..........");
+
+	data = 0x02;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x01, &data);
+	data = 0x02;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x02, &data);
+
+	data = 0x00;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
+	data = 0x00;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data);
+
+	data = 0xCF;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data);
+	data = 0x03;
+        mos7720_port->shadowLCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data);
+	data = 0x0b;
+        mos7720_port->shadowMCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+	data = 0x0b;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+
+	data = 0x00;
+	send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data);
+	data = 0x00;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data);
+
+/*	data = 0x00;
+	send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, port_number + 1, &data);
+	data = 0x03;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data);
+	data = 0x00;
+	send_mos_cmd(port->serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data);
+*/
+	data = 0x00;
+	send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data);
+
+	data = data | (port->number - port->serial->minor + 1);
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data);
+
+	data = 0x83;
+        mos7720_port->shadowLCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data);
+	data = 0x0c;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data);
+	data = 0x00;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
+	data = 0x03;
+        mos7720_port->shadowLCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data);
+	data = 0x0c;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
+	data = 0x0c;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
+
+//Matrix
+
+	/* force low_latency on so that our tty_push actually forces *
+	 * the data through,otherwise it is scheduled, and with      *
+	 * high data rates (like with OHCI) data can get lost.       */
+
+	if (port->tty)
+		port->tty->low_latency = 1;
+
+	/* see if we've set up our endpoint info yet   *
+	 * (can't set it up in mos7720_startup as the  *
+	 * structures were not set up at that time.)   */
+	if (!mos7720_serial->interrupt_started) {
+		dbg("Interrupt buffer NULL !!!");
+
+		/* not set up yet, so do it now */
+		mos7720_serial->interrupt_started = 1;
+
+		dbg("To Submit URB !!!");
+
+		/* set up our interrupt urb */
+		usb_fill_int_urb(port0->interrupt_in_urb, serial->dev,
+				 usb_rcvintpipe(serial->dev,
+				 		port->interrupt_in_endpointAddress),
+				 port0->interrupt_in_buffer,
+				 port0->interrupt_in_urb->transfer_buffer_length,
+				 mos7720_interrupt_callback, mos7720_port,
+				 port0->interrupt_in_urb->interval);
+
+		/* start interrupt read for this mos7720 this interrupt *
+	         * will continue as long as the mos7720 is connected    */
+		dbg("Submit URB over !!!");
+		response = usb_submit_urb(port0->interrupt_in_urb, GFP_KERNEL);
+		if (response)
+			dev_err(&port->dev,
+				"%s - Error %d submitting control urb",
+				__FUNCTION__, response);
+	}
+
+	/* set up our bulk in urb */
+	usb_fill_bulk_urb(port->read_urb, serial->dev,
+			  usb_rcvbulkpipe(serial->dev,
+			  		  port->bulk_in_endpointAddress),
+			  port->bulk_in_buffer,
+			  port->read_urb->transfer_buffer_length,
+			  mos7720_bulk_in_callback, mos7720_port);
+	response = usb_submit_urb(port->read_urb, GFP_KERNEL);
+	if (response)
+		dev_err(&port->dev,
+			"%s - Error %d submitting read urb", __FUNCTION__, response);
+
+	/* initialize our icount structure */
+	memset(&(mos7720_port->icount), 0x00, sizeof(mos7720_port->icount));
+
+	/* initialize our port settings */
+	mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */
+
+	/* send a open port command */
+	mos7720_port->open = 1;
+
+	return 0;
+}
+
+/*
+ * mos7720_chars_in_buffer
+ *	this function is called by the tty driver when it wants to know how many
+ *	bytes of data we currently have outstanding in the port (data that has
+ *	been written, but hasn't made it out the port yet)
+ *	If successful, we return the number of bytes left to be written in the
+ *	system,
+ *	Otherwise we return a negative error number.
+ */
+static int mos7720_chars_in_buffer(struct usb_serial_port *port)
+{
+	int i;
+	int chars = 0;
+	struct moschip_port *mos7720_port;
+
+	dbg("%s:entering ...........", __FUNCTION__);
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL) {
+		dbg("%s:leaving ...........", __FUNCTION__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
+			chars += URB_TRANSFER_BUFFER_SIZE;
+	}
+	dbg("%s - returns %d", __FUNCTION__, chars);
+	return chars;
+}
+
+static void mos7720_close(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial;
+	struct moschip_port *mos7720_port;
+	char data;
+	int j;
+
+	dbg("mos7720_close:entering...");
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return;
+
+	for (j = 0; j < NUM_URBS; ++j)
+		usb_kill_urb(mos7720_port->write_urb_pool[j]);
+
+	/* Freeing Write URBs */
+	for (j = 0; j < NUM_URBS; ++j) {
+		if (mos7720_port->write_urb_pool[j]) {
+			kfree(mos7720_port->write_urb_pool[j]->transfer_buffer);
+			usb_free_urb(mos7720_port->write_urb_pool[j]);
+		}
+	}
+
+	/* While closing port, shutdown all bulk read, write  *
+	 * and interrupt read if they exists                  */
+	if (serial->dev) {
+		dbg("Shutdown bulk write");
+		usb_kill_urb(port->write_urb);
+		dbg("Shutdown bulk read");
+		usb_kill_urb(port->read_urb);
+	}
+
+	data = 0x00;
+	send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor,
+		     0x04, &data);
+
+	data = 0x00;
+	send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor,
+		     0x01, &data);
+
+	mos7720_port->open = 0;
+
+	dbg("Leaving %s", __FUNCTION__);
+}
+
+static void mos7720_break(struct usb_serial_port *port, int break_state)
+{
+        unsigned char data;
+	struct usb_serial *serial;
+	struct moschip_port *mos7720_port;
+
+	dbg("Entering %s", __FUNCTION__);
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return;
+
+	if (break_state == -1)
+		data = mos7720_port->shadowLCR | UART_LCR_SBC;
+	else
+		data = mos7720_port->shadowLCR & ~UART_LCR_SBC;
+
+	mos7720_port->shadowLCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor,
+		     0x03, &data);
+
+	return;
+}
+
+/*
+ * mos7720_write_room
+ *	this function is called by the tty driver when it wants to know how many
+ *	bytes of data we can accept for a specific port.
+ *	If successful, we return the amount of room that we have for this port
+ *	Otherwise we return a negative error number.
+ */
+static int mos7720_write_room(struct usb_serial_port *port)
+{
+	struct moschip_port *mos7720_port;
+	int room = 0;
+	int i;
+
+	dbg("%s:entering ...........", __FUNCTION__);
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL) {
+		dbg("%s:leaving ...........", __FUNCTION__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
+			room += URB_TRANSFER_BUFFER_SIZE;
+	}
+
+	dbg("%s - returns %d", __FUNCTION__, room);
+	return room;
+}
+
+static int mos7720_write(struct usb_serial_port *port,
+			 const unsigned char *data, int count)
+{
+	int status;
+	int i;
+	int bytes_sent = 0;
+	int transfer_size;
+
+	struct moschip_port *mos7720_port;
+	struct usb_serial *serial;
+	struct urb    *urb;
+	const unsigned char *current_position = data;
+
+	dbg("%s:entering ...........", __FUNCTION__);
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL) {
+		dbg("mos7720_port is NULL");
+		return -ENODEV;
+	}
+
+	/* try to find a free urb in the list */
+	urb = NULL;
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
+			urb = mos7720_port->write_urb_pool[i];
+			dbg("URB:%d",i);
+			break;
+		}
+	}
+
+	if (urb == NULL) {
+		dbg("%s - no more free urbs", __FUNCTION__);
+		goto exit;
+	}
+
+	if (urb->transfer_buffer == NULL) {
+		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+					       GFP_KERNEL);
+		if (urb->transfer_buffer == NULL) {
+			err("%s no more kernel memory...", __FUNCTION__);
+			goto exit;
+		}
+	}
+	transfer_size = min (count, URB_TRANSFER_BUFFER_SIZE);
+
+	memcpy(urb->transfer_buffer, current_position, transfer_size);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size,
+			      urb->transfer_buffer);
+
+	/* fill urb with data and submit  */
+	usb_fill_bulk_urb(urb, serial->dev,
+			  usb_sndbulkpipe(serial->dev,
+			  		  port->bulk_out_endpointAddress),
+			  urb->transfer_buffer, transfer_size,
+			  mos7720_bulk_out_data_callback, mos7720_port);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb,GFP_ATOMIC);
+	if (status) {
+		err("%s - usb_submit_urb(write bulk) failed with status = %d",
+		    __FUNCTION__, status);
+		bytes_sent = status;
+		goto exit;
+	}
+	bytes_sent = transfer_size;
+
+exit:
+	return bytes_sent;
+}
+
+static void mos7720_throttle(struct usb_serial_port *port)
+{
+	struct moschip_port *mos7720_port;
+	struct tty_struct *tty;
+	int status;
+
+	dbg("%s- port %d\n", __FUNCTION__, port->number);
+
+	mos7720_port = usb_get_serial_port_data(port);
+
+	if (mos7720_port == NULL)
+		return;
+
+	if (!mos7720_port->open) {
+		dbg("port not opened");
+		return;
+	}
+
+	dbg("%s: Entering ..........", __FUNCTION__);
+
+	tty = port->tty;
+	if (!tty) {
+		dbg("%s - no tty available", __FUNCTION__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the stop character */
+	if (I_IXOFF(tty)) {
+		unsigned char stop_char = STOP_CHAR(tty);
+		status = mos7720_write(port, &stop_char, 1);
+		if (status <= 0)
+			return;
+	}
+
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		mos7720_port->shadowMCR &= ~UART_MCR_RTS;
+		status = send_mos_cmd(port->serial, MOS_WRITE,
+				      port->number - port->serial->minor,
+				      UART_MCR, &mos7720_port->shadowMCR);
+		if (status != 0)
+			return;
+	}
+}
+
+static void mos7720_unthrottle(struct usb_serial_port *port)
+{
+	struct tty_struct *tty;
+	int status;
+	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
+
+	if (mos7720_port == NULL)
+		return;
+
+	if (!mos7720_port->open) {
+		dbg("%s - port not opened", __FUNCTION__);
+		return;
+	}
+
+	dbg("%s: Entering ..........", __FUNCTION__);
+
+	tty = port->tty;
+	if (!tty) {
+		dbg("%s - no tty available", __FUNCTION__);
+		return;
+	}
+
+	/* if we are implementing XON/XOFF, send the start character */
+	if (I_IXOFF(tty)) {
+		unsigned char start_char = START_CHAR(tty);
+		status = mos7720_write(port, &start_char, 1);
+		if (status <= 0)
+			return;
+	}
+
+	/* if we are implementing RTS/CTS, toggle that line */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		mos7720_port->shadowMCR |= UART_MCR_RTS;
+		status = send_mos_cmd(port->serial, MOS_WRITE,
+				      port->number - port->serial->minor,
+				      UART_MCR, &mos7720_port->shadowMCR);
+		if (status != 0)
+			return;
+	}
+}
+
+static int set_higher_rates(struct moschip_port *mos7720_port,
+			    unsigned int baud)
+{
+	unsigned char data;
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+	int port_number;
+
+	if (mos7720_port == NULL)
+		return -EINVAL;
+
+	port = mos7720_port->port;
+	serial = port->serial;
+
+        /***********************************************
+         *      Init Sequence for higher rates
+         ***********************************************/
+	dbg("Sending Setting Commands ..........");
+	port_number = port->number - port->serial->minor;
+
+	data = 0x000;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
+	data = 0x000;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data);
+	data = 0x0CF;
+	send_mos_cmd(serial, MOS_WRITE, port->number, 0x02, &data);
+	data = 0x00b;
+        mos7720_port->shadowMCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+	data = 0x00b;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+
+	data = 0x000;
+	send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data);
+	data = 0x000;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data);
+
+
+        /***********************************************
+         *              Set for higher rates           *
+         ***********************************************/
+
+	data = baud * 0x10;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1,&data);
+
+	data = 0x003;
+	send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data);
+	data = 0x003;
+	send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data);
+
+	data = 0x02b;
+        mos7720_port->shadowMCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+	data = 0x02b;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+
+        /***********************************************
+         *              Set DLL/DLM
+         ***********************************************/
+
+	data = mos7720_port->shadowLCR | UART_LCR_DLAB;
+        mos7720_port->shadowLCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data);
+
+	data =  0x001; /* DLL */
+        send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data);
+	data =  0x000; /* DLM */
+        send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
+
+	data = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
+        mos7720_port->shadowLCR  = data;
+	send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data);
+
+	return 0;
+}
+
+/* baud rate information */
+struct divisor_table_entry
+{
+	__u32  baudrate;
+	__u16  divisor;
+};
+
+/* Define table of divisors for moschip 7720 hardware	   *
+ * These assume a 3.6864MHz crystal, the standard /16, and *
+ * MCR.7 = 0.						   */
+static struct divisor_table_entry divisor_table[] = {
+	{   50,		2304},
+	{   110,	1047},	/* 2094.545455 => 230450   => .0217 % over */
+	{   134,	857},	/* 1713.011152 => 230398.5 => .00065% under */
+	{   150,	768},
+	{   300,	384},
+	{   600,	192},
+	{   1200,	96},
+	{   1800,	64},
+	{   2400,	48},
+	{   4800,	24},
+	{   7200,	16},
+	{   9600,	12},
+	{   19200,	6},
+	{   38400,	3},
+	{   57600,	2},
+	{   115200,	1},
+};
+
+/*****************************************************************************
+ * calc_baud_rate_divisor
+ *	this function calculates the proper baud rate divisor for the specified
+ *	baud rate.
+ *****************************************************************************/
+static int calc_baud_rate_divisor(int baudrate, int *divisor)
+{
+	int i;
+	__u16 custom;
+	__u16 round1;
+	__u16 round;
+
+
+	dbg("%s - %d", __FUNCTION__, baudrate);
+
+	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
+		if (divisor_table[i].baudrate == baudrate) {
+			*divisor = divisor_table[i].divisor;
+			return 0;
+		}
+	}
+
+        /* After trying for all the standard baud rates    *
+         * Try calculating the divisor for this baud rate  */
+	if (baudrate > 75 &&  baudrate < 230400) {
+		/* get the divisor */
+		custom = (__u16)(230400L  / baudrate);
+
+		/* Check for round off */
+		round1 = (__u16)(2304000L / baudrate);
+		round = (__u16)(round1 - (custom * 10));
+		if (round > 4)
+			custom++;
+		*divisor = custom;
+
+		dbg("Baud %d = %d",baudrate, custom);
+		return 0;
+	}
+
+	dbg("Baud calculation Failed...");
+	return -EINVAL;
+}
+
+/*
+ * send_cmd_write_baud_rate
+ *	this function sends the proper command to change the baud rate of the
+ *	specified port.
+ */
+static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,
+				    int baudrate)
+{
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+	int divisor;
+	int status;
+	unsigned char data;
+	unsigned char number;
+
+	if (mos7720_port == NULL)
+		return -1;
+
+	port = mos7720_port->port;
+	serial = port->serial;
+
+	dbg("%s: Entering ..........", __FUNCTION__);
+
+	number = port->number - port->serial->minor;
+	dbg("%s - port = %d, baud = %d", __FUNCTION__, port->number, baudrate);
+
+        /* Calculate the Divisor */
+	status = calc_baud_rate_divisor(baudrate, &divisor);
+	if (status) {
+		err("%s - bad baud rate", __FUNCTION__);
+		return status;
+	}
+
+        /* Enable access to divisor latch */
+        data = mos7720_port->shadowLCR | UART_LCR_DLAB;
+        mos7720_port->shadowLCR  = data;
+        send_mos_cmd(serial, MOS_WRITE, number, UART_LCR, &data);
+
+	/* Write the divisor */
+	data = ((unsigned char)(divisor & 0xff));
+        send_mos_cmd(serial, MOS_WRITE, number, 0x00, &data);
+
+	data = ((unsigned char)((divisor & 0xff00) >> 8));
+        send_mos_cmd(serial, MOS_WRITE, number, 0x01, &data);
+
+        /* Disable access to divisor latch */
+        data = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
+        mos7720_port->shadowLCR = data;
+        send_mos_cmd(serial, MOS_WRITE, number, 0x03, &data);
+
+	return status;
+}
+
+/*
+ * change_port_settings
+ *	This routine is called to set the UART on the device to match
+ *      the specified new settings.
+ */
+static void change_port_settings(struct moschip_port *mos7720_port,
+				 struct termios *old_termios)
+{
+	struct usb_serial_port *port;
+	struct usb_serial *serial;
+	struct tty_struct *tty;
+	int baud;
+	unsigned cflag;
+	unsigned iflag;
+	__u8 mask = 0xff;
+	__u8 lData;
+	__u8 lParity;
+	__u8 lStop;
+	int status;
+	int port_number;
+	char data;
+
+	if (mos7720_port == NULL)
+		return ;
+
+	port = mos7720_port->port;
+	serial = port->serial;
+	port_number = port->number - port->serial->minor;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!mos7720_port->open) {
+		dbg("%s - port not opened", __FUNCTION__);
+		return;
+	}
+
+	tty = mos7720_port->port->tty;
+
+	if ((!tty) || (!tty->termios)) {
+		dbg("%s - no tty structures", __FUNCTION__);
+		return;
+	}
+
+	dbg("%s: Entering ..........", __FUNCTION__);
+
+	lData = UART_LCR_WLEN8;
+	lStop = 0x00;	/* 1 stop bit */
+	lParity = 0x00;	/* No parity */
+
+	cflag = tty->termios->c_cflag;
+	iflag = tty->termios->c_iflag;
+
+	/* Change the number of bits */
+	switch (cflag & CSIZE) {
+	case CS5:
+		lData = UART_LCR_WLEN5;
+		mask = 0x1f;
+		break;
+
+	case CS6:
+		lData = UART_LCR_WLEN6;
+		mask = 0x3f;
+		break;
+
+	case CS7:
+		lData = UART_LCR_WLEN7;
+		mask = 0x7f;
+		break;
+	default:
+	case CS8:
+		lData = UART_LCR_WLEN8;
+		break;
+	}
+
+	/* Change the Parity bit */
+	if (cflag & PARENB) {
+		if (cflag & PARODD) {
+			lParity = UART_LCR_PARITY;
+			dbg("%s - parity = odd", __FUNCTION__);
+		} else {
+			lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
+			dbg("%s - parity = even", __FUNCTION__);
+		}
+
+	} else {
+		dbg("%s - parity = none", __FUNCTION__);
+	}
+
+	if (cflag & CMSPAR)
+		lParity = lParity | 0x20;
+
+	/* Change the Stop bit */
+	if (cflag & CSTOPB) {
+		lStop = UART_LCR_STOP;
+		dbg("%s - stop bits = 2", __FUNCTION__);
+	} else {
+		lStop = 0x00;
+		dbg("%s - stop bits = 1", __FUNCTION__);
+	}
+
+#define LCR_BITS_MASK		0x03	/* Mask for bits/char field */
+#define LCR_STOP_MASK		0x04	/* Mask for stop bits field */
+#define LCR_PAR_MASK		0x38	/* Mask for parity field */
+
+	/* Update the LCR with the correct value */
+	mos7720_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+	mos7720_port->shadowLCR |= (lData | lParity | lStop);
+
+
+	/* Disable Interrupts */
+	data = 0x00;
+        send_mos_cmd(serial,MOS_WRITE,port->number - port->serial->minor, UART_IER, &data);
+
+	data = 0x00;
+        send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data);
+
+	data = 0xcf;
+        send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data);
+
+	/* Send the updated LCR value to the mos7720 */
+	data = mos7720_port->shadowLCR;
+        send_mos_cmd(serial, MOS_WRITE, port_number, UART_LCR, &data);
+
+        data = 0x00b;
+        mos7720_port->shadowMCR = data;
+        send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+        data = 0x00b;
+        send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data);
+
+	/* set up the MCR register and send it to the mos7720 */
+	mos7720_port->shadowMCR = UART_MCR_OUT2;
+	if (cflag & CBAUD)
+		mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS);
+
+	if (cflag & CRTSCTS) {
+		mos7720_port->shadowMCR |= (UART_MCR_XONANY);
+
+                /* To set hardware flow control to the specified *
+                 * serial port, in SP1/2_CONTROL_REG             */
+		if (port->number) {
+			data = 0x001;
+			send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT,
+				     0x08, &data);
+		} else {
+			data = 0x002;
+			send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT,
+				     0x08, &data);
+		}
+	} else {
+		mos7720_port->shadowMCR &= ~(UART_MCR_XONANY);
+	}
+
+	data = mos7720_port->shadowMCR;
+	send_mos_cmd(serial, MOS_WRITE, port_number, UART_MCR, &data);
+
+	/* Determine divisor based on baud rate */
+	baud = tty_get_baud_rate(tty);
+	if (!baud) {
+		/* pick a default, any default... */
+		dbg("Picked default baud...");
+		baud = 9600;
+	}
+
+	if (baud >= 230400) {
+		set_higher_rates(mos7720_port, baud);
+		/* Enable Interrupts */
+		data = 0x0c;
+		send_mos_cmd(serial, MOS_WRITE, port_number, UART_IER, &data);
+		return;
+	}
+
+	dbg("%s - baud rate = %d", __FUNCTION__, baud);
+	status = send_cmd_write_baud_rate(mos7720_port, baud);
+
+	/* Enable Interrupts */
+	data = 0x0c;
+	send_mos_cmd(serial, MOS_WRITE, port_number, UART_IER, &data);
+
+	if (port->read_urb->status != -EINPROGRESS) {
+		port->read_urb->dev = serial->dev;
+
+		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (status)
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    status);
+	}
+	return;
+}
+
+/*
+ * mos7720_set_termios
+ *	this function is called by the tty driver when it wants to change the
+ *	termios structure.
+ */
+static void mos7720_set_termios(struct usb_serial_port *port,
+				struct termios *old_termios)
+{
+	int status;
+	unsigned int cflag;
+	struct usb_serial *serial;
+	struct moschip_port *mos7720_port;
+	struct tty_struct *tty;
+
+	serial = port->serial;
+
+	mos7720_port = usb_get_serial_port_data(port);
+
+	if (mos7720_port == NULL)
+		return;
+
+	tty = port->tty;
+
+	if (!port->tty || !port->tty->termios) {
+		dbg("%s - no tty or termios", __FUNCTION__);
+		return;
+	}
+
+	if (!mos7720_port->open) {
+		dbg("%s - port not opened", __FUNCTION__);
+		return;
+	}
+
+	dbg("%s\n","setting termios - ASPIRE");
+
+	cflag = tty->termios->c_cflag;
+
+	if (!cflag) {
+		printk("%s %s\n",__FUNCTION__,"cflag is NULL");
+		return;
+	}
+
+	/* check that they really want us to change something */
+	if (old_termios) {
+		if ((cflag == old_termios->c_cflag) &&
+		    (RELEVANT_IFLAG(tty->termios->c_iflag) ==
+		     RELEVANT_IFLAG(old_termios->c_iflag))) {
+			dbg("Nothing to change");
+			return;
+		}
+	}
+
+	dbg("%s - clfag %08x iflag %08x", __FUNCTION__,
+	    tty->termios->c_cflag,
+	    RELEVANT_IFLAG(tty->termios->c_iflag));
+
+	if (old_termios)
+		dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__,
+		    old_termios->c_cflag,
+		    RELEVANT_IFLAG(old_termios->c_iflag));
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	/* change the port settings to the new ones specified */
+	change_port_settings(mos7720_port, old_termios);
+
+	if(!port->read_urb) {
+		dbg("%s","URB KILLED !!!!!\n");
+		return;
+	}
+
+	if(port->read_urb->status != -EINPROGRESS) {
+		port->read_urb->dev = serial->dev;
+		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (status)
+			dbg("usb_submit_urb(read bulk) failed, status = %d",
+			    status);
+	}
+	return;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct moschip_port *mos7720_port,
+			unsigned int __user *value)
+{
+	int count;
+	unsigned int result = 0;
+
+	count = mos7720_chars_in_buffer(mos7720_port->port);
+	if (count == 0) {
+		dbg("%s -- Empty", __FUNCTION__);
+		result = TIOCSER_TEMT;
+	}
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * get_number_bytes_avail - get number of bytes available
+ *
+ * Purpose: Let user call ioctl to get the count of number of bytes available.
+ */
+static int get_number_bytes_avail(struct moschip_port *mos7720_port,
+				  unsigned int __user *value)
+{
+	unsigned int result = 0;
+	struct tty_struct *tty = mos7720_port->port->tty;
+
+	if (!tty)
+		return -ENOIOCTLCMD;
+
+	result = tty->read_cnt;
+
+	dbg("%s(%d) = %d", __FUNCTION__,  mos7720_port->port->number, result);
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+
+	return -ENOIOCTLCMD;
+}
+
+static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd,
+			  unsigned int __user *value)
+{
+	unsigned int mcr ;
+	unsigned int arg;
+	unsigned char data;
+
+	struct usb_serial_port *port;
+
+	if (mos7720_port == NULL)
+		return -1;
+
+	port = (struct usb_serial_port*)mos7720_port->port;
+	mcr = mos7720_port->shadowMCR;
+
+	if (copy_from_user(&arg, value, sizeof(int)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS)
+			mcr |= UART_MCR_RTS;
+		if (arg & TIOCM_DTR)
+			mcr |= UART_MCR_RTS;
+		if (arg & TIOCM_LOOP)
+			mcr |= UART_MCR_LOOP;
+		break;
+
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			mcr &= ~UART_MCR_RTS;
+		if (arg & TIOCM_DTR)
+			mcr &= ~UART_MCR_RTS;
+		if (arg & TIOCM_LOOP)
+			mcr &= ~UART_MCR_LOOP;
+		break;
+
+	case TIOCMSET:
+		/* turn off the RTS and DTR and LOOPBACK
+		 * and then only turn on what was asked to */
+		mcr &=  ~(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_LOOP);
+		mcr |= ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0);
+		mcr |= ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0);
+		mcr |= ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0);
+		break;
+	}
+
+	mos7720_port->shadowMCR = mcr;
+
+	data = mos7720_port->shadowMCR;
+	send_mos_cmd(port->serial, MOS_WRITE,
+		     port->number - port->serial->minor, UART_MCR, &data);
+
+	return 0;
+}
+
+static int get_modem_info(struct moschip_port *mos7720_port,
+			  unsigned int __user *value)
+{
+	unsigned int result = 0;
+	unsigned int msr = mos7720_port->shadowMSR;
+	unsigned int mcr = mos7720_port->shadowMCR;
+
+	result = ((mcr & UART_MCR_DTR)	? TIOCM_DTR: 0)	  /* 0x002 */
+		  | ((mcr & UART_MCR_RTS)	? TIOCM_RTS: 0)   /* 0x004 */
+		  | ((msr & UART_MSR_CTS)	? TIOCM_CTS: 0)   /* 0x020 */
+		  | ((msr & UART_MSR_DCD)	? TIOCM_CAR: 0)   /* 0x040 */
+		  | ((msr & UART_MSR_RI)	? TIOCM_RI:  0)   /* 0x080 */
+		  | ((msr & UART_MSR_DSR)	? TIOCM_DSR: 0);  /* 0x100 */
+
+
+	dbg("%s -- %x", __FUNCTION__, result);
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_serial_info(struct moschip_port *mos7720_port,
+			   struct serial_struct __user *retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type		= PORT_16550A;
+	tmp.line		= mos7720_port->port->serial->minor;
+	tmp.port		= mos7720_port->port->number;
+	tmp.irq			= 0;
+	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+        tmp.xmit_fifo_size	= NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+	tmp.baud_base		= 9600;
+	tmp.close_delay		= 5*HZ;
+	tmp.closing_wait	= 30*HZ;
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int mos7720_ioctl(struct usb_serial_port *port, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct moschip_port *mos7720_port;
+	struct async_icount cnow;
+	struct async_icount cprev;
+	struct serial_icounter_struct icount;
+
+	mos7720_port = usb_get_serial_port_data(port);
+	if (mos7720_port == NULL)
+		return -ENODEV;
+
+	dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
+
+	switch (cmd) {
+	case TIOCINQ:
+		/* return number of bytes available */
+		dbg("%s (%d) TIOCINQ", __FUNCTION__,  port->number);
+		return get_number_bytes_avail(mos7720_port,
+					      (unsigned int __user *)arg);
+		break;
+
+	case TIOCSERGETLSR:
+		dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__,  port->number);
+		return get_lsr_info(mos7720_port, (unsigned int __user *)arg);
+		return 0;
+
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__,
+		    port->number);
+		return set_modem_info(mos7720_port, cmd,
+				      (unsigned int __user *)arg);
+
+	case TIOCMGET:
+		dbg("%s (%d) TIOCMGET", __FUNCTION__,  port->number);
+		return get_modem_info(mos7720_port,
+				      (unsigned int __user *)arg);
+
+	case TIOCGSERIAL:
+		dbg("%s (%d) TIOCGSERIAL", __FUNCTION__,  port->number);
+		return get_serial_info(mos7720_port,
+				       (struct serial_struct __user *)arg);
+
+	case TIOCSSERIAL:
+		dbg("%s (%d) TIOCSSERIAL", __FUNCTION__,  port->number);
+		break;
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __FUNCTION__,  port->number);
+		cprev = mos7720_port->icount;
+		while (1) {
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+			cnow = mos7720_port->icount;
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+				return -EIO; /* no change => error */
+			if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+			    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+			    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+			    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+				return 0;
+			}
+			cprev = cnow;
+		}
+		/* NOTREACHED */
+		break;
+
+	case TIOCGICOUNT:
+		cnow = mos7720_port->icount;
+		icount.cts = cnow.cts;
+		icount.dsr = cnow.dsr;
+		icount.rng = cnow.rng;
+		icount.dcd = cnow.dcd;
+		icount.rx = cnow.rx;
+		icount.tx = cnow.tx;
+		icount.frame = cnow.frame;
+		icount.overrun = cnow.overrun;
+		icount.parity = cnow.parity;
+		icount.brk = cnow.brk;
+		icount.buf_overrun = cnow.buf_overrun;
+
+		dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__,
+		    port->number, icount.rx, icount.tx );
+		if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
+			return -EFAULT;
+		return 0;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int mos7720_startup(struct usb_serial *serial)
+{
+	struct moschip_serial *mos7720_serial;
+	struct moschip_port *mos7720_port;
+	struct usb_device *dev;
+	int i;
+	char data;
+
+	dbg("%s: Entering ..........", __FUNCTION__);
+
+	if (!serial) {
+		dbg("Invalid Handler");
+		return -ENODEV;
+	}
+
+	dev = serial->dev;
+
+	/* create our private serial structure */
+	mos7720_serial = kzalloc(sizeof(struct moschip_serial), GFP_KERNEL);
+	if (mos7720_serial == NULL) {
+		err("%s - Out of memory", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	usb_set_serial_data(serial, mos7720_serial);
+
+	/* we set up the pointers to the endpoints in the mos7720_open *
+	 * function, as the structures aren't created yet.             */
+
+	/* set up port private structures */
+	for (i = 0; i < serial->num_ports; ++i) {
+		mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
+		if (mos7720_port == NULL) {
+			err("%s - Out of memory", __FUNCTION__);
+			usb_set_serial_data(serial, NULL);
+			kfree(mos7720_serial);
+			return -ENOMEM;
+		}
+
+		/* Initialize all port interrupt end point to port 0 int
+		 * endpoint.  Our device has only one interrupt endpoint
+		 * comman to all ports */
+		serial->port[i]->interrupt_in_endpointAddress = serial->port[0]->interrupt_in_endpointAddress;
+
+		mos7720_port->port = serial->port[i];
+		usb_set_serial_port_data(serial->port[i], mos7720_port);
+
+		dbg("port number is %d", serial->port[i]->number);
+		dbg("serial number is %d", serial->minor);
+	}
+
+
+	/* setting configuration feature to one */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			(__u8)0x03, 0x00,0x01,0x00, NULL, 0x00, 5*HZ);
+
+	send_mos_cmd(serial,MOS_READ,0x00, UART_LSR, &data);  // LSR For Port 1
+	dbg("LSR:%x",data);
+
+	send_mos_cmd(serial,MOS_READ,0x01, UART_LSR, &data);  // LSR For Port 2
+	dbg("LSR:%x",data);
+
+	return 0;
+}
+
+static void mos7720_shutdown(struct usb_serial *serial)
+{
+	int i;
+
+	/* free private structure allocated for serial port */
+	for (i=0; i < serial->num_ports; ++i) {
+		kfree(usb_get_serial_port_data(serial->port[i]));
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+
+	/* free private structure allocated for serial device */
+	kfree(usb_get_serial_data(serial));
+	usb_set_serial_data(serial, NULL);
+}
+
+static struct usb_serial_driver moschip7720_2port_driver = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"moschip7720",
+	},
+	.description		= "Moschip 2 port adapter",
+	.id_table		= moschip_port_id_table,
+	.num_interrupt_in	= 1,
+	.num_bulk_in		= 2,
+	.num_bulk_out		= 2,
+	.num_ports		= 2,
+	.open			= mos7720_open,
+	.close			= mos7720_close,
+	.throttle		= mos7720_throttle,
+	.unthrottle		= mos7720_unthrottle,
+	.attach			= mos7720_startup,
+	.shutdown		= mos7720_shutdown,
+	.ioctl			= mos7720_ioctl,
+	.set_termios		= mos7720_set_termios,
+	.write			= mos7720_write,
+	.write_room		= mos7720_write_room,
+	.chars_in_buffer	= mos7720_chars_in_buffer,
+	.break_ctl		= mos7720_break,
+	.read_bulk_callback	= mos7720_bulk_in_callback,
+};
+
+static struct usb_driver usb_driver = {
+	.name =		"moschip7720",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	moschip_port_id_table,
+};
+
+static int __init moschip7720_init(void)
+{
+	int retval;
+
+	dbg("%s: Entering ..........", __FUNCTION__);
+
+	/* Register with the usb serial */
+	retval = usb_serial_register(&moschip7720_2port_driver);
+	if (retval)
+		goto failed_port_device_register;
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	/* Register with the usb */
+	retval = usb_register(&usb_driver);
+	if (retval)
+		goto failed_usb_register;
+
+	return 0;
+
+failed_usb_register:
+	usb_serial_deregister(&moschip7720_2port_driver);
+
+failed_port_device_register:
+	return retval;
+}
+
+static void __exit moschip7720_exit(void)
+{
+	usb_deregister(&usb_driver);
+	usb_serial_deregister(&moschip7720_2port_driver);
+}
+
+module_init(moschip7720_init);
+module_exit(moschip7720_exit);
+
+/* Module information */
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");

+ 2 - 1
drivers/usb/serial/mos7840.c

@@ -2413,11 +2413,12 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
 	}
 	}
 
 
 	mos7840_port = mos7840_get_port_private(port);
 	mos7840_port = mos7840_get_port_private(port);
-	tty = mos7840_port->port->tty;
 
 
 	if (mos7840_port == NULL)
 	if (mos7840_port == NULL)
 		return -1;
 		return -1;
 
 
+	tty = mos7840_port->port->tty;
+
 	dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
 	dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
 
 
 	switch (cmd) {
 	switch (cmd) {

+ 666 - 31
drivers/usb/serial/sierra.c

@@ -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/kernel.h>
-#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
 #include <linux/tty.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 #include <linux/usb/serial.h>
 
 
+
 static struct usb_device_id id_table [] = {
 static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(0x1199, 0x0018) },	/* Sierra Wireless MC5720 */
 	{ USB_DEVICE(0x1199, 0x0018) },	/* Sierra Wireless MC5720 */
 	{ USB_DEVICE(0x1199, 0x0020) },	/* Sierra Wireless MC5725 */
 	{ 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, 0x0019) },	/* Sierra Wireless AirCard 595 */
 	{ USB_DEVICE(0x1199, 0x6802) },	/* Sierra Wireless MC8755 */
 	{ USB_DEVICE(0x1199, 0x6802) },	/* Sierra Wireless MC8755 */
 	{ USB_DEVICE(0x1199, 0x6803) },	/* Sierra Wireless MC8765 */
 	{ 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, 0x6812) },	/* Sierra Wireless MC8775 */
 	{ USB_DEVICE(0x1199, 0x6820) },	/* Sierra Wireless AirCard 875 */
 	{ 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);
 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 = {
 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 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 =		"sierra1",
+	},
+	.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_device = {
+static struct usb_serial_driver sierra_3port_device = {
 	.driver = {
 	.driver = {
-	.owner =		THIS_MODULE,
-	.name =			"Sierra_Wireless",
+		.owner =	THIS_MODULE,
+		.name =		"sierra3",
 	},
 	},
-	.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 (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)
 static int __init sierra_init(void)
 {
 {
 	int retval;
 	int retval;
-
-	retval = usb_serial_register(&sierra_device);
+	retval = usb_serial_register(&sierra_1port_device);
+	if (retval)
+		goto failed_1port_device_register;
+	retval = usb_serial_register(&sierra_3port_device);
 	if (retval)
 	if (retval)
-		return retval;
+		goto failed_3port_device_register;
+
+
 	retval = usb_register(&sierra_driver);
 	retval = usb_register(&sierra_driver);
 	if (retval)
 	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;
 	return retval;
 }
 }
 
 
 static void __exit sierra_exit(void)
 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_init(sierra_init);
 module_exit(sierra_exit);
 module_exit(sierra_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_USB_DEBUG
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages");
+#endif
+

+ 23 - 1
drivers/usb/storage/unusual_devs.h

@@ -55,7 +55,8 @@ UNUSUAL_DEV(  0x03eb, 0x2002, 0x0100, 0x0100,
                 US_SC_DEVICE, US_PR_DEVICE, NULL,
                 US_SC_DEVICE, US_PR_DEVICE, NULL,
                 US_FL_IGNORE_RESIDUE),
                 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",
 		"Mitsumi",
 		"USB FDD",
 		"USB FDD",
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		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_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ),
 		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 */
 /* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */
 UNUSUAL_DEV(  0x0424, 0x0fdc, 0x0210, 0x0210,
 UNUSUAL_DEV(  0x0424, 0x0fdc, 0x0210, 0x0210,
 		"SMSC",
 		"SMSC",
@@ -1291,6 +1306,13 @@ UNUSUAL_DEV(  0x0fce, 0xe030, 0x0000, 0x0000,
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_CAPACITY ),
 		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>
 /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
  * Tested on hardware version 1.10.
  * Tested on hardware version 1.10.
  * Entry is needed only for the initializer function override.
  * Entry is needed only for the initializer function override.