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> 6*5a0e3ad6STejun Heo #include <linux/slab.h> 732fe5e39SKevin Lloyd 832fe5e39SKevin Lloyd #include "usb.h" 932fe5e39SKevin Lloyd #include "transport.h" 1032fe5e39SKevin Lloyd #include "protocol.h" 1132fe5e39SKevin Lloyd #include "scsiglue.h" 1232fe5e39SKevin Lloyd #include "sierra_ms.h" 1332fe5e39SKevin Lloyd #include "debug.h" 1432fe5e39SKevin Lloyd 1532fe5e39SKevin Lloyd #define SWIMS_USB_REQUEST_SetSwocMode 0x0B 1632fe5e39SKevin Lloyd #define SWIMS_USB_REQUEST_GetSwocInfo 0x0A 1732fe5e39SKevin Lloyd #define SWIMS_USB_INDEX_SetMode 0x0000 1832fe5e39SKevin Lloyd #define SWIMS_SET_MODE_Modem 0x0001 1932fe5e39SKevin Lloyd 2032fe5e39SKevin Lloyd #define TRU_NORMAL 0x01 2132fe5e39SKevin Lloyd #define TRU_FORCE_MS 0x02 2232fe5e39SKevin Lloyd #define TRU_FORCE_MODEM 0x03 2332fe5e39SKevin Lloyd 2432fe5e39SKevin Lloyd static unsigned int swi_tru_install = 1; 2532fe5e39SKevin Lloyd module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR); 2632fe5e39SKevin Lloyd MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def)," 2732fe5e39SKevin Lloyd " 2=Force CD-Rom, 3=Force Modem)"); 2832fe5e39SKevin Lloyd 2932fe5e39SKevin Lloyd struct swoc_info { 3032fe5e39SKevin Lloyd __u8 rev; 3132fe5e39SKevin Lloyd __u8 reserved[8]; 3232fe5e39SKevin Lloyd __u16 LinuxSKU; 3332fe5e39SKevin Lloyd __u16 LinuxVer; 3432fe5e39SKevin Lloyd __u8 reserved2[47]; 3532fe5e39SKevin Lloyd } __attribute__((__packed__)); 3632fe5e39SKevin Lloyd 3732fe5e39SKevin Lloyd static bool containsFullLinuxPackage(struct swoc_info *swocInfo) 3832fe5e39SKevin Lloyd { 3932fe5e39SKevin Lloyd if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) || 4032fe5e39SKevin Lloyd (swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF)) 4132fe5e39SKevin Lloyd return true; 4232fe5e39SKevin Lloyd else 4332fe5e39SKevin Lloyd return false; 4432fe5e39SKevin Lloyd } 4532fe5e39SKevin Lloyd 4632fe5e39SKevin Lloyd static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) 4732fe5e39SKevin Lloyd { 4832fe5e39SKevin Lloyd int result; 4932fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", "DEVICE MODE SWITCH\n"); 5032fe5e39SKevin Lloyd result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 5132fe5e39SKevin Lloyd SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */ 5232fe5e39SKevin Lloyd USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */ 5332fe5e39SKevin Lloyd eSWocMode, /* __u16 value */ 5432fe5e39SKevin Lloyd 0x0000, /* __u16 index */ 5532fe5e39SKevin Lloyd NULL, /* void *data */ 5632fe5e39SKevin Lloyd 0, /* __u16 size */ 5732fe5e39SKevin Lloyd USB_CTRL_SET_TIMEOUT); /* int timeout */ 5832fe5e39SKevin Lloyd return result; 5932fe5e39SKevin Lloyd } 6032fe5e39SKevin Lloyd 6132fe5e39SKevin Lloyd 6232fe5e39SKevin Lloyd static int sierra_get_swoc_info(struct usb_device *udev, 6332fe5e39SKevin Lloyd struct swoc_info *swocInfo) 6432fe5e39SKevin Lloyd { 6532fe5e39SKevin Lloyd int result; 6632fe5e39SKevin Lloyd 6732fe5e39SKevin Lloyd US_DEBUGP("SWIMS: Attempting to get TRU-Install info.\n"); 6832fe5e39SKevin Lloyd 6932fe5e39SKevin Lloyd result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 7032fe5e39SKevin Lloyd SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */ 7132fe5e39SKevin Lloyd USB_TYPE_VENDOR | USB_DIR_IN, /* __u8 request type */ 7232fe5e39SKevin Lloyd 0, /* __u16 value */ 7332fe5e39SKevin Lloyd 0, /* __u16 index */ 7432fe5e39SKevin Lloyd (void *) swocInfo, /* void *data */ 7532fe5e39SKevin Lloyd sizeof(struct swoc_info), /* __u16 size */ 7632fe5e39SKevin Lloyd USB_CTRL_SET_TIMEOUT); /* int timeout */ 7732fe5e39SKevin Lloyd 7832fe5e39SKevin Lloyd swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); 7932fe5e39SKevin Lloyd swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); 8032fe5e39SKevin Lloyd return result; 8132fe5e39SKevin Lloyd } 8232fe5e39SKevin Lloyd 8332fe5e39SKevin Lloyd static void debug_swoc(struct swoc_info *swocInfo) 8432fe5e39SKevin Lloyd { 8532fe5e39SKevin Lloyd US_DEBUGP("SWIMS: SWoC Rev: %02d \n", swocInfo->rev); 8632fe5e39SKevin Lloyd US_DEBUGP("SWIMS: Linux SKU: %04X \n", swocInfo->LinuxSKU); 8732fe5e39SKevin Lloyd US_DEBUGP("SWIMS: Linux Version: %04X \n", swocInfo->LinuxVer); 8832fe5e39SKevin Lloyd } 8932fe5e39SKevin Lloyd 9032fe5e39SKevin Lloyd 9132fe5e39SKevin Lloyd static ssize_t show_truinst(struct device *dev, struct device_attribute *attr, 9232fe5e39SKevin Lloyd char *buf) 9332fe5e39SKevin Lloyd { 9432fe5e39SKevin Lloyd struct swoc_info *swocInfo; 9532fe5e39SKevin Lloyd struct usb_interface *intf = to_usb_interface(dev); 9632fe5e39SKevin Lloyd struct usb_device *udev = interface_to_usbdev(intf); 9732fe5e39SKevin Lloyd int result; 9832fe5e39SKevin Lloyd if (swi_tru_install == TRU_FORCE_MS) { 9932fe5e39SKevin Lloyd result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n"); 10032fe5e39SKevin Lloyd } else { 10132fe5e39SKevin Lloyd swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); 10232fe5e39SKevin Lloyd if (!swocInfo) { 10332fe5e39SKevin Lloyd US_DEBUGP("SWIMS: Allocation failure\n"); 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) { 10932fe5e39SKevin Lloyd US_DEBUGP("SWIMS: failed SWoC query\n"); 11032fe5e39SKevin Lloyd kfree(swocInfo); 11132fe5e39SKevin Lloyd snprintf(buf, PAGE_SIZE, "Error\n"); 11232fe5e39SKevin Lloyd return -EIO; 11332fe5e39SKevin Lloyd } 11432fe5e39SKevin Lloyd debug_swoc(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 } 12432fe5e39SKevin Lloyd static DEVICE_ATTR(truinst, S_IWUGO | 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 signed long delay_t; 13032fe5e39SKevin Lloyd struct swoc_info *swocInfo; 13132fe5e39SKevin Lloyd struct usb_device *udev; 13232fe5e39SKevin Lloyd struct Scsi_Host *sh; 13332fe5e39SKevin Lloyd struct scsi_device *sd; 13432fe5e39SKevin Lloyd 13532fe5e39SKevin Lloyd delay_t = 2; 13632fe5e39SKevin Lloyd retries = 3; 13732fe5e39SKevin Lloyd result = 0; 13832fe5e39SKevin Lloyd udev = us->pusb_dev; 13932fe5e39SKevin Lloyd 14032fe5e39SKevin Lloyd sh = us_to_host(us); 14132fe5e39SKevin Lloyd sd = scsi_get_host_dev(sh); 14232fe5e39SKevin Lloyd 14332fe5e39SKevin Lloyd US_DEBUGP("SWIMS: sierra_ms_init called\n"); 14432fe5e39SKevin Lloyd 14532fe5e39SKevin Lloyd /* Force Modem mode */ 14632fe5e39SKevin Lloyd if (swi_tru_install == TRU_FORCE_MODEM) { 14732fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", "Forcing Modem Mode\n"); 14832fe5e39SKevin Lloyd result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); 14932fe5e39SKevin Lloyd if (result < 0) 15032fe5e39SKevin Lloyd US_DEBUGP("SWIMS: Failed to switch to modem mode.\n"); 15132fe5e39SKevin Lloyd return -EIO; 15232fe5e39SKevin Lloyd } 15332fe5e39SKevin Lloyd /* Force Mass Storage mode (keep CD-Rom) */ 15432fe5e39SKevin Lloyd else if (swi_tru_install == TRU_FORCE_MS) { 15532fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", "Forcing Mass Storage Mode\n"); 15632fe5e39SKevin Lloyd goto complete; 15732fe5e39SKevin Lloyd } 15832fe5e39SKevin Lloyd /* Normal TRU-Install Logic */ 15932fe5e39SKevin Lloyd else { 16032fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", "Normal SWoC Logic\n"); 16132fe5e39SKevin Lloyd 16232fe5e39SKevin Lloyd swocInfo = kmalloc(sizeof(struct swoc_info), 16332fe5e39SKevin Lloyd GFP_KERNEL); 16432fe5e39SKevin Lloyd if (!swocInfo) { 16532fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", "Allocation failure\n"); 16632fe5e39SKevin Lloyd return -ENOMEM; 16732fe5e39SKevin Lloyd } 16832fe5e39SKevin Lloyd 16932fe5e39SKevin Lloyd retries = 3; 17032fe5e39SKevin Lloyd do { 17132fe5e39SKevin Lloyd retries--; 17232fe5e39SKevin Lloyd result = sierra_get_swoc_info(udev, swocInfo); 17332fe5e39SKevin Lloyd if (result < 0) { 17432fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", "Failed SWoC query\n"); 17532fe5e39SKevin Lloyd schedule_timeout_uninterruptible(2*HZ); 17632fe5e39SKevin Lloyd } 17732fe5e39SKevin Lloyd } while (retries && result < 0); 17832fe5e39SKevin Lloyd 17932fe5e39SKevin Lloyd if (result < 0) { 18032fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", 18132fe5e39SKevin Lloyd "Completely failed SWoC query\n"); 18232fe5e39SKevin Lloyd kfree(swocInfo); 18332fe5e39SKevin Lloyd return -EIO; 18432fe5e39SKevin Lloyd } 18532fe5e39SKevin Lloyd 18632fe5e39SKevin Lloyd debug_swoc(swocInfo); 18732fe5e39SKevin Lloyd 18832fe5e39SKevin Lloyd /* If there is not Linux software on the TRU-Install device 18932fe5e39SKevin Lloyd * then switch to modem mode 19032fe5e39SKevin Lloyd */ 19132fe5e39SKevin Lloyd if (!containsFullLinuxPackage(swocInfo)) { 19232fe5e39SKevin Lloyd US_DEBUGP("SWIMS: %s", 19332fe5e39SKevin Lloyd "Switching to Modem Mode\n"); 19432fe5e39SKevin Lloyd result = sierra_set_ms_mode(udev, 19532fe5e39SKevin Lloyd SWIMS_SET_MODE_Modem); 19632fe5e39SKevin Lloyd if (result < 0) 19732fe5e39SKevin Lloyd US_DEBUGP("SWIMS: Failed to switch modem\n"); 19832fe5e39SKevin Lloyd kfree(swocInfo); 19932fe5e39SKevin Lloyd return -EIO; 20032fe5e39SKevin Lloyd } 20132fe5e39SKevin Lloyd kfree(swocInfo); 20232fe5e39SKevin Lloyd } 20332fe5e39SKevin Lloyd complete: 20432fe5e39SKevin Lloyd result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); 20532fe5e39SKevin Lloyd 206be475d90SAlan Stern return 0; 20732fe5e39SKevin Lloyd } 20832fe5e39SKevin Lloyd 209