summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/storage/Kconfig12
-rw-r--r--drivers/usb/storage/Makefile1
-rw-r--r--drivers/usb/storage/sierra_ms.c207
-rw-r--r--drivers/usb/storage/sierra_ms.h4
-rw-r--r--drivers/usb/storage/unusual_devs.h6
-rw-r--r--drivers/usb/storage/usb.c3
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>");