0001
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,
0054 USB_TYPE_VENDOR | USB_DIR_OUT,
0055 eSWocMode,
0056 0x0000,
0057 NULL,
0058 0,
0059 USB_CTRL_SET_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,
0073 USB_TYPE_VENDOR | USB_DIR_IN,
0074 0,
0075 0,
0076 (void *) swocInfo,
0077 sizeof(struct swoc_info),
0078 USB_CTRL_SET_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
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
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
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
0177
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