5 |
5 |
*
|
6 |
6 |
* Copyright (c) 2008 Max Krasnyansky
|
7 |
7 |
* Support for host device auto connect & disconnect
|
8 |
|
* Magor rewrite to support fully async operation
|
|
8 |
* Major rewrite to support fully async operation
|
9 |
9 |
*
|
10 |
10 |
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
11 |
11 |
* of this software and associated documentation files (the "Software"), to deal
|
... | ... | |
951 |
951 |
return NULL;
|
952 |
952 |
}
|
953 |
953 |
|
|
954 |
static int usb_host_auto_add(const char *spec);
|
|
955 |
static int usb_host_auto_del(const char *spec);
|
|
956 |
|
954 |
957 |
USBDevice *usb_host_device_open(const char *devname)
|
955 |
958 |
{
|
956 |
959 |
int bus_num, addr;
|
957 |
960 |
char product_name[PRODUCT_NAME_SZ];
|
958 |
961 |
|
959 |
|
if (usb_host_find_device(&bus_num, &addr,
|
960 |
|
product_name, sizeof(product_name),
|
961 |
|
devname) < 0)
|
|
962 |
if (strstr(devname, "auto:")) {
|
|
963 |
usb_host_auto_add(devname);
|
962 |
964 |
return NULL;
|
|
965 |
}
|
963 |
966 |
|
964 |
|
if (hostdev_find(bus_num, addr)) {
|
965 |
|
term_printf("husb: host usb device %d.%d is already open\n", bus_num, addr);
|
|
967 |
if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name),
|
|
968 |
devname) < 0)
|
966 |
969 |
return NULL;
|
967 |
|
}
|
|
970 |
|
|
971 |
if (hostdev_find(bus_num, addr)) {
|
|
972 |
term_printf("husb: host usb device %d.%d is already open\n", bus_num, addr);
|
|
973 |
return NULL;
|
|
974 |
}
|
968 |
975 |
|
969 |
976 |
return usb_host_device_open_addr(bus_num, addr, product_name);
|
970 |
977 |
}
|
|
978 |
|
|
979 |
int usb_host_device_close(const char *devname)
|
|
980 |
{
|
|
981 |
char product_name[PRODUCT_NAME_SZ];
|
|
982 |
int bus_num, addr;
|
|
983 |
USBHostDevice *s;
|
|
984 |
|
|
985 |
if (strstr(devname, "auto:"))
|
|
986 |
return usb_host_auto_del(devname);
|
|
987 |
|
|
988 |
if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name),
|
|
989 |
devname) < 0)
|
|
990 |
return -1;
|
|
991 |
|
|
992 |
s = hostdev_find(bus_num, addr);
|
|
993 |
if (s) {
|
|
994 |
usb_device_del_addr(0, s->dev.addr);
|
|
995 |
return 0;
|
|
996 |
}
|
|
997 |
|
|
998 |
return -1;
|
|
999 |
}
|
971 |
1000 |
|
972 |
1001 |
static int get_tag_value(char *buf, int buf_size,
|
973 |
1002 |
const char *str, const char *tag,
|
... | ... | |
1126 |
1155 |
}
|
1127 |
1156 |
|
1128 |
1157 |
/*
|
1129 |
|
* Add autoconnect filter
|
1130 |
|
* -1 means 'any' (device, vendor, etc)
|
|
1158 |
* Autoconnect filter
|
|
1159 |
* Format:
|
|
1160 |
* auto:bus:dev[:vid:pid]
|
|
1161 |
* auto:bus.dev[:vid:pid]
|
|
1162 |
*
|
|
1163 |
* bus - bus number (dec, * means any)
|
|
1164 |
* dev - device number (dec, * means any)
|
|
1165 |
* vid - vendor id (hex, * means any)
|
|
1166 |
* pid - product id (hex, * means any)
|
|
1167 |
*
|
|
1168 |
* See 'lsusb' output.
|
1131 |
1169 |
*/
|
1132 |
|
static void usb_host_auto_add(int bus_num, int addr, int vendor_id, int product_id)
|
|
1170 |
static int parse_filter(const char *spec, struct USBAutoFilter *f)
|
1133 |
1171 |
{
|
1134 |
|
struct USBAutoFilter *f = qemu_mallocz(sizeof(*f));
|
|
1172 |
enum { BUS, DEV, VID, PID, DONE };
|
|
1173 |
const char *p = spec;
|
|
1174 |
int i;
|
|
1175 |
|
|
1176 |
f->bus_num = -1;
|
|
1177 |
f->addr = -1;
|
|
1178 |
f->vendor_id = -1;
|
|
1179 |
f->product_id = -1;
|
|
1180 |
|
|
1181 |
for (i = BUS; i < DONE; i++) {
|
|
1182 |
p = strpbrk(p, ":.");
|
|
1183 |
if (!p) break;
|
|
1184 |
p++;
|
|
1185 |
|
|
1186 |
if (*p == '*')
|
|
1187 |
continue;
|
|
1188 |
|
|
1189 |
switch(i) {
|
|
1190 |
case BUS: f->bus_num = strtol(p, NULL, 10); break;
|
|
1191 |
case DEV: f->addr = strtol(p, NULL, 10); break;
|
|
1192 |
case VID: f->vendor_id = strtol(p, NULL, 16); break;
|
|
1193 |
case PID: f->product_id = strtol(p, NULL, 16); break;
|
|
1194 |
}
|
|
1195 |
}
|
|
1196 |
|
|
1197 |
if (i < DEV) {
|
|
1198 |
fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
|
|
1199 |
return -1;
|
|
1200 |
}
|
|
1201 |
|
|
1202 |
return 0;
|
|
1203 |
}
|
|
1204 |
|
|
1205 |
static int match_filter(const struct USBAutoFilter *f1,
|
|
1206 |
const struct USBAutoFilter *f2)
|
|
1207 |
{
|
|
1208 |
return f1->bus_num == f2->bus_num &&
|
|
1209 |
f1->addr == f2->addr &&
|
|
1210 |
f1->vendor_id == f2->vendor_id &&
|
|
1211 |
f1->product_id == f2->product_id;
|
|
1212 |
}
|
|
1213 |
|
|
1214 |
static int usb_host_auto_add(const char *spec)
|
|
1215 |
{
|
|
1216 |
struct USBAutoFilter filter, *f;
|
|
1217 |
|
|
1218 |
if (parse_filter(spec, &filter) < 0)
|
|
1219 |
return -1;
|
|
1220 |
|
|
1221 |
f = qemu_mallocz(sizeof(*f));
|
1135 |
1222 |
if (!f) {
|
1136 |
1223 |
fprintf(stderr, "husb: failed to allocate auto filter\n");
|
1137 |
|
return;
|
|
1224 |
return -1;
|
1138 |
1225 |
}
|
1139 |
1226 |
|
1140 |
|
f->bus_num = bus_num;
|
1141 |
|
f->addr = addr;
|
1142 |
|
f->vendor_id = vendor_id;
|
1143 |
|
f->product_id = product_id;
|
|
1227 |
*f = filter;
|
1144 |
1228 |
|
1145 |
1229 |
if (!usb_auto_filter) {
|
1146 |
1230 |
/*
|
... | ... | |
1153 |
1237 |
if (!usb_auto_timer) {
|
1154 |
1238 |
fprintf(stderr, "husb: failed to allocate auto scan timer\n");
|
1155 |
1239 |
qemu_free(f);
|
1156 |
|
return;
|
|
1240 |
return -1;
|
1157 |
1241 |
}
|
1158 |
1242 |
|
1159 |
1243 |
/* Check for new devices every two seconds */
|
1160 |
1244 |
qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
|
1161 |
1245 |
}
|
1162 |
1246 |
|
1163 |
|
dprintf("husb: auto filter: bus_num %d addr %d vid %d pid %d\n",
|
1164 |
|
bus_num, addr, vendor_id, product_id);
|
|
1247 |
dprintf("husb: added auto filter: bus_num %d addr %d vid %d pid %d\n",
|
|
1248 |
f->bus_num, f->addr, f->vendor_id, f->product_id);
|
1165 |
1249 |
|
1166 |
1250 |
f->next = usb_auto_filter;
|
1167 |
1251 |
usb_auto_filter = f;
|
|
1252 |
|
|
1253 |
return 0;
|
|
1254 |
}
|
|
1255 |
|
|
1256 |
static int usb_host_auto_del(const char *spec)
|
|
1257 |
{
|
|
1258 |
struct USBAutoFilter *pf = usb_auto_filter;
|
|
1259 |
struct USBAutoFilter **prev = &usb_auto_filter;
|
|
1260 |
struct USBAutoFilter filter;
|
|
1261 |
|
|
1262 |
if (parse_filter(spec, &filter) < 0)
|
|
1263 |
return -1;
|
|
1264 |
|
|
1265 |
while (pf) {
|
|
1266 |
if (match_filter(pf, &filter)) {
|
|
1267 |
dprintf("husb: removed auto filter: bus_num %d addr %d vid %d pid %d\n",
|
|
1268 |
pf->bus_num, pf->addr, pf->vendor_id, pf->product_id);
|
|
1269 |
|
|
1270 |
*prev = pf->next;
|
|
1271 |
|
|
1272 |
if (!usb_auto_filter) {
|
|
1273 |
/* No more filters. Stop scanning. */
|
|
1274 |
qemu_del_timer(usb_auto_timer);
|
|
1275 |
qemu_free_timer(usb_auto_timer);
|
|
1276 |
}
|
|
1277 |
|
|
1278 |
return 0;
|
|
1279 |
}
|
|
1280 |
|
|
1281 |
prev = &pf->next;
|
|
1282 |
pf = pf->next;
|
|
1283 |
}
|
|
1284 |
|
|
1285 |
return -1;
|
1168 |
1286 |
}
|
1169 |
1287 |
|
1170 |
1288 |
typedef struct FindDeviceState {
|
... | ... | |
1208 |
1326 |
p = strchr(devname, '.');
|
1209 |
1327 |
if (p) {
|
1210 |
1328 |
*pbus_num = strtoul(devname, NULL, 0);
|
1211 |
|
|
1212 |
|
if (*(p + 1) == '*') {
|
1213 |
|
usb_host_auto_add(*pbus_num, -1, -1, -1);
|
1214 |
|
return -1;
|
1215 |
|
}
|
1216 |
|
|
1217 |
1329 |
*paddr = strtoul(p + 1, NULL, 0);
|
1218 |
1330 |
fs.bus_num = *pbus_num;
|
1219 |
1331 |
fs.addr = *paddr;
|
... | ... | |
1222 |
1334 |
pstrcpy(product_name, product_name_size, fs.product_name);
|
1223 |
1335 |
return 0;
|
1224 |
1336 |
}
|
|
1337 |
|
1225 |
1338 |
p = strchr(devname, ':');
|
1226 |
1339 |
if (p) {
|
1227 |
1340 |
fs.vendor_id = strtoul(devname, NULL, 16);
|
1228 |
|
|
1229 |
|
if (*(p + 1) == '*') {
|
1230 |
|
usb_host_auto_add(-1, -1, fs.vendor_id, -1);
|
1231 |
|
return -1;
|
1232 |
|
}
|
1233 |
|
|
1234 |
1341 |
fs.product_id = strtoul(p + 1, NULL, 16);
|
1235 |
1342 |
ret = usb_host_scan(&fs, usb_host_find_device_scan);
|
1236 |
1343 |
if (ret) {
|
... | ... | |
1324 |
1431 |
return 0;
|
1325 |
1432 |
}
|
1326 |
1433 |
|
|
1434 |
static void dec2str(int val, char *str)
|
|
1435 |
{
|
|
1436 |
if (val == -1)
|
|
1437 |
strcpy(str, "*");
|
|
1438 |
else
|
|
1439 |
sprintf(str, "%d", val);
|
|
1440 |
}
|
|
1441 |
|
|
1442 |
static void hex2str(int val, char *str)
|
|
1443 |
{
|
|
1444 |
if (val == -1)
|
|
1445 |
strcpy(str, "*");
|
|
1446 |
else
|
|
1447 |
sprintf(str, "%x", val);
|
|
1448 |
}
|
|
1449 |
|
1327 |
1450 |
void usb_host_info(void)
|
1328 |
1451 |
{
|
|
1452 |
struct USBAutoFilter *f;
|
|
1453 |
|
1329 |
1454 |
usb_host_scan(NULL, usb_host_info_device);
|
|
1455 |
|
|
1456 |
if (usb_auto_filter)
|
|
1457 |
term_printf(" Auto filters:\n");
|
|
1458 |
for (f = usb_auto_filter; f; f = f->next) {
|
|
1459 |
char bus[10], addr[10], vid[10], pid[10];
|
|
1460 |
dec2str(f->bus_num, bus);
|
|
1461 |
dec2str(f->addr, addr);
|
|
1462 |
hex2str(f->vendor_id, vid);
|
|
1463 |
hex2str(f->product_id, pid);
|
|
1464 |
term_printf(" Device %s.%s ID %s:%s\n", bus, addr, vid, pid);
|
|
1465 |
}
|
1330 |
1466 |
}
|
1331 |
1467 |
|
1332 |
1468 |
#else
|
... | ... | |
1344 |
1480 |
return NULL;
|
1345 |
1481 |
}
|
1346 |
1482 |
|
|
1483 |
int usb_host_device_close(const char *devname)
|
|
1484 |
{
|
|
1485 |
return 0;
|
|
1486 |
}
|
|
1487 |
|
1347 |
1488 |
#endif
|