forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
195 lines
5.2 KiB
195 lines
5.2 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
#include <scsi/scsi.h> |
|
#include <scsi/scsi_host.h> |
|
#include <scsi/scsi_cmnd.h> |
|
#include <scsi/scsi_device.h> |
|
#include <linux/usb.h> |
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
|
|
#include "usb.h" |
|
#include "transport.h" |
|
#include "protocol.h" |
|
#include "scsiglue.h" |
|
#include "sierra_ms.h" |
|
#include "debug.h" |
|
|
|
#define SWIMS_USB_REQUEST_SetSwocMode 0x0B |
|
#define SWIMS_USB_REQUEST_GetSwocInfo 0x0A |
|
#define SWIMS_USB_INDEX_SetMode 0x0000 |
|
#define SWIMS_SET_MODE_Modem 0x0001 |
|
|
|
#define TRU_NORMAL 0x01 |
|
#define TRU_FORCE_MS 0x02 |
|
#define TRU_FORCE_MODEM 0x03 |
|
|
|
static unsigned int swi_tru_install = 1; |
|
module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR); |
|
MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def)," |
|
" 2=Force CD-Rom, 3=Force Modem)"); |
|
|
|
struct swoc_info { |
|
__u8 rev; |
|
__u8 reserved[8]; |
|
__u16 LinuxSKU; |
|
__u16 LinuxVer; |
|
__u8 reserved2[47]; |
|
} __attribute__((__packed__)); |
|
|
|
static bool containsFullLinuxPackage(struct swoc_info *swocInfo) |
|
{ |
|
if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) || |
|
(swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF)) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) |
|
{ |
|
int result; |
|
dev_dbg(&udev->dev, "SWIMS: %s", "DEVICE MODE SWITCH\n"); |
|
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), |
|
SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */ |
|
USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */ |
|
eSWocMode, /* __u16 value */ |
|
0x0000, /* __u16 index */ |
|
NULL, /* void *data */ |
|
0, /* __u16 size */ |
|
USB_CTRL_SET_TIMEOUT); /* int timeout */ |
|
return result; |
|
} |
|
|
|
|
|
static int sierra_get_swoc_info(struct usb_device *udev, |
|
struct swoc_info *swocInfo) |
|
{ |
|
int result; |
|
|
|
dev_dbg(&udev->dev, "SWIMS: Attempting to get TRU-Install info\n"); |
|
|
|
result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), |
|
SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */ |
|
USB_TYPE_VENDOR | USB_DIR_IN, /* __u8 request type */ |
|
0, /* __u16 value */ |
|
0, /* __u16 index */ |
|
(void *) swocInfo, /* void *data */ |
|
sizeof(struct swoc_info), /* __u16 size */ |
|
USB_CTRL_SET_TIMEOUT); /* int timeout */ |
|
|
|
swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); |
|
swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); |
|
return result; |
|
} |
|
|
|
static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo) |
|
{ |
|
dev_dbg(dev, "SWIMS: SWoC Rev: %02d\n", swocInfo->rev); |
|
dev_dbg(dev, "SWIMS: Linux SKU: %04X\n", swocInfo->LinuxSKU); |
|
dev_dbg(dev, "SWIMS: Linux Version: %04X\n", swocInfo->LinuxVer); |
|
} |
|
|
|
|
|
static ssize_t truinst_show(struct device *dev, struct device_attribute *attr, |
|
char *buf) |
|
{ |
|
struct swoc_info *swocInfo; |
|
struct usb_interface *intf = to_usb_interface(dev); |
|
struct usb_device *udev = interface_to_usbdev(intf); |
|
int result; |
|
if (swi_tru_install == TRU_FORCE_MS) { |
|
result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n"); |
|
} else { |
|
swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); |
|
if (!swocInfo) { |
|
snprintf(buf, PAGE_SIZE, "Error\n"); |
|
return -ENOMEM; |
|
} |
|
result = sierra_get_swoc_info(udev, swocInfo); |
|
if (result < 0) { |
|
dev_dbg(dev, "SWIMS: failed SWoC query\n"); |
|
kfree(swocInfo); |
|
snprintf(buf, PAGE_SIZE, "Error\n"); |
|
return -EIO; |
|
} |
|
debug_swoc(dev, swocInfo); |
|
result = snprintf(buf, PAGE_SIZE, |
|
"REV=%02d SKU=%04X VER=%04X\n", |
|
swocInfo->rev, |
|
swocInfo->LinuxSKU, |
|
swocInfo->LinuxVer); |
|
kfree(swocInfo); |
|
} |
|
return result; |
|
} |
|
static DEVICE_ATTR_RO(truinst); |
|
|
|
int sierra_ms_init(struct us_data *us) |
|
{ |
|
int result, retries; |
|
struct swoc_info *swocInfo; |
|
struct usb_device *udev; |
|
|
|
retries = 3; |
|
result = 0; |
|
udev = us->pusb_dev; |
|
|
|
/* Force Modem mode */ |
|
if (swi_tru_install == TRU_FORCE_MODEM) { |
|
usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n"); |
|
result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); |
|
if (result < 0) |
|
usb_stor_dbg(us, "SWIMS: Failed to switch to modem mode\n"); |
|
return -EIO; |
|
} |
|
/* Force Mass Storage mode (keep CD-Rom) */ |
|
else if (swi_tru_install == TRU_FORCE_MS) { |
|
usb_stor_dbg(us, "SWIMS: Forcing Mass Storage Mode\n"); |
|
goto complete; |
|
} |
|
/* Normal TRU-Install Logic */ |
|
else { |
|
usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n"); |
|
|
|
swocInfo = kmalloc(sizeof(struct swoc_info), |
|
GFP_KERNEL); |
|
if (!swocInfo) |
|
return -ENOMEM; |
|
|
|
retries = 3; |
|
do { |
|
retries--; |
|
result = sierra_get_swoc_info(udev, swocInfo); |
|
if (result < 0) { |
|
usb_stor_dbg(us, "SWIMS: Failed SWoC query\n"); |
|
schedule_timeout_uninterruptible(2*HZ); |
|
} |
|
} while (retries && result < 0); |
|
|
|
if (result < 0) { |
|
usb_stor_dbg(us, "SWIMS: Completely failed SWoC query\n"); |
|
kfree(swocInfo); |
|
return -EIO; |
|
} |
|
|
|
debug_swoc(&us->pusb_dev->dev, swocInfo); |
|
|
|
/* |
|
* If there is not Linux software on the TRU-Install device |
|
* then switch to modem mode |
|
*/ |
|
if (!containsFullLinuxPackage(swocInfo)) { |
|
usb_stor_dbg(us, "SWIMS: Switching to Modem Mode\n"); |
|
result = sierra_set_ms_mode(udev, |
|
SWIMS_SET_MODE_Modem); |
|
if (result < 0) |
|
usb_stor_dbg(us, "SWIMS: Failed to switch modem\n"); |
|
kfree(swocInfo); |
|
return -EIO; |
|
} |
|
kfree(swocInfo); |
|
} |
|
complete: |
|
return device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); |
|
} |
|
|
|
|