diff options
-rw-r--r-- | drivers/usb/storage/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/storage/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/storage/sierra_ms.c | 207 | ||||
-rw-r--r-- | drivers/usb/storage/sierra_ms.h | 4 | ||||
-rw-r--r-- | drivers/usb/storage/unusual_devs.h | 6 | ||||
-rw-r--r-- | drivers/usb/storage/usb.c | 3 |
6 files changed, 231 insertions, 2 deletions
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 3d9249632ae1..c76034672c18 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -146,6 +146,18 @@ config USB_STORAGE_KARMA on the resulting scsi device node returns the Karma to normal operation. +config USB_STORAGE_SIERRA + bool "Sierra Wireless TRU-Install Feature Support" + depends on USB_STORAGE + help + Say Y here to include additional code to support Sierra Wireless + products with the TRU-Install feature (e.g., AC597E, AC881U). + + This code switches the Sierra Wireless device from being in + Mass Storage mode to Modem mode. It also has the ability to + support host software upgrades should full Linux support be added + to TRU-Install. + config USB_STORAGE_CYPRESS_ATACB bool "SAT emulation on Cypress USB/ATA Bridge with ATACB" depends on USB_STORAGE diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 4c596c766c53..bc3415b475c9 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -21,6 +21,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o +usb-storage-obj-$(CONFIG_USB_STORAGE_SIERRA) += sierra_ms.o usb-storage-obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += cypress_atacb.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c new file mode 100644 index 000000000000..4359a2cb42df --- /dev/null +++ b/drivers/usb/storage/sierra_ms.c @@ -0,0 +1,207 @@ +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <linux/usb.h> + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "scsiglue.h" +#include "sierra_ms.h" +#include "debug.h" + +#define SWIMS_USB_REQUEST_SetSwocMode 0x0B +#define SWIMS_USB_REQUEST_GetSwocInfo 0x0A +#define SWIMS_USB_INDEX_SetMode 0x0000 +#define SWIMS_SET_MODE_Modem 0x0001 + +#define TRU_NORMAL 0x01 +#define TRU_FORCE_MS 0x02 +#define TRU_FORCE_MODEM 0x03 + +static unsigned int swi_tru_install = 1; +module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def)," + " 2=Force CD-Rom, 3=Force Modem)"); + +struct swoc_info { + __u8 rev; + __u8 reserved[8]; + __u16 LinuxSKU; + __u16 LinuxVer; + __u8 reserved2[47]; +} __attribute__((__packed__)); + +static bool containsFullLinuxPackage(struct swoc_info *swocInfo) +{ + if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) || + (swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF)) + return true; + else + return false; +} + +static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) +{ + int result; + US_DEBUGP("SWIMS: %s", "DEVICE MODE SWITCH\n"); + result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */ + USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */ + eSWocMode, /* __u16 value */ + 0x0000, /* __u16 index */ + NULL, /* void *data */ + 0, /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + return result; +} + + +static int sierra_get_swoc_info(struct usb_device *udev, + struct swoc_info *swocInfo) +{ + int result; + + US_DEBUGP("SWIMS: Attempting to get TRU-Install info.\n"); + + result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */ + USB_TYPE_VENDOR | USB_DIR_IN, /* __u8 request type */ + 0, /* __u16 value */ + 0, /* __u16 index */ + (void *) swocInfo, /* void *data */ + sizeof(struct swoc_info), /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + + swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); + swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); + return result; +} + +static void debug_swoc(struct swoc_info *swocInfo) +{ + US_DEBUGP("SWIMS: SWoC Rev: %02d \n", swocInfo->rev); + US_DEBUGP("SWIMS: Linux SKU: %04X \n", swocInfo->LinuxSKU); + US_DEBUGP("SWIMS: Linux Version: %04X \n", swocInfo->LinuxVer); +} + + +static ssize_t show_truinst(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct swoc_info *swocInfo; + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev = interface_to_usbdev(intf); + int result; + if (swi_tru_install == TRU_FORCE_MS) { + result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n"); + } else { + swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); + if (!swocInfo) { + US_DEBUGP("SWIMS: Allocation failure\n"); + snprintf(buf, PAGE_SIZE, "Error\n"); + return -ENOMEM; + } + result = sierra_get_swoc_info(udev, swocInfo); + if (result < 0) { + US_DEBUGP("SWIMS: failed SWoC query\n"); + kfree(swocInfo); + snprintf(buf, PAGE_SIZE, "Error\n"); + return -EIO; + } + debug_swoc(swocInfo); + result = snprintf(buf, PAGE_SIZE, + "REV=%02d SKU=%04X VER=%04X\n", + swocInfo->rev, + swocInfo->LinuxSKU, + swocInfo->LinuxVer); + kfree(swocInfo); + } + return result; +} +static DEVICE_ATTR(truinst, S_IWUGO | S_IRUGO, show_truinst, NULL); + +int sierra_ms_init(struct us_data *us) +{ + int result, retries; + signed long delay_t; + struct swoc_info *swocInfo; + struct usb_device *udev; + struct Scsi_Host *sh; + struct scsi_device *sd; + + delay_t = 2; + retries = 3; + result = 0; + udev = us->pusb_dev; + + sh = us_to_host(us); + sd = scsi_get_host_dev(sh); + + US_DEBUGP("SWIMS: sierra_ms_init called\n"); + + /* Force Modem mode */ + if (swi_tru_install == TRU_FORCE_MODEM) { + US_DEBUGP("SWIMS: %s", "Forcing Modem Mode\n"); + result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); + if (result < 0) + US_DEBUGP("SWIMS: Failed to switch to modem mode.\n"); + return -EIO; + } + /* Force Mass Storage mode (keep CD-Rom) */ + else if (swi_tru_install == TRU_FORCE_MS) { + US_DEBUGP("SWIMS: %s", "Forcing Mass Storage Mode\n"); + goto complete; + } + /* Normal TRU-Install Logic */ + else { + US_DEBUGP("SWIMS: %s", "Normal SWoC Logic\n"); + + swocInfo = kmalloc(sizeof(struct swoc_info), + GFP_KERNEL); + if (!swocInfo) { + US_DEBUGP("SWIMS: %s", "Allocation failure\n"); + return -ENOMEM; + } + + retries = 3; + do { + retries--; + result = sierra_get_swoc_info(udev, swocInfo); + if (result < 0) { + US_DEBUGP("SWIMS: %s", "Failed SWoC query\n"); + schedule_timeout_uninterruptible(2*HZ); + } + } while (retries && result < 0); + + if (result < 0) { + US_DEBUGP("SWIMS: %s", + "Completely failed SWoC query\n"); + kfree(swocInfo); + return -EIO; + } + + debug_swoc(swocInfo); + + /* If there is not Linux software on the TRU-Install device + * then switch to modem mode + */ + if (!containsFullLinuxPackage(swocInfo)) { + US_DEBUGP("SWIMS: %s", + "Switching to Modem Mode\n"); + result = sierra_set_ms_mode(udev, + SWIMS_SET_MODE_Modem); + if (result < 0) + US_DEBUGP("SWIMS: Failed to switch modem\n"); + kfree(swocInfo); + return -EIO; + } + kfree(swocInfo); + } +complete: + result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); + + return USB_STOR_TRANSPORT_GOOD; +} + diff --git a/drivers/usb/storage/sierra_ms.h b/drivers/usb/storage/sierra_ms.h new file mode 100644 index 000000000000..bb48634ac1fc --- /dev/null +++ b/drivers/usb/storage/sierra_ms.h @@ -0,0 +1,4 @@ +#ifndef _SIERRA_MS_H_ +#define _SIERRA_MS_H_ +extern int sierra_ms_init(struct us_data *us); +#endif diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 11249bf5d74f..927572377f32 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1569,6 +1569,7 @@ UNUSUAL_DEV( 0x10d6, 0x2200, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, 0), +#ifdef CONFIG_USB_STORAGE_SIERRA /* Reported by Kevin Lloyd <linux@sierrawireless.com> * Entry is needed for the initializer function override, * which instructs the device to load as a modem @@ -1577,8 +1578,9 @@ UNUSUAL_DEV( 0x10d6, 0x2200, 0x0100, 0x0100, UNUSUAL_DEV( 0x1199, 0x0fff, 0x0000, 0x9999, "Sierra Wireless", "USB MMC Storage", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_DEVICE), + US_SC_DEVICE, US_PR_DEVICE, sierra_ms_init, + 0), +#endif /* Reported by Jaco Kroon <jaco@kroon.co.za> * The usb-storage module found on the Digitech GNX4 (and supposedly other diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index bfea851be985..73679aa506de 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -102,6 +102,9 @@ #ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB #include "cypress_atacb.h" #endif +#ifdef CONFIG_USB_STORAGE_SIERRA +#include "sierra_ms.h" +#endif /* Some informational data */ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); |