6 #include <libubox/blobmsg_json.h>
7 #include <libubox/avl.h>
8 #include <libubox/avl-cmp.h>
11 #define DEFAULT_CONFIG "/etc/usb-mode.json"
15 struct blob_attr
*data
;
18 static int verbose
= 0;
19 static const char *config_file
= DEFAULT_CONFIG
;
20 static struct blob_buf conf
;
22 char **messages
= NULL
;
26 static struct avl_tree devices
;
28 struct libusb_context
*usb
;
29 static struct libusb_device
**usbdevs
;
32 static int hex2num(char c
)
34 if (c
>= '0' && c
<= '9')
38 if (c
>= 'A' && c
<= 'F')
44 static int hex2byte(const char *hex
)
59 static int hexstr2bin(const char *hex
, char *buffer
, int len
)
61 const char *ipos
= hex
;
65 for (i
= 0; i
< len
; i
++) {
77 static int convert_message(struct blob_attr
*attr
)
82 data
= blobmsg_data(attr
);
87 if (hexstr2bin(data
, data
, len
/ 2))
93 static int parse_config(void)
100 static const struct blobmsg_policy policy
[__CONF_MAX
] = {
101 [CONF_MESSAGES
] = { .name
= "messages", .type
= BLOBMSG_TYPE_ARRAY
},
102 [CONF_DEVICES
] = { .name
= "devices", .type
= BLOBMSG_TYPE_TABLE
},
104 struct blob_attr
*tb
[__CONF_MAX
];
105 struct blob_attr
*cur
;
109 blobmsg_parse(policy
, __CONF_MAX
, tb
, blob_data(conf
.head
), blob_len(conf
.head
));
110 if (!tb
[CONF_MESSAGES
] || !tb
[CONF_DEVICES
]) {
111 fprintf(stderr
, "Configuration incomplete\n");
115 blobmsg_for_each_attr(cur
, tb
[CONF_MESSAGES
], rem
)
118 messages
= calloc(n_messages
, sizeof(*messages
));
119 message_len
= calloc(n_messages
, sizeof(*message_len
));
121 blobmsg_for_each_attr(cur
, tb
[CONF_MESSAGES
], rem
) {
122 int len
= convert_message(cur
);
125 fprintf(stderr
, "Invalid data in message %d\n", n_messages
);
129 message_len
[n_messages
] = len
;
130 messages
[n_messages
++] = blobmsg_data(cur
);
133 blobmsg_for_each_attr(cur
, tb
[CONF_DEVICES
], rem
) {
134 dev
= calloc(1, sizeof(*dev
));
135 dev
->avl
.key
= blobmsg_name(cur
);
137 avl_insert(&devices
, &dev
->avl
);
143 static int usage(const char *prog
)
145 fprintf(stderr
, "Usage: %s <command> <options>\n"
147 " -l List matching devices\n"
148 " -s Modeswitch matching devices\n"
151 " -v Verbose output\n"
152 " -c <file> Set configuration file to <file> (default: %s)\n"
153 "\n", prog
, DEFAULT_CONFIG
);
157 typedef void (*cmd_cb_t
)(struct usbdev_data
*data
);
159 static struct blob_attr
*
160 find_dev_data(struct usbdev_data
*data
, struct device
*dev
)
162 struct blob_attr
*cur
;
165 blobmsg_for_each_attr(cur
, dev
->data
, rem
) {
166 const char *name
= blobmsg_name(cur
);
170 if (!strcmp(blobmsg_name(cur
), "*"))
173 next
= strchr(name
, '=');
178 if (!strncmp(name
, "uMa", 3)) {
180 } else if (!strncmp(name
, "uPr", 3)) {
182 } else if (!strncmp(name
, "uSe", 3)) {
185 /* ignore unsupported scsi attributes */
189 if (!strcmp(val
, next
))
197 parse_interface_config(libusb_device
*dev
, struct usbdev_data
*data
)
199 struct libusb_config_descriptor
*config
;
200 const struct libusb_interface
*iface
;
201 const struct libusb_interface_descriptor
*alt
;
204 data
->interface
= -1;
205 if (libusb_get_config_descriptor(dev
, 0, &config
))
208 data
->config
= config
;
209 if (!config
->bNumInterfaces
)
212 iface
= &config
->interface
[0];
213 if (!iface
->num_altsetting
)
216 alt
= &iface
->altsetting
[0];
217 data
->interface
= alt
->bInterfaceNumber
;
218 data
->dev_class
= alt
->bInterfaceClass
;
220 for (i
= 0; i
< alt
->bNumEndpoints
; i
++) {
221 const struct libusb_endpoint_descriptor
*ep
= &alt
->endpoint
[i
];
224 if (data
->msg_endpoint
&& data
->response_endpoint
)
227 if ((ep
->bmAttributes
& LIBUSB_TRANSFER_TYPE_MASK
) !=
228 LIBUSB_TRANSFER_TYPE_BULK
)
231 out
= (ep
->bEndpointAddress
& LIBUSB_ENDPOINT_DIR_MASK
) ==
234 if (!data
->msg_endpoint
&& out
)
235 data
->msg_endpoint
= ep
->bEndpointAddress
;
236 if (!data
->response_endpoint
&& !out
)
237 data
->response_endpoint
= ep
->bEndpointAddress
;
241 static void iterate_devs(cmd_cb_t cb
)
243 struct usbdev_data data
;
250 for (i
= 0; i
< n_usbdevs
; i
++) {
251 memset(&data
, 0, sizeof(data
));
253 if (libusb_get_device_descriptor(usbdevs
[i
], &data
.desc
))
256 sprintf(data
.idstr
, "%04x:%04x", data
.desc
.idVendor
, data
.desc
.idProduct
);
258 dev
= avl_find_element(&devices
, data
.idstr
, dev
, avl
);
262 if (libusb_open(usbdevs
[i
], &data
.devh
))
265 data
.dev
= usbdevs
[i
];
267 libusb_get_string_descriptor_ascii(
268 data
.devh
, data
.desc
.iManufacturer
,
269 (void *) data
.mfg
, sizeof(data
.mfg
));
270 libusb_get_string_descriptor_ascii(
271 data
.devh
, data
.desc
.iProduct
,
272 (void *) data
.prod
, sizeof(data
.prod
));
273 libusb_get_string_descriptor_ascii(
274 data
.devh
, data
.desc
.iSerialNumber
,
275 (void *) data
.serial
, sizeof(data
.serial
));
277 parse_interface_config(usbdevs
[i
], &data
);
279 data
.info
= find_dev_data(&data
, dev
);
284 libusb_free_config_descriptor(data
.config
);
287 libusb_close(data
.devh
);
291 static void handle_list(struct usbdev_data
*data
)
293 fprintf(stderr
, "Found device: %s (Manufacturer: \"%s\", Product: \"%s\", Serial: \"%s\")\n",
294 data
->idstr
, data
->mfg
, data
->prod
, data
->serial
);
297 int main(int argc
, char **argv
)
303 avl_init(&devices
, avl_strcmp
, false, NULL
);
305 while ((ch
= getopt(argc
, argv
, "lsc:v")) != -1) {
314 config_file
= optarg
;
320 return usage(argv
[0]);
324 blob_buf_init(&conf
, 0);
325 if (!blobmsg_add_json_from_file(&conf
, config_file
) ||
327 fprintf(stderr
, "Failed to load config file\n");
331 ret
= libusb_init(&usb
);
333 fprintf(stderr
, "Failed to initialize libusb: %s\n", libusb_error_name(ret
));
337 n_usbdevs
= libusb_get_device_list(usb
, &usbdevs
);
339 libusb_free_device_list(usbdevs
, 1);