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