]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blame - drivers/usb/storage/option_ms.c
UBUNTU: Ubuntu-5.3.0-29.31
[mirror_ubuntu-eoan-kernel.git] / drivers / usb / storage / option_ms.c
CommitLineData
5fd54ace 1// SPDX-License-Identifier: GPL-2.0+
281b064f
DW
2/*
3 * Driver for Option High Speed Mobile Devices.
4 *
5 * (c) 2008 Dan Williams <dcbw@redhat.com>
6 *
7 * Inspiration taken from sierra_ms.c by Kevin Lloyd <klloyd@sierrawireless.com>
281b064f
DW
8 */
9
10#include <linux/usb.h>
5a0e3ad6 11#include <linux/slab.h>
6eb0de82 12#include <linux/module.h>
281b064f
DW
13
14#include "usb.h"
15#include "transport.h"
16#include "option_ms.h"
17#include "debug.h"
18
19#define ZCD_FORCE_MODEM 0x01
20#define ZCD_ALLOW_MS 0x02
21
22static unsigned int option_zero_cd = ZCD_FORCE_MODEM;
23module_param(option_zero_cd, uint, S_IRUGO | S_IWUSR);
24MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default),"
25 " 2=Allow CD-Rom");
26
27#define RESPONSE_LEN 1024
28
32ebbe7b 29static int option_rezero(struct us_data *us)
281b064f 30{
38502ef4 31 static const unsigned char rezero_msg[] = {
281b064f
DW
32 0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12,
33 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
36 };
37 char *buffer;
38 int result;
39
191648d0 40 usb_stor_dbg(us, "Option MS: %s\n", "DEVICE MODE SWITCH");
281b064f
DW
41
42 buffer = kzalloc(RESPONSE_LEN, GFP_KERNEL);
43 if (buffer == NULL)
44 return USB_STOR_TRANSPORT_ERROR;
45
32ebbe7b 46 memcpy(buffer, rezero_msg, sizeof(rezero_msg));
281b064f 47 result = usb_stor_bulk_transfer_buf(us,
32ebbe7b
JD
48 us->send_bulk_pipe,
49 buffer, sizeof(rezero_msg), NULL);
281b064f
DW
50 if (result != USB_STOR_XFER_GOOD) {
51 result = USB_STOR_XFER_ERROR;
52 goto out;
53 }
54
f0183a33
FB
55 /*
56 * Some of the devices need to be asked for a response, but we don't
281b064f
DW
57 * care what that response is.
58 */
32ebbe7b
JD
59 usb_stor_bulk_transfer_buf(us,
60 us->recv_bulk_pipe,
281b064f 61 buffer, RESPONSE_LEN, NULL);
32ebbe7b
JD
62
63 /* Read the CSW */
64 usb_stor_bulk_transfer_buf(us,
65 us->recv_bulk_pipe,
66 buffer, 13, NULL);
67
281b064f
DW
68 result = USB_STOR_XFER_GOOD;
69
70out:
71 kfree(buffer);
72 return result;
73}
74
32ebbe7b 75static int option_inquiry(struct us_data *us)
281b064f 76{
38502ef4 77 static const unsigned char inquiry_msg[] = {
32ebbe7b
JD
78 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
79 0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
80 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
82 };
83 char *buffer;
84 int result;
281b064f 85
191648d0 86 usb_stor_dbg(us, "Option MS: %s\n", "device inquiry for vendor name");
281b064f 87
32ebbe7b
JD
88 buffer = kzalloc(0x24, GFP_KERNEL);
89 if (buffer == NULL)
90 return USB_STOR_TRANSPORT_ERROR;
91
92 memcpy(buffer, inquiry_msg, sizeof(inquiry_msg));
93 result = usb_stor_bulk_transfer_buf(us,
94 us->send_bulk_pipe,
95 buffer, sizeof(inquiry_msg), NULL);
96 if (result != USB_STOR_XFER_GOOD) {
97 result = USB_STOR_XFER_ERROR;
98 goto out;
281b064f
DW
99 }
100
32ebbe7b
JD
101 result = usb_stor_bulk_transfer_buf(us,
102 us->recv_bulk_pipe,
103 buffer, 0x24, NULL);
104 if (result != USB_STOR_XFER_GOOD) {
105 result = USB_STOR_XFER_ERROR;
106 goto out;
281b064f
DW
107 }
108
32ebbe7b
JD
109 result = memcmp(buffer+8, "Option", 6);
110
2ab2178c
JM
111 if (result != 0)
112 result = memcmp(buffer+8, "ZCOPTION", 8);
113
32ebbe7b
JD
114 /* Read the CSW */
115 usb_stor_bulk_transfer_buf(us,
116 us->recv_bulk_pipe,
117 buffer, 13, NULL);
118
119out:
120 kfree(buffer);
121 return result;
122}
123
124
125int option_ms_init(struct us_data *us)
126{
127 int result;
128
191648d0 129 usb_stor_dbg(us, "Option MS: %s\n", "option_ms_init called");
32ebbe7b 130
f0183a33
FB
131 /*
132 * Additional test for vendor information via INQUIRY,
32ebbe7b
JD
133 * because some vendor/product IDs are ambiguous
134 */
135 result = option_inquiry(us);
136 if (result != 0) {
191648d0
JP
137 usb_stor_dbg(us, "Option MS: %s\n",
138 "vendor is not Option or not determinable, no action taken");
be475d90 139 return 0;
32ebbe7b 140 } else
191648d0
JP
141 usb_stor_dbg(us, "Option MS: %s\n",
142 "this is a genuine Option device, proceeding");
281b064f
DW
143
144 /* Force Modem mode */
145 if (option_zero_cd == ZCD_FORCE_MODEM) {
191648d0 146 usb_stor_dbg(us, "Option MS: %s\n", "Forcing Modem Mode");
32ebbe7b 147 result = option_rezero(us);
281b064f 148 if (result != USB_STOR_XFER_GOOD)
191648d0
JP
149 usb_stor_dbg(us, "Option MS: %s\n",
150 "Failed to switch to modem mode");
281b064f
DW
151 return -EIO;
152 } else if (option_zero_cd == ZCD_ALLOW_MS) {
153 /* Allow Mass Storage mode (keep CD-Rom) */
191648d0
JP
154 usb_stor_dbg(us, "Option MS: %s\n",
155 "Allowing Mass Storage Mode if device requests it");
281b064f
DW
156 }
157
be475d90 158 return 0;
281b064f
DW
159}
160