1c8a7a7ff1289b864ea8e6c31b1cf90e8d006a55
[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 }