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