add config/altsetting switching
[project/usbmode.git] / switch.c
1 #include <unistd.h>
2 #include "switch.h"
3
4 enum {
5 DATA_MODE,
6 DATA_MSG,
7 DATA_INTERFACE,
8 DATA_MSG_EP,
9 DATA_RES_EP,
10 DATA_RESPONSE,
11 DATA_RELEASE_DELAY,
12 DATA_CONFIG,
13 DATA_ALT,
14 __DATA_MAX
15 };
16
17 static void detach_driver(struct usbdev_data *data)
18 {
19 libusb_detach_kernel_driver(data->devh, data->interface);
20 }
21
22 static int send_msg(struct usbdev_data *data, int msg)
23 {
24 int transferred;
25
26 return libusb_bulk_transfer(data->devh, data->msg_endpoint,
27 (void *) messages[msg], message_len[msg],
28 &transferred, 3000);
29 }
30
31 static int read_response(struct usbdev_data *data, int len)
32 {
33 unsigned char *buf;
34 int ret, transferred;
35
36 if (len < 13)
37 len = 13;
38 buf = alloca(len);
39 ret = libusb_bulk_transfer(data->devh, data->response_endpoint,
40 buf, len, &transferred, 3000);
41 libusb_bulk_transfer(data->devh, data->response_endpoint,
42 buf, 13, &transferred, 100);
43 return ret;
44 }
45
46 static void send_messages(struct usbdev_data *data, struct blob_attr *attr)
47 {
48 struct blob_attr *cur;
49 int rem;
50
51 libusb_claim_interface(data->devh, data->interface);
52 libusb_clear_halt(data->devh, data->msg_endpoint);
53
54 blobmsg_for_each_attr(cur, attr, rem) {
55 int msg, len;
56
57 if (blobmsg_type(cur) != BLOBMSG_TYPE_INT32) {
58 fprintf(stderr, "Invalid data in message list\n");
59 return;
60 }
61
62 msg = blobmsg_get_u32(cur);
63 if (msg >= n_messages) {
64 fprintf(stderr, "Message index out of range!\n");
65 return;
66 }
67
68 if (send_msg(data, msg)) {
69 fprintf(stderr, "Failed to send switch message\n");
70 continue;
71 }
72
73 if (!data->need_response)
74 continue;
75
76 if (!memcmp(messages[msg], "\x55\x53\x42\x43", 4))
77 len = 13;
78 else
79 len = message_len[msg];
80
81 if (read_response(data, len))
82 return;
83 }
84
85 libusb_clear_halt(data->devh, data->msg_endpoint);
86 libusb_clear_halt(data->devh, data->response_endpoint);
87
88 usleep(200000);
89
90 if (data->release_delay)
91 usleep(data->release_delay * 1000);
92
93 libusb_release_interface(data->devh, data->interface);
94 return;
95 }
96
97 static void handle_generic(struct usbdev_data *data, struct blob_attr **tb)
98 {
99 detach_driver(data);
100 send_messages(data, tb[DATA_MSG]);
101 }
102
103 static void handle_huawei(struct usbdev_data *data, struct blob_attr **tb)
104 {
105 /* TODO */
106 }
107
108 static void handle_sierra(struct usbdev_data *data, struct blob_attr **tb)
109 {
110 /* TODO */
111 }
112
113 static void handle_sony(struct usbdev_data *data, struct blob_attr **tb)
114 {
115 /* TODO */
116 }
117
118 static void handle_qisda(struct usbdev_data *data, struct blob_attr **tb)
119 {
120 /* TODO */
121 }
122
123 static void handle_gct(struct usbdev_data *data, struct blob_attr **tb)
124 {
125 detach_driver(data);
126 /* TODO */
127 }
128
129 static void handle_kobil(struct usbdev_data *data, struct blob_attr **tb)
130 {
131 detach_driver(data);
132 /* TODO */
133 }
134
135 static void handle_sequans(struct usbdev_data *data, struct blob_attr **tb)
136 {
137 /* TODO */
138 }
139
140 static void handle_mobile_action(struct usbdev_data *data, struct blob_attr **tb)
141 {
142 /* TODO */
143 }
144
145 static void handle_cisco(struct usbdev_data *data, struct blob_attr **tb)
146 {
147 detach_driver(data);
148 /* TODO */
149 }
150
151 static void set_alt_setting(struct usbdev_data *data, int setting)
152 {
153 if (libusb_claim_interface(data->devh, data->interface))
154 return;
155
156 libusb_set_interface_alt_setting(data->devh, data->interface, setting);
157 libusb_release_interface(data->devh, data->interface);
158 }
159
160 enum {
161 MODE_GENERIC,
162 MODE_HUAWEI,
163 MODE_SIERRA,
164 MODE_SONY,
165 MODE_QISDA,
166 MODE_GCT,
167 MODE_KOBIL,
168 MODE_SEQUANS,
169 MODE_MOBILE_ACTION,
170 MODE_CISCO,
171 __MODE_MAX
172 };
173
174 static const struct {
175 const char *name;
176 void (*cb)(struct usbdev_data *data, struct blob_attr **tb);
177 } modeswitch_cb[__MODE_MAX] = {
178 [MODE_GENERIC] = { "Generic", handle_generic },
179 [MODE_HUAWEI] = { "Huawei", handle_huawei },
180 [MODE_SIERRA] = { "Sierra", handle_sierra },
181 [MODE_SONY] = { "Sony", handle_sony },
182 [MODE_QISDA] = { "Qisda", handle_qisda },
183 [MODE_GCT] = { "GCT", handle_gct },
184 [MODE_KOBIL] = { "Kobil", handle_kobil },
185 [MODE_SEQUANS] = { "Sequans", handle_sequans },
186 [MODE_MOBILE_ACTION] = { "MobileAction", handle_mobile_action },
187 [MODE_CISCO] = { "Cisco", handle_cisco },
188 };
189
190 void handle_switch(struct usbdev_data *data)
191 {
192 static const struct blobmsg_policy data_policy[__DATA_MAX] = {
193 [DATA_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
194 [DATA_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_ARRAY },
195 [DATA_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_INT32 },
196 [DATA_MSG_EP] = { .name = "msg_endpoint", .type = BLOBMSG_TYPE_INT32 },
197 [DATA_RES_EP] = { .name = "response_endpoint", .type = BLOBMSG_TYPE_INT32 },
198 [DATA_RESPONSE] = { .name = "response", .type = BLOBMSG_TYPE_INT32 },
199 [DATA_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_INT32 },
200 [DATA_ALT] = { .name = "alt", .type = BLOBMSG_TYPE_INT32 },
201 };
202 struct blob_attr *tb[__DATA_MAX];
203 int mode = MODE_GENERIC;
204
205 blobmsg_parse(data_policy, __DATA_MAX, tb, blobmsg_data(data->info), blobmsg_data_len(data->info));
206
207 if (tb[DATA_INTERFACE])
208 data->interface = blobmsg_get_u32(tb[DATA_INTERFACE]);
209
210 if (tb[DATA_MSG_EP])
211 data->msg_endpoint = blobmsg_get_u32(tb[DATA_MSG_EP]);
212
213 if (tb[DATA_RES_EP])
214 data->response_endpoint = blobmsg_get_u32(tb[DATA_RES_EP]);
215
216 if (tb[DATA_RELEASE_DELAY])
217 data->release_delay = blobmsg_get_u32(tb[DATA_RELEASE_DELAY]);
218
219 if (tb[DATA_RESPONSE])
220 data->need_response = blobmsg_get_bool(tb[DATA_RESPONSE]);
221
222 if (tb[DATA_MODE]) {
223 const char *modestr;
224 int i;
225
226 modestr = blobmsg_data(tb[DATA_MODE]);
227 for (i = 0; i < __MODE_MAX; i++) {
228 if (strcmp(modeswitch_cb[i].name, modestr) != 0)
229 continue;
230
231 mode = i;
232 break;
233 }
234 }
235
236 modeswitch_cb[mode].cb(data, tb);
237
238 if (tb[DATA_CONFIG]) {
239 int config, config_new;
240
241 config_new = blobmsg_get_u32(tb[DATA_CONFIG]);
242 if (libusb_get_configuration(data->devh, &config) ||
243 config != config_new)
244 libusb_set_configuration(data->devh, config_new);
245 }
246
247 if (tb[DATA_ALT]) {
248 int new = blobmsg_get_u32(tb[DATA_ALT]);
249 set_alt_setting(data, new);
250 }
251 }