Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <scsi/scsi.h>
0003 #include <scsi/scsi_host.h>
0004 #include <scsi/scsi_cmnd.h>
0005 #include <scsi/scsi_device.h>
0006 #include <linux/usb.h>
0007 #include <linux/module.h>
0008 #include <linux/slab.h>
0009 
0010 #include "usb.h"
0011 #include "transport.h"
0012 #include "protocol.h"
0013 #include "scsiglue.h"
0014 #include "sierra_ms.h"
0015 #include "debug.h"
0016 
0017 #define SWIMS_USB_REQUEST_SetSwocMode   0x0B
0018 #define SWIMS_USB_REQUEST_GetSwocInfo   0x0A
0019 #define SWIMS_USB_INDEX_SetMode     0x0000
0020 #define SWIMS_SET_MODE_Modem        0x0001
0021 
0022 #define TRU_NORMAL          0x01
0023 #define TRU_FORCE_MS            0x02
0024 #define TRU_FORCE_MODEM         0x03
0025 
0026 static unsigned int swi_tru_install = 1;
0027 module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR);
0028 MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def),"
0029          " 2=Force CD-Rom, 3=Force Modem)");
0030 
0031 struct swoc_info {
0032     __u8 rev;
0033     __u8 reserved[8];
0034     __u16 LinuxSKU;
0035     __u16 LinuxVer;
0036     __u8 reserved2[47];
0037 } __attribute__((__packed__));
0038 
0039 static bool containsFullLinuxPackage(struct swoc_info *swocInfo)
0040 {
0041     if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) ||
0042        (swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF))
0043         return true;
0044     else
0045         return false;
0046 }
0047 
0048 static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode)
0049 {
0050     int result;
0051     dev_dbg(&udev->dev, "SWIMS: %s", "DEVICE MODE SWITCH\n");
0052     result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0053             SWIMS_USB_REQUEST_SetSwocMode,  /* __u8 request      */
0054             USB_TYPE_VENDOR | USB_DIR_OUT,  /* __u8 request type */
0055             eSWocMode,          /* __u16 value       */
0056             0x0000,             /* __u16 index       */
0057             NULL,               /* void *data        */
0058             0,              /* __u16 size        */
0059             USB_CTRL_SET_TIMEOUT);      /* int timeout       */
0060     return result;
0061 }
0062 
0063 
0064 static int sierra_get_swoc_info(struct usb_device *udev,
0065                 struct swoc_info *swocInfo)
0066 {
0067     int result;
0068 
0069     dev_dbg(&udev->dev, "SWIMS: Attempting to get TRU-Install info\n");
0070 
0071     result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0072             SWIMS_USB_REQUEST_GetSwocInfo,  /* __u8 request      */
0073             USB_TYPE_VENDOR | USB_DIR_IN,   /* __u8 request type */
0074             0,              /* __u16 value       */
0075             0,              /* __u16 index       */
0076             (void *) swocInfo,      /* void *data        */
0077             sizeof(struct swoc_info),   /* __u16 size        */
0078             USB_CTRL_SET_TIMEOUT);      /* int timeout       */
0079 
0080     swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU);
0081     swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer);
0082     return result;
0083 }
0084 
0085 static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo)
0086 {
0087     dev_dbg(dev, "SWIMS: SWoC Rev: %02d\n", swocInfo->rev);
0088     dev_dbg(dev, "SWIMS: Linux SKU: %04X\n", swocInfo->LinuxSKU);
0089     dev_dbg(dev, "SWIMS: Linux Version: %04X\n", swocInfo->LinuxVer);
0090 }
0091 
0092 
0093 static ssize_t truinst_show(struct device *dev, struct device_attribute *attr,
0094             char *buf)
0095 {
0096     struct swoc_info *swocInfo;
0097     struct usb_interface *intf = to_usb_interface(dev);
0098     struct usb_device *udev = interface_to_usbdev(intf);
0099     int result;
0100     if (swi_tru_install == TRU_FORCE_MS) {
0101         result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n");
0102     } else {
0103         swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL);
0104         if (!swocInfo) {
0105             snprintf(buf, PAGE_SIZE, "Error\n");
0106             return -ENOMEM;
0107         }
0108         result = sierra_get_swoc_info(udev, swocInfo);
0109         if (result < 0) {
0110             dev_dbg(dev, "SWIMS: failed SWoC query\n");
0111             kfree(swocInfo);
0112             snprintf(buf, PAGE_SIZE, "Error\n");
0113             return -EIO;
0114         }
0115         debug_swoc(dev, swocInfo);
0116         result = snprintf(buf, PAGE_SIZE,
0117             "REV=%02d SKU=%04X VER=%04X\n",
0118             swocInfo->rev,
0119             swocInfo->LinuxSKU,
0120             swocInfo->LinuxVer);
0121         kfree(swocInfo);
0122     }
0123     return result;
0124 }
0125 static DEVICE_ATTR_RO(truinst);
0126 
0127 int sierra_ms_init(struct us_data *us)
0128 {
0129     int result, retries;
0130     struct swoc_info *swocInfo;
0131     struct usb_device *udev;
0132 
0133     udev = us->pusb_dev;
0134 
0135     /* Force Modem mode */
0136     if (swi_tru_install == TRU_FORCE_MODEM) {
0137         usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n");
0138         result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem);
0139         if (result < 0)
0140             usb_stor_dbg(us, "SWIMS: Failed to switch to modem mode\n");
0141         return -EIO;
0142     }
0143     /* Force Mass Storage mode (keep CD-Rom) */
0144     else if (swi_tru_install == TRU_FORCE_MS) {
0145         usb_stor_dbg(us, "SWIMS: Forcing Mass Storage Mode\n");
0146         goto complete;
0147     }
0148     /* Normal TRU-Install Logic */
0149     else {
0150         usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n");
0151 
0152         swocInfo = kmalloc(sizeof(struct swoc_info),
0153                 GFP_KERNEL);
0154         if (!swocInfo)
0155             return -ENOMEM;
0156 
0157         retries = 3;
0158         do {
0159             retries--;
0160             result = sierra_get_swoc_info(udev, swocInfo);
0161             if (result < 0) {
0162                 usb_stor_dbg(us, "SWIMS: Failed SWoC query\n");
0163                 schedule_timeout_uninterruptible(2*HZ);
0164             }
0165         } while (retries && result < 0);
0166 
0167         if (result < 0) {
0168             usb_stor_dbg(us, "SWIMS: Completely failed SWoC query\n");
0169             kfree(swocInfo);
0170             return -EIO;
0171         }
0172 
0173         debug_swoc(&us->pusb_dev->dev, swocInfo);
0174 
0175         /*
0176          * If there is not Linux software on the TRU-Install device
0177          * then switch to modem mode
0178          */
0179         if (!containsFullLinuxPackage(swocInfo)) {
0180             usb_stor_dbg(us, "SWIMS: Switching to Modem Mode\n");
0181             result = sierra_set_ms_mode(udev,
0182                 SWIMS_SET_MODE_Modem);
0183             if (result < 0)
0184                 usb_stor_dbg(us, "SWIMS: Failed to switch modem\n");
0185             kfree(swocInfo);
0186             return -EIO;
0187         }
0188         kfree(swocInfo);
0189     }
0190 complete:
0191     return device_create_file(&us->pusb_intf->dev, &dev_attr_truinst);
0192 }
0193