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