sierra_ms.c 5.3 KB

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