linux/drivers/usb/storage/sierra_ms.c
<<
>>
Prefs
   1#include <scsi/scsi.h>
   2#include <scsi/scsi_host.h>
   3#include <scsi/scsi_cmnd.h>
   4#include <scsi/scsi_device.h>
   5#include <linux/usb.h>
   6#include <linux/module.h>
   7#include <linux/slab.h>
   8
   9#include "usb.h"
  10#include "transport.h"
  11#include "protocol.h"
  12#include "scsiglue.h"
  13#include "sierra_ms.h"
  14#include "debug.h"
  15
  16#define SWIMS_USB_REQUEST_SetSwocMode   0x0B
  17#define SWIMS_USB_REQUEST_GetSwocInfo   0x0A
  18#define SWIMS_USB_INDEX_SetMode         0x0000
  19#define SWIMS_SET_MODE_Modem            0x0001
  20
  21#define TRU_NORMAL                      0x01
  22#define TRU_FORCE_MS                    0x02
  23#define TRU_FORCE_MODEM                 0x03
  24
  25static unsigned int swi_tru_install = 1;
  26module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR);
  27MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def),"
  28                 " 2=Force CD-Rom, 3=Force Modem)");
  29
  30struct swoc_info {
  31        __u8 rev;
  32        __u8 reserved[8];
  33        __u16 LinuxSKU;
  34        __u16 LinuxVer;
  35        __u8 reserved2[47];
  36} __attribute__((__packed__));
  37
  38static bool containsFullLinuxPackage(struct swoc_info *swocInfo)
  39{
  40        if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) ||
  41           (swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF))
  42                return true;
  43        else
  44                return false;
  45}
  46
  47static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode)
  48{
  49        int result;
  50        dev_dbg(&udev->dev, "SWIMS: %s", "DEVICE MODE SWITCH\n");
  51        result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
  52                        SWIMS_USB_REQUEST_SetSwocMode,  /* __u8 request      */
  53                        USB_TYPE_VENDOR | USB_DIR_OUT,  /* __u8 request type */
  54                        eSWocMode,                      /* __u16 value       */
  55                        0x0000,                         /* __u16 index       */
  56                        NULL,                           /* void *data        */
  57                        0,                              /* __u16 size        */
  58                        USB_CTRL_SET_TIMEOUT);          /* int timeout       */
  59        return result;
  60}
  61
  62
  63static int sierra_get_swoc_info(struct usb_device *udev,
  64                                struct swoc_info *swocInfo)
  65{
  66        int result;
  67
  68        dev_dbg(&udev->dev, "SWIMS: Attempting to get TRU-Install info\n");
  69
  70        result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
  71                        SWIMS_USB_REQUEST_GetSwocInfo,  /* __u8 request      */
  72                        USB_TYPE_VENDOR | USB_DIR_IN,   /* __u8 request type */
  73                        0,                              /* __u16 value       */
  74                        0,                              /* __u16 index       */
  75                        (void *) swocInfo,              /* void *data        */
  76                        sizeof(struct swoc_info),       /* __u16 size        */
  77                        USB_CTRL_SET_TIMEOUT);          /* int timeout       */
  78
  79        swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU);
  80        swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer);
  81        return result;
  82}
  83
  84static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo)
  85{
  86        dev_dbg(dev, "SWIMS: SWoC Rev: %02d\n", swocInfo->rev);
  87        dev_dbg(dev, "SWIMS: Linux SKU: %04X\n", swocInfo->LinuxSKU);
  88        dev_dbg(dev, "SWIMS: Linux Version: %04X\n", swocInfo->LinuxVer);
  89}
  90
  91
  92static ssize_t show_truinst(struct device *dev, struct device_attribute *attr,
  93                        char *buf)
  94{
  95        struct swoc_info *swocInfo;
  96        struct usb_interface *intf = to_usb_interface(dev);
  97        struct usb_device *udev = interface_to_usbdev(intf);
  98        int result;
  99        if (swi_tru_install == TRU_FORCE_MS) {
 100                result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n");
 101        } else {
 102                swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL);
 103                if (!swocInfo) {
 104                        snprintf(buf, PAGE_SIZE, "Error\n");
 105                        return -ENOMEM;
 106                }
 107                result = sierra_get_swoc_info(udev, swocInfo);
 108                if (result < 0) {
 109                        dev_dbg(dev, "SWIMS: failed SWoC query\n");
 110                        kfree(swocInfo);
 111                        snprintf(buf, PAGE_SIZE, "Error\n");
 112                        return -EIO;
 113                }
 114                debug_swoc(dev, swocInfo);
 115                result = snprintf(buf, PAGE_SIZE,
 116                        "REV=%02d SKU=%04X VER=%04X\n",
 117                        swocInfo->rev,
 118                        swocInfo->LinuxSKU,
 119                        swocInfo->LinuxVer);
 120                kfree(swocInfo);
 121        }
 122        return result;
 123}
 124static DEVICE_ATTR(truinst, S_IRUGO, show_truinst, NULL);
 125
 126int sierra_ms_init(struct us_data *us)
 127{
 128        int result, retries;
 129        struct swoc_info *swocInfo;
 130        struct usb_device *udev;
 131        struct Scsi_Host *sh;
 132
 133        retries = 3;
 134        result = 0;
 135        udev = us->pusb_dev;
 136
 137        sh = us_to_host(us);
 138        scsi_get_host_dev(sh);
 139
 140        /* Force Modem mode */
 141        if (swi_tru_install == TRU_FORCE_MODEM) {
 142                usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n");
 143                result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem);
 144                if (result < 0)
 145                        usb_stor_dbg(us, "SWIMS: Failed to switch to modem mode\n");
 146                return -EIO;
 147        }
 148        /* Force Mass Storage mode (keep CD-Rom) */
 149        else if (swi_tru_install == TRU_FORCE_MS) {
 150                usb_stor_dbg(us, "SWIMS: Forcing Mass Storage Mode\n");
 151                goto complete;
 152        }
 153        /* Normal TRU-Install Logic */
 154        else {
 155                usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n");
 156
 157                swocInfo = kmalloc(sizeof(struct swoc_info),
 158                                GFP_KERNEL);
 159                if (!swocInfo)
 160                        return -ENOMEM;
 161
 162                retries = 3;
 163                do {
 164                        retries--;
 165                        result = sierra_get_swoc_info(udev, swocInfo);
 166                        if (result < 0) {
 167                                usb_stor_dbg(us, "SWIMS: Failed SWoC query\n");
 168                                schedule_timeout_uninterruptible(2*HZ);
 169                        }
 170                } while (retries && result < 0);
 171
 172                if (result < 0) {
 173                        usb_stor_dbg(us, "SWIMS: Completely failed SWoC query\n");
 174                        kfree(swocInfo);
 175                        return -EIO;
 176                }
 177
 178                debug_swoc(&us->pusb_dev->dev, swocInfo);
 179
 180                /* If there is not Linux software on the TRU-Install device
 181                 * then switch to modem mode
 182                 */
 183                if (!containsFullLinuxPackage(swocInfo)) {
 184                        usb_stor_dbg(us, "SWIMS: Switching to Modem Mode\n");
 185                        result = sierra_set_ms_mode(udev,
 186                                SWIMS_SET_MODE_Modem);
 187                        if (result < 0)
 188                                usb_stor_dbg(us, "SWIMS: Failed to switch modem\n");
 189                        kfree(swocInfo);
 190                        return -EIO;
 191                }
 192                kfree(swocInfo);
 193        }
 194complete:
 195        result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst);
 196
 197        return 0;
 198}
 199
 200