USB Storage Sierra: TRU-Install feature update

This patch upgrades the support for the Sierra Wireless TRU-Install
feature (i.e. zeroCD) to allow for future support of Linux enabled
TRU-Install devices.

By default all devices that do not have a Linux enabled TRU-Install
device (i.e. the device does not have a Linux package on the virtual CD
partition) will be switched into "modem mode." Devices that do contain a
Linux package in the TRU-Install virtual CD will be allowed to enumerate
as a CD-Rom so that either (a) a user can install the packaged software
or (b) a user-space application (e.g. udev) can switch it to modem mode.

This patch does allow for manual override by adding a usb-storage module
parameter 'swi_tru_install' which can force the modem into either mode
regardless of what packages it contains.

Signed-off-by: Kevin Lloyd <klloyd@sierrawireless.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Kevin Lloyd 2008-07-10 14:14:57 -07:00 committed by Greg Kroah-Hartman
parent 0585e4dfe5
commit 32fe5e3934
6 changed files with 231 additions and 2 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
#ifndef _SIERRA_MS_H_
#define _SIERRA_MS_H_
extern int sierra_ms_init(struct us_data *us);
#endif

View File

@ -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

View File

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