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