3 |
3 |
*
|
4 |
4 |
* Copyright (c) 2005 Fabrice Bellard
|
5 |
5 |
*
|
|
6 |
* Support for host device auto connect & disconnect
|
|
7 |
* Copyright (c) 2008 Max Krasnyansky
|
|
8 |
*
|
6 |
9 |
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 |
10 |
* of this software and associated documentation files (the "Software"), to deal
|
8 |
11 |
* in the Software without restriction, including without limitation the rights
|
... | ... | |
67 |
70 |
uint8_t type;
|
68 |
71 |
};
|
69 |
72 |
|
|
73 |
|
|
74 |
|
70 |
75 |
/* FIXME: move USBPacket to PendingURB */
|
71 |
76 |
typedef struct USBHostDevice {
|
72 |
77 |
USBDevice dev;
|
... | ... | |
78 |
83 |
uint8_t descr[1024];
|
79 |
84 |
int descr_len;
|
80 |
85 |
int urbs_ready;
|
|
86 |
|
81 |
87 |
QEMUTimer *timer;
|
|
88 |
|
|
89 |
/* Host side address */
|
|
90 |
int bus_num;
|
|
91 |
int addr;
|
|
92 |
|
|
93 |
struct USBHostDevice *next;
|
82 |
94 |
} USBHostDevice;
|
83 |
95 |
|
|
96 |
static USBHostDevice *hostdev_list;
|
|
97 |
|
|
98 |
static void hostdev_link(USBHostDevice *dev)
|
|
99 |
{
|
|
100 |
dev->next = hostdev_list;
|
|
101 |
hostdev_list = dev;
|
|
102 |
}
|
|
103 |
|
|
104 |
static void hostdev_unlink(USBHostDevice *dev)
|
|
105 |
{
|
|
106 |
USBHostDevice *pdev = hostdev_list;
|
|
107 |
USBHostDevice **prev = &hostdev_list;
|
|
108 |
|
|
109 |
while (pdev) {
|
|
110 |
if (pdev == dev) {
|
|
111 |
*prev = dev->next;
|
|
112 |
return;
|
|
113 |
}
|
|
114 |
|
|
115 |
prev = &pdev->next;
|
|
116 |
pdev = pdev->next;
|
|
117 |
}
|
|
118 |
}
|
|
119 |
|
|
120 |
static USBHostDevice *hostdev_find(int bus_num, int addr)
|
|
121 |
{
|
|
122 |
USBHostDevice *s = hostdev_list;
|
|
123 |
while (s) {
|
|
124 |
if (s->bus_num == bus_num && s->addr == addr)
|
|
125 |
return s;
|
|
126 |
s = s->next;
|
|
127 |
}
|
|
128 |
return NULL;
|
|
129 |
}
|
|
130 |
|
84 |
131 |
typedef struct PendingURB {
|
85 |
132 |
struct usbdevfs_urb *urb;
|
86 |
133 |
int status;
|
... | ... | |
238 |
285 |
|
239 |
286 |
qemu_del_timer(s->timer);
|
240 |
287 |
|
|
288 |
hostdev_unlink(s);
|
|
289 |
|
241 |
290 |
if (s->fd >= 0)
|
242 |
291 |
close(s->fd);
|
243 |
292 |
|
... | ... | |
619 |
668 |
qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
|
620 |
669 |
}
|
621 |
670 |
|
622 |
|
/* XXX: exclude high speed devices or implement EHCI */
|
623 |
|
USBDevice *usb_host_device_open(const char *devname)
|
|
671 |
static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *prod_name)
|
624 |
672 |
{
|
625 |
673 |
int fd = -1, ret;
|
626 |
674 |
USBHostDevice *dev = NULL;
|
627 |
675 |
struct usbdevfs_connectinfo ci;
|
628 |
676 |
char buf[1024];
|
629 |
|
int bus_num, addr;
|
630 |
|
char product_name[PRODUCT_NAME_SZ];
|
631 |
|
|
632 |
|
if (usb_host_find_device(&bus_num, &addr,
|
633 |
|
product_name, sizeof(product_name),
|
634 |
|
devname) < 0)
|
635 |
|
return NULL;
|
636 |
|
|
637 |
677 |
|
638 |
678 |
dev = qemu_mallocz(sizeof(USBHostDevice));
|
639 |
679 |
if (!dev)
|
640 |
680 |
goto fail;
|
641 |
681 |
|
|
682 |
dev->bus_num = bus_num;
|
|
683 |
dev->addr = addr;
|
|
684 |
|
642 |
685 |
dev->timer = qemu_new_timer(rt_clock, usb_host_device_check, (void *) dev);
|
643 |
686 |
if (!dev->timer)
|
644 |
687 |
goto fail;
|
645 |
688 |
|
646 |
689 |
#ifdef DEBUG
|
647 |
|
printf("usb_host_device_open %s\n", devname);
|
|
690 |
printf("usb_host_device_open %d.%d\n", bus_num, addr);
|
648 |
691 |
#endif
|
649 |
692 |
|
650 |
693 |
snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
|
... | ... | |
704 |
747 |
dev->dev.handle_data = usb_host_handle_data;
|
705 |
748 |
dev->dev.handle_destroy = usb_host_handle_destroy;
|
706 |
749 |
|
707 |
|
if (product_name[0] == '\0')
|
|
750 |
if (!prod_name || prod_name[0] == '\0')
|
708 |
751 |
snprintf(dev->dev.devname, sizeof(dev->dev.devname),
|
709 |
|
"host:%s", devname);
|
|
752 |
"host:%d.%d", bus_num, addr);
|
710 |
753 |
else
|
711 |
754 |
pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
|
712 |
|
product_name);
|
|
755 |
prod_name);
|
713 |
756 |
|
714 |
757 |
#ifdef USE_ASYNCIO
|
715 |
758 |
/* set up the signal handlers */
|
... | ... | |
735 |
778 |
/* Start the timer to detect disconnect */
|
736 |
779 |
qemu_mod_timer(dev->timer, qemu_get_clock(rt_clock) + 1000);
|
737 |
780 |
|
|
781 |
hostdev_link(dev);
|
|
782 |
|
738 |
783 |
dev->urbs_ready = 0;
|
739 |
784 |
return (USBDevice *)dev;
|
|
785 |
|
740 |
786 |
fail:
|
741 |
787 |
if (dev) {
|
742 |
788 |
if (dev->timer)
|
... | ... | |
747 |
793 |
return NULL;
|
748 |
794 |
}
|
749 |
795 |
|
|
796 |
USBDevice *usb_host_device_open(const char *devname)
|
|
797 |
{
|
|
798 |
int bus_num, addr;
|
|
799 |
char product_name[PRODUCT_NAME_SZ];
|
|
800 |
|
|
801 |
if (usb_host_find_device(&bus_num, &addr,
|
|
802 |
product_name, sizeof(product_name),
|
|
803 |
devname) < 0)
|
|
804 |
return NULL;
|
|
805 |
|
|
806 |
if (hostdev_find(bus_num, addr)) {
|
|
807 |
printf("host usb device %d.%d is already open\n", bus_num, addr);
|
|
808 |
return NULL;
|
|
809 |
}
|
|
810 |
|
|
811 |
return usb_host_device_open_addr(bus_num, addr, product_name);
|
|
812 |
}
|
|
813 |
|
750 |
814 |
static int get_tag_value(char *buf, int buf_size,
|
751 |
815 |
const char *str, const char *tag,
|
752 |
816 |
const char *stopchars)
|
... | ... | |
846 |
910 |
return ret;
|
847 |
911 |
}
|
848 |
912 |
|
|
913 |
struct USBAutoFilter {
|
|
914 |
struct USBAutoFilter *next;
|
|
915 |
int bus_num;
|
|
916 |
int addr;
|
|
917 |
int vendor_id;
|
|
918 |
int product_id;
|
|
919 |
};
|
|
920 |
|
|
921 |
static QEMUTimer *usb_auto_timer;
|
|
922 |
static struct USBAutoFilter *usb_auto_filter;
|
|
923 |
|
|
924 |
static int usb_host_auto_scan(void *opaque, int bus_num, int addr,
|
|
925 |
int class_id, int vendor_id, int product_id,
|
|
926 |
const char *product_name, int speed)
|
|
927 |
{
|
|
928 |
struct USBAutoFilter *f;
|
|
929 |
struct USBDevice *dev;
|
|
930 |
|
|
931 |
/* Ignore hubs */
|
|
932 |
if (class_id == 9)
|
|
933 |
return 0;
|
|
934 |
|
|
935 |
for (f = usb_auto_filter; f; f = f->next) {
|
|
936 |
// printf("Auto match: bus_num %d addr %d vid %d pid %d\n",
|
|
937 |
// bus_num, addr, vendor_id, product_id);
|
|
938 |
|
|
939 |
if (f->bus_num >= 0 && f->bus_num != bus_num)
|
|
940 |
continue;
|
|
941 |
|
|
942 |
if (f->addr >= 0 && f->addr != addr)
|
|
943 |
continue;
|
|
944 |
|
|
945 |
if (f->vendor_id >= 0 && f->vendor_id != vendor_id)
|
|
946 |
continue;
|
|
947 |
|
|
948 |
if (f->product_id >= 0 && f->product_id != product_id)
|
|
949 |
continue;
|
|
950 |
|
|
951 |
/* We got a match */
|
|
952 |
|
|
953 |
/* Allredy attached ? */
|
|
954 |
if (hostdev_find(bus_num, addr))
|
|
955 |
return 0;
|
|
956 |
|
|
957 |
printf("Auto open: bus_num %d addr %d\n", bus_num, addr);
|
|
958 |
|
|
959 |
dev = usb_host_device_open_addr(bus_num, addr, product_name);
|
|
960 |
if (dev)
|
|
961 |
usb_device_add_dev(dev);
|
|
962 |
}
|
|
963 |
|
|
964 |
return 0;
|
|
965 |
}
|
|
966 |
|
|
967 |
static void usb_host_auto_timer(void *unused)
|
|
968 |
{
|
|
969 |
usb_host_scan(NULL, usb_host_auto_scan);
|
|
970 |
qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
|
|
971 |
}
|
|
972 |
|
|
973 |
/*
|
|
974 |
* Add autoconnect filter
|
|
975 |
* -1 means 'any' (device, vendor, etc)
|
|
976 |
*/
|
|
977 |
static void usb_host_auto_add(int bus_num, int addr, int vendor_id, int product_id)
|
|
978 |
{
|
|
979 |
struct USBAutoFilter *f = qemu_mallocz(sizeof(*f));
|
|
980 |
if (!f) {
|
|
981 |
printf("Failed to allocate auto filter\n");
|
|
982 |
return;
|
|
983 |
}
|
|
984 |
|
|
985 |
f->bus_num = bus_num;
|
|
986 |
f->addr = addr;
|
|
987 |
f->vendor_id = vendor_id;
|
|
988 |
f->product_id = product_id;
|
|
989 |
|
|
990 |
if (!usb_auto_filter) {
|
|
991 |
/*
|
|
992 |
* First entry. Init and start the monitor.
|
|
993 |
* Right now we're using timer to check for new devices.
|
|
994 |
* If this turns out to be too expensive we can move that into a
|
|
995 |
* separate thread.
|
|
996 |
*/
|
|
997 |
usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_timer, NULL);
|
|
998 |
if (!usb_auto_timer) {
|
|
999 |
printf("Failed to allocate timer\n");
|
|
1000 |
qemu_free(f);
|
|
1001 |
return;
|
|
1002 |
}
|
|
1003 |
|
|
1004 |
/* Check for new devices every two seconds */
|
|
1005 |
qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
|
|
1006 |
}
|
|
1007 |
|
|
1008 |
printf("Auto filter: bus_num %d addr %d vid %d pid %d\n",
|
|
1009 |
bus_num, addr, vendor_id, product_id);
|
|
1010 |
|
|
1011 |
f->next = usb_auto_filter;
|
|
1012 |
usb_auto_filter = f;
|
|
1013 |
}
|
|
1014 |
|
849 |
1015 |
typedef struct FindDeviceState {
|
850 |
1016 |
int vendor_id;
|
851 |
1017 |
int product_id;
|
... | ... | |
887 |
1053 |
p = strchr(devname, '.');
|
888 |
1054 |
if (p) {
|
889 |
1055 |
*pbus_num = strtoul(devname, NULL, 0);
|
|
1056 |
|
|
1057 |
if (*(p + 1) == '*') {
|
|
1058 |
usb_host_auto_add(*pbus_num, -1, -1, -1);
|
|
1059 |
return -1;
|
|
1060 |
}
|
|
1061 |
|
890 |
1062 |
*paddr = strtoul(p + 1, NULL, 0);
|
891 |
1063 |
fs.bus_num = *pbus_num;
|
892 |
1064 |
fs.addr = *paddr;
|
... | ... | |
898 |
1070 |
p = strchr(devname, ':');
|
899 |
1071 |
if (p) {
|
900 |
1072 |
fs.vendor_id = strtoul(devname, NULL, 16);
|
|
1073 |
|
|
1074 |
if (*(p + 1) == '*') {
|
|
1075 |
usb_host_auto_add(-1, -1, fs.vendor_id, -1);
|
|
1076 |
return -1;
|
|
1077 |
}
|
|
1078 |
|
901 |
1079 |
fs.product_id = strtoul(p + 1, NULL, 16);
|
902 |
1080 |
ret = usb_host_scan(&fs, usb_host_find_device_scan);
|
903 |
1081 |
if (ret) {
|
... | ... | |
996 |
1174 |
usb_host_scan(NULL, usb_host_info_device);
|
997 |
1175 |
}
|
998 |
1176 |
|
|
1177 |
|
|
1178 |
|
|
1179 |
|
999 |
1180 |
#else
|
1000 |
1181 |
|
1001 |
1182 |
void usb_host_info(void)
|