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