132fe5e39SKevin Lloyd #include <scsi/scsi.h> 232fe5e39SKevin Lloyd #include <scsi/scsi_host.h> 332fe5e39SKevin Lloyd #include <scsi/scsi_cmnd.h> 432fe5e39SKevin Lloyd #include <scsi/scsi_device.h> 532fe5e39SKevin Lloyd #include <linux/usb.h> 66eb0de82SPaul Gortmaker #include <linux/module.h> 75a0e3ad6STejun Heo #include <linux/slab.h> 832fe5e39SKevin Lloyd 932fe5e39SKevin Lloyd #include "usb.h" 1032fe5e39SKevin Lloyd #include "transport.h" 1132fe5e39SKevin Lloyd #include "protocol.h" 1232fe5e39SKevin Lloyd #include "scsiglue.h" 1332fe5e39SKevin Lloyd #include "sierra_ms.h" 1432fe5e39SKevin Lloyd #include "debug.h" 1532fe5e39SKevin Lloyd 1632fe5e39SKevin Lloyd #define SWIMS_USB_REQUEST_SetSwocMode 0x0B 1732fe5e39SKevin Lloyd #define SWIMS_USB_REQUEST_GetSwocInfo 0x0A 1832fe5e39SKevin Lloyd #define SWIMS_USB_INDEX_SetMode 0x0000 1932fe5e39SKevin Lloyd #define SWIMS_SET_MODE_Modem 0x0001 2032fe5e39SKevin Lloyd 2132fe5e39SKevin Lloyd #define TRU_NORMAL 0x01 2232fe5e39SKevin Lloyd #define TRU_FORCE_MS 0x02 2332fe5e39SKevin Lloyd #define TRU_FORCE_MODEM 0x03 2432fe5e39SKevin Lloyd 2532fe5e39SKevin Lloyd static unsigned int swi_tru_install = 1; 2632fe5e39SKevin Lloyd module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR); 2732fe5e39SKevin Lloyd MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def)," 2832fe5e39SKevin Lloyd " 2=Force CD-Rom, 3=Force Modem)"); 2932fe5e39SKevin Lloyd 3032fe5e39SKevin Lloyd struct swoc_info { 3132fe5e39SKevin Lloyd __u8 rev; 3232fe5e39SKevin Lloyd __u8 reserved[8]; 3332fe5e39SKevin Lloyd __u16 LinuxSKU; 3432fe5e39SKevin Lloyd __u16 LinuxVer; 3532fe5e39SKevin Lloyd __u8 reserved2[47]; 3632fe5e39SKevin Lloyd } __attribute__((__packed__)); 3732fe5e39SKevin Lloyd 3832fe5e39SKevin Lloyd static bool containsFullLinuxPackage(struct swoc_info *swocInfo) 3932fe5e39SKevin Lloyd { 4032fe5e39SKevin Lloyd if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) || 4132fe5e39SKevin Lloyd (swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF)) 4232fe5e39SKevin Lloyd return true; 4332fe5e39SKevin Lloyd else 4432fe5e39SKevin Lloyd return false; 4532fe5e39SKevin Lloyd } 4632fe5e39SKevin Lloyd 4732fe5e39SKevin Lloyd static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) 4832fe5e39SKevin Lloyd { 4932fe5e39SKevin Lloyd int result; 50*191648d0SJoe Perches dev_dbg(&udev->dev, "SWIMS: %s", "DEVICE MODE SWITCH\n"); 5132fe5e39SKevin Lloyd result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 5232fe5e39SKevin Lloyd SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */ 5332fe5e39SKevin Lloyd USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */ 5432fe5e39SKevin Lloyd eSWocMode, /* __u16 value */ 5532fe5e39SKevin Lloyd 0x0000, /* __u16 index */ 5632fe5e39SKevin Lloyd NULL, /* void *data */ 5732fe5e39SKevin Lloyd 0, /* __u16 size */ 5832fe5e39SKevin Lloyd USB_CTRL_SET_TIMEOUT); /* int timeout */ 5932fe5e39SKevin Lloyd return result; 6032fe5e39SKevin Lloyd } 6132fe5e39SKevin Lloyd 6232fe5e39SKevin Lloyd 6332fe5e39SKevin Lloyd static int sierra_get_swoc_info(struct usb_device *udev, 6432fe5e39SKevin Lloyd struct swoc_info *swocInfo) 6532fe5e39SKevin Lloyd { 6632fe5e39SKevin Lloyd int result; 6732fe5e39SKevin Lloyd 68*191648d0SJoe Perches dev_dbg(&udev->dev, "SWIMS: Attempting to get TRU-Install info\n"); 6932fe5e39SKevin Lloyd 7032fe5e39SKevin Lloyd result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 7132fe5e39SKevin Lloyd SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */ 7232fe5e39SKevin Lloyd USB_TYPE_VENDOR | USB_DIR_IN, /* __u8 request type */ 7332fe5e39SKevin Lloyd 0, /* __u16 value */ 7432fe5e39SKevin Lloyd 0, /* __u16 index */ 7532fe5e39SKevin Lloyd (void *) swocInfo, /* void *data */ 7632fe5e39SKevin Lloyd sizeof(struct swoc_info), /* __u16 size */ 7732fe5e39SKevin Lloyd USB_CTRL_SET_TIMEOUT); /* int timeout */ 7832fe5e39SKevin Lloyd 7932fe5e39SKevin Lloyd swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); 8032fe5e39SKevin Lloyd swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); 8132fe5e39SKevin Lloyd return result; 8232fe5e39SKevin Lloyd } 8332fe5e39SKevin Lloyd 84*191648d0SJoe Perches static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo) 8532fe5e39SKevin Lloyd { 86*191648d0SJoe Perches dev_dbg(dev, "SWIMS: SWoC Rev: %02d\n", swocInfo->rev); 87*191648d0SJoe Perches dev_dbg(dev, "SWIMS: Linux SKU: %04X\n", swocInfo->LinuxSKU); 88*191648d0SJoe Perches dev_dbg(dev, "SWIMS: Linux Version: %04X\n", swocInfo->LinuxVer); 8932fe5e39SKevin Lloyd } 9032fe5e39SKevin Lloyd 9132fe5e39SKevin Lloyd 9232fe5e39SKevin Lloyd static ssize_t show_truinst(struct device *dev, struct device_attribute *attr, 9332fe5e39SKevin Lloyd char *buf) 9432fe5e39SKevin Lloyd { 9532fe5e39SKevin Lloyd struct swoc_info *swocInfo; 9632fe5e39SKevin Lloyd struct usb_interface *intf = to_usb_interface(dev); 9732fe5e39SKevin Lloyd struct usb_device *udev = interface_to_usbdev(intf); 9832fe5e39SKevin Lloyd int result; 9932fe5e39SKevin Lloyd if (swi_tru_install == TRU_FORCE_MS) { 10032fe5e39SKevin Lloyd result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n"); 10132fe5e39SKevin Lloyd } else { 10232fe5e39SKevin Lloyd swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); 10332fe5e39SKevin Lloyd if (!swocInfo) { 10432fe5e39SKevin Lloyd snprintf(buf, PAGE_SIZE, "Error\n"); 10532fe5e39SKevin Lloyd return -ENOMEM; 10632fe5e39SKevin Lloyd } 10732fe5e39SKevin Lloyd result = sierra_get_swoc_info(udev, swocInfo); 10832fe5e39SKevin Lloyd if (result < 0) { 109*191648d0SJoe Perches dev_dbg(dev, "SWIMS: failed SWoC query\n"); 11032fe5e39SKevin Lloyd kfree(swocInfo); 11132fe5e39SKevin Lloyd snprintf(buf, PAGE_SIZE, "Error\n"); 11232fe5e39SKevin Lloyd return -EIO; 11332fe5e39SKevin Lloyd } 114*191648d0SJoe Perches debug_swoc(dev, swocInfo); 11532fe5e39SKevin Lloyd result = snprintf(buf, PAGE_SIZE, 11632fe5e39SKevin Lloyd "REV=%02d SKU=%04X VER=%04X\n", 11732fe5e39SKevin Lloyd swocInfo->rev, 11832fe5e39SKevin Lloyd swocInfo->LinuxSKU, 11932fe5e39SKevin Lloyd swocInfo->LinuxVer); 12032fe5e39SKevin Lloyd kfree(swocInfo); 12132fe5e39SKevin Lloyd } 12232fe5e39SKevin Lloyd return result; 12332fe5e39SKevin Lloyd } 124d9624e75SGreg Kroah-Hartman static DEVICE_ATTR(truinst, S_IRUGO, show_truinst, NULL); 12532fe5e39SKevin Lloyd 12632fe5e39SKevin Lloyd int sierra_ms_init(struct us_data *us) 12732fe5e39SKevin Lloyd { 12832fe5e39SKevin Lloyd int result, retries; 12932fe5e39SKevin Lloyd struct swoc_info *swocInfo; 13032fe5e39SKevin Lloyd struct usb_device *udev; 13132fe5e39SKevin Lloyd struct Scsi_Host *sh; 13232fe5e39SKevin Lloyd 13332fe5e39SKevin Lloyd retries = 3; 13432fe5e39SKevin Lloyd result = 0; 13532fe5e39SKevin Lloyd udev = us->pusb_dev; 13632fe5e39SKevin Lloyd 13732fe5e39SKevin Lloyd sh = us_to_host(us); 1380220a3f0SAlan Cox scsi_get_host_dev(sh); 13932fe5e39SKevin Lloyd 14032fe5e39SKevin Lloyd /* Force Modem mode */ 14132fe5e39SKevin Lloyd if (swi_tru_install == TRU_FORCE_MODEM) { 142*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n"); 14332fe5e39SKevin Lloyd result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); 14432fe5e39SKevin Lloyd if (result < 0) 145*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Failed to switch to modem mode\n"); 14632fe5e39SKevin Lloyd return -EIO; 14732fe5e39SKevin Lloyd } 14832fe5e39SKevin Lloyd /* Force Mass Storage mode (keep CD-Rom) */ 14932fe5e39SKevin Lloyd else if (swi_tru_install == TRU_FORCE_MS) { 150*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Forcing Mass Storage Mode\n"); 15132fe5e39SKevin Lloyd goto complete; 15232fe5e39SKevin Lloyd } 15332fe5e39SKevin Lloyd /* Normal TRU-Install Logic */ 15432fe5e39SKevin Lloyd else { 155*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n"); 15632fe5e39SKevin Lloyd 15732fe5e39SKevin Lloyd swocInfo = kmalloc(sizeof(struct swoc_info), 15832fe5e39SKevin Lloyd GFP_KERNEL); 159*191648d0SJoe Perches if (!swocInfo) 16032fe5e39SKevin Lloyd return -ENOMEM; 16132fe5e39SKevin Lloyd 16232fe5e39SKevin Lloyd retries = 3; 16332fe5e39SKevin Lloyd do { 16432fe5e39SKevin Lloyd retries--; 16532fe5e39SKevin Lloyd result = sierra_get_swoc_info(udev, swocInfo); 16632fe5e39SKevin Lloyd if (result < 0) { 167*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Failed SWoC query\n"); 16832fe5e39SKevin Lloyd schedule_timeout_uninterruptible(2*HZ); 16932fe5e39SKevin Lloyd } 17032fe5e39SKevin Lloyd } while (retries && result < 0); 17132fe5e39SKevin Lloyd 17232fe5e39SKevin Lloyd if (result < 0) { 173*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Completely failed SWoC query\n"); 17432fe5e39SKevin Lloyd kfree(swocInfo); 17532fe5e39SKevin Lloyd return -EIO; 17632fe5e39SKevin Lloyd } 17732fe5e39SKevin Lloyd 178*191648d0SJoe Perches debug_swoc(&us->pusb_dev->dev, swocInfo); 17932fe5e39SKevin Lloyd 18032fe5e39SKevin Lloyd /* If there is not Linux software on the TRU-Install device 18132fe5e39SKevin Lloyd * then switch to modem mode 18232fe5e39SKevin Lloyd */ 18332fe5e39SKevin Lloyd if (!containsFullLinuxPackage(swocInfo)) { 184*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Switching to Modem Mode\n"); 18532fe5e39SKevin Lloyd result = sierra_set_ms_mode(udev, 18632fe5e39SKevin Lloyd SWIMS_SET_MODE_Modem); 18732fe5e39SKevin Lloyd if (result < 0) 188*191648d0SJoe Perches usb_stor_dbg(us, "SWIMS: Failed to switch modem\n"); 18932fe5e39SKevin Lloyd kfree(swocInfo); 19032fe5e39SKevin Lloyd return -EIO; 19132fe5e39SKevin Lloyd } 19232fe5e39SKevin Lloyd kfree(swocInfo); 19332fe5e39SKevin Lloyd } 19432fe5e39SKevin Lloyd complete: 19532fe5e39SKevin Lloyd result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); 19632fe5e39SKevin Lloyd 197be475d90SAlan Stern return 0; 19832fe5e39SKevin Lloyd } 19932fe5e39SKevin Lloyd 200