1 // SPDX-License-Identifier: GPL-2.0-or-later
7 #include <libubox/blobmsg_json.h>
8 #include <libubox/avl.h>
9 #include <libubox/avl-cmp.h>
12 #define DEFAULT_CONFIG "/etc/usb-mode.json"
16 struct blob_attr
*data
;
19 static int verbose
= 0;
20 static const char *config_file
= DEFAULT_CONFIG
;
21 static struct blob_buf conf
;
23 char **messages
= NULL
;
27 static struct avl_tree devices
;
29 struct libusb_context
*usb
;
30 static struct libusb_device
**usbdevs
;
33 static int hex2num(char c
)
35 if (c
>= '0' && c
<= '9')
39 if (c
>= 'A' && c
<= 'F')
45 static int hex2byte(const char *hex
)
60 static int hexstr2bin(const char *hex
, char *buffer
, int len
)
62 const char *ipos
= hex
;
66 for (i
= 0; i
< len
; i
++) {
78 static int convert_message(struct blob_attr
*attr
)
83 data
= blobmsg_data(attr
);
88 if (hexstr2bin(data
, data
, len
/ 2))
94 static int parse_config(void)
101 static const struct blobmsg_policy policy
[__CONF_MAX
] = {
102 [CONF_MESSAGES
] = { .name
= "messages", .type
= BLOBMSG_TYPE_ARRAY
},
103 [CONF_DEVICES
] = { .name
= "devices", .type
= BLOBMSG_TYPE_TABLE
},
105 struct blob_attr
*tb
[__CONF_MAX
];
106 struct blob_attr
*cur
;
110 blobmsg_parse(policy
, __CONF_MAX
, tb
, blob_data(conf
.head
), blob_len(conf
.head
));
111 if (!tb
[CONF_MESSAGES
] || !tb
[CONF_DEVICES
]) {
112 fprintf(stderr
, "Configuration incomplete\n");
116 blobmsg_for_each_attr(cur
, tb
[CONF_MESSAGES
], rem
)
119 messages
= calloc(n_messages
, sizeof(*messages
));
120 message_len
= calloc(n_messages
, sizeof(*message_len
));
122 blobmsg_for_each_attr(cur
, tb
[CONF_MESSAGES
], rem
) {
123 int len
= convert_message(cur
);
126 fprintf(stderr
, "Invalid data in message %d\n", n_messages
);
130 message_len
[n_messages
] = len
;
131 messages
[n_messages
++] = blobmsg_data(cur
);
134 blobmsg_for_each_attr(cur
, tb
[CONF_DEVICES
], rem
) {
135 dev
= calloc(1, sizeof(*dev
));
136 dev
->avl
.key
= blobmsg_name(cur
);
138 avl_insert(&devices
, &dev
->avl
);
144 static int usage(const char *prog
)
146 fprintf(stderr
, "Usage: %s <command> <options>\n"
148 " -l List matching devices\n"
149 " -s Modeswitch matching devices\n"
152 " -v Verbose output\n"
153 " -c <file> Set configuration file to <file> (default: %s)\n"
154 "\n", prog
, DEFAULT_CONFIG
);
158 typedef void (*cmd_cb_t
)(struct usbdev_data
*data
);
160 static struct blob_attr
*
161 find_dev_data(struct usbdev_data
*data
, struct device
*dev
)
163 struct blob_attr
*cur
;
166 blobmsg_for_each_attr(cur
, dev
->data
, rem
) {
167 const char *name
= blobmsg_name(cur
);
171 if (!strcmp(blobmsg_name(cur
), "*"))
174 next
= strchr(name
, '=');
179 if (!strncmp(name
, "uMa", 3)) {
181 } else if (!strncmp(name
, "uPr", 3)) {
183 } else if (!strncmp(name
, "uSe", 3)) {
186 /* ignore unsupported scsi attributes */
190 if (!strcmp(val
, next
))
198 parse_interface_config(libusb_device
*dev
, struct usbdev_data
*data
)
200 struct libusb_config_descriptor
*config
;
201 const struct libusb_interface
*iface
;
202 const struct libusb_interface_descriptor
*alt
;
205 data
->interface
= -1;
206 if (libusb_get_config_descriptor(dev
, 0, &config
))
209 data
->config
= config
;
210 if (!config
->bNumInterfaces
)
213 iface
= &config
->interface
[0];
214 if (!iface
->num_altsetting
)
217 alt
= &iface
->altsetting
[0];
218 data
->interface
= alt
->bInterfaceNumber
;
219 data
->dev_class
= alt
->bInterfaceClass
;
221 for (i
= 0; i
< alt
->bNumEndpoints
; i
++) {
222 const struct libusb_endpoint_descriptor
*ep
= &alt
->endpoint
[i
];
225 if (data
->msg_endpoint
&& data
->response_endpoint
)
228 if ((ep
->bmAttributes
& LIBUSB_TRANSFER_TYPE_MASK
) !=
229 LIBUSB_TRANSFER_TYPE_BULK
)
232 out
= (ep
->bEndpointAddress
& LIBUSB_ENDPOINT_DIR_MASK
) ==
235 if (!data
->msg_endpoint
&& out
)
236 data
->msg_endpoint
= ep
->bEndpointAddress
;
237 if (!data
->response_endpoint
&& !out
)
238 data
->response_endpoint
= ep
->bEndpointAddress
;
242 static void iterate_devs(cmd_cb_t cb
)
244 struct usbdev_data data
;
251 for (i
= 0; i
< n_usbdevs
; i
++) {
252 memset(&data
, 0, sizeof(data
));
254 if (libusb_get_device_descriptor(usbdevs
[i
], &data
.desc
))
257 sprintf(data
.idstr
, "%04x:%04x", data
.desc
.idVendor
, data
.desc
.idProduct
);
259 dev
= avl_find_element(&devices
, data
.idstr
, dev
, avl
);
263 if (libusb_open(usbdevs
[i
], &data
.devh
))
266 data
.dev
= usbdevs
[i
];
268 libusb_get_string_descriptor_ascii(
269 data
.devh
, data
.desc
.iManufacturer
,
270 (void *) data
.mfg
, sizeof(data
.mfg
));
271 libusb_get_string_descriptor_ascii(
272 data
.devh
, data
.desc
.iProduct
,
273 (void *) data
.prod
, sizeof(data
.prod
));
274 libusb_get_string_descriptor_ascii(
275 data
.devh
, data
.desc
.iSerialNumber
,
276 (void *) data
.serial
, sizeof(data
.serial
));
278 parse_interface_config(usbdevs
[i
], &data
);
280 data
.info
= find_dev_data(&data
, dev
);
285 libusb_free_config_descriptor(data
.config
);
288 libusb_close(data
.devh
);
292 static void handle_list(struct usbdev_data
*data
)
294 fprintf(stderr
, "Found device: %s (Manufacturer: \"%s\", Product: \"%s\", Serial: \"%s\")\n",
295 data
->idstr
, data
->mfg
, data
->prod
, data
->serial
);
298 int main(int argc
, char **argv
)
304 avl_init(&devices
, avl_strcmp
, false, NULL
);
306 while ((ch
= getopt(argc
, argv
, "lsc:v")) != -1) {
315 config_file
= optarg
;
321 return usage(argv
[0]);
325 blob_buf_init(&conf
, 0);
326 if (!blobmsg_add_json_from_file(&conf
, config_file
) ||
328 fprintf(stderr
, "Failed to load config file\n");
332 ret
= libusb_init(&usb
);
334 fprintf(stderr
, "Failed to initialize libusb: %s\n", libusb_error_name(ret
));
338 n_usbdevs
= libusb_get_device_list(usb
, &usbdevs
);
340 libusb_free_device_list(usbdevs
, 1);