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 }