sierra_ms.c 5.3 KB

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