mirror of https://gitee.com/openkylin/qemu.git
Merge remote-tracking branch 'kraxel/usb.74' into staging
* kraxel/usb.74: usb-tablet: Allow connecting to ehci ehci: Lower timer freq when the periodic schedule is idle usb: Allow overriding of usb_desc at the device level usb: Don't allow USB_RET_ASYNC for interrupt packets usb: Call wakeup when data becomes available for all devices with int eps add pc-1.4 Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
c62adbee15
24
hw/pc_piix.c
24
hw/pc_piix.c
|
@ -281,8 +281,8 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args)
|
|||
}
|
||||
#endif
|
||||
|
||||
static QEMUMachine pc_machine_v1_3 = {
|
||||
.name = "pc-1.3",
|
||||
static QEMUMachine pc_machine_v1_4 = {
|
||||
.name = "pc-1.4",
|
||||
.alias = "pc",
|
||||
.desc = "Standard PC",
|
||||
.init = pc_init_pci_1_3,
|
||||
|
@ -290,7 +290,26 @@ static QEMUMachine pc_machine_v1_3 = {
|
|||
.is_default = 1,
|
||||
};
|
||||
|
||||
#define PC_COMPAT_1_3 \
|
||||
{\
|
||||
.driver = "usb-tablet",\
|
||||
.property = "usb_version",\
|
||||
.value = stringify(1),\
|
||||
}
|
||||
|
||||
static QEMUMachine pc_machine_v1_3 = {
|
||||
.name = "pc-1.3",
|
||||
.desc = "Standard PC",
|
||||
.init = pc_init_pci_1_3,
|
||||
.max_cpus = 255,
|
||||
.compat_props = (GlobalProperty[]) {
|
||||
PC_COMPAT_1_3,
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
#define PC_COMPAT_1_2 \
|
||||
PC_COMPAT_1_3,\
|
||||
{\
|
||||
.driver = "nec-usb-xhci",\
|
||||
.property = "msi",\
|
||||
|
@ -626,6 +645,7 @@ static QEMUMachine xenfv_machine = {
|
|||
|
||||
static void pc_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&pc_machine_v1_4);
|
||||
qemu_register_machine(&pc_machine_v1_3);
|
||||
qemu_register_machine(&pc_machine_v1_2);
|
||||
qemu_register_machine(&pc_machine_v1_1);
|
||||
|
|
2
hw/usb.h
2
hw/usb.h
|
@ -197,6 +197,7 @@ struct USBEndpoint {
|
|||
|
||||
enum USBDeviceFlags {
|
||||
USB_DEV_FLAG_FULL_PATH,
|
||||
USB_DEV_FLAG_IS_HOST,
|
||||
};
|
||||
|
||||
/* definition of a USB device */
|
||||
|
@ -229,6 +230,7 @@ struct USBDevice {
|
|||
USBEndpoint ep_out[USB_MAX_ENDPOINTS];
|
||||
|
||||
QLIST_HEAD(, USBDescString) strings;
|
||||
const USBDesc *usb_desc; /* Overrides class usb_desc if not NULL */
|
||||
const USBDescDevice *device;
|
||||
|
||||
int configuration;
|
||||
|
|
|
@ -166,6 +166,9 @@ const char *usb_device_get_product_desc(USBDevice *dev)
|
|||
const USBDesc *usb_device_get_usb_desc(USBDevice *dev)
|
||||
{
|
||||
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
|
||||
if (dev->usb_desc) {
|
||||
return dev->usb_desc;
|
||||
}
|
||||
return klass->usb_desc;
|
||||
}
|
||||
|
||||
|
|
|
@ -406,7 +406,11 @@ void usb_handle_packet(USBDevice *dev, USBPacket *p)
|
|||
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
|
||||
usb_process_one(p);
|
||||
if (p->status == USB_RET_ASYNC) {
|
||||
/* hcd drivers cannot handle async for isoc */
|
||||
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
|
||||
/* using async for interrupt packets breaks migration */
|
||||
assert(p->ep->type != USB_ENDPOINT_XFER_INT ||
|
||||
(dev->flags & USB_DEV_FLAG_IS_HOST));
|
||||
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||
} else if (p->status == USB_RET_ADD_TO_QUEUE) {
|
||||
|
|
|
@ -46,6 +46,7 @@ typedef struct USBHIDState {
|
|||
USBDevice dev;
|
||||
USBEndpoint *intr;
|
||||
HIDState hid;
|
||||
uint32_t usb_version;
|
||||
} USBHIDState;
|
||||
|
||||
enum {
|
||||
|
@ -131,6 +132,36 @@ static const USBDescIface desc_iface_tablet = {
|
|||
},
|
||||
};
|
||||
|
||||
static const USBDescIface desc_iface_tablet2 = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceProtocol = 0x02,
|
||||
.ndesc = 1,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* HID descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_HID, /* u8 bDescriptorType */
|
||||
0x01, 0x00, /* u16 HID_class */
|
||||
0x00, /* u8 country_code */
|
||||
0x01, /* u8 num_descriptors */
|
||||
USB_DT_REPORT, /* u8 type: Report */
|
||||
74, 0, /* u16 len */
|
||||
},
|
||||
},
|
||||
},
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = 8,
|
||||
.bInterval = 4, /* 2 ^ (4-1) * 125 usecs = 1 ms */
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDescIface desc_iface_keyboard = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
|
@ -196,6 +227,23 @@ static const USBDescDevice desc_device_tablet = {
|
|||
},
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_tablet2 = {
|
||||
.bcdUSB = 0x0200,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_TABLET,
|
||||
.bmAttributes = 0xa0,
|
||||
.bMaxPower = 50,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_tablet2,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_keyboard = {
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 8,
|
||||
|
@ -239,6 +287,20 @@ static const USBDesc desc_tablet = {
|
|||
.str = desc_strings,
|
||||
};
|
||||
|
||||
static const USBDesc desc_tablet2 = {
|
||||
.id = {
|
||||
.idVendor = 0x0627,
|
||||
.idProduct = 0x0001,
|
||||
.bcdDevice = 0,
|
||||
.iManufacturer = STR_MANUFACTURER,
|
||||
.iProduct = STR_PRODUCT_TABLET,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device_tablet,
|
||||
.high = &desc_device_tablet2,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
static const USBDesc desc_keyboard = {
|
||||
.id = {
|
||||
.idVendor = 0x0627,
|
||||
|
@ -508,6 +570,21 @@ static int usb_hid_initfn(USBDevice *dev, int kind)
|
|||
|
||||
static int usb_tablet_initfn(USBDevice *dev)
|
||||
{
|
||||
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
|
||||
|
||||
switch (us->usb_version) {
|
||||
case 1:
|
||||
dev->usb_desc = &desc_tablet;
|
||||
break;
|
||||
case 2:
|
||||
dev->usb_desc = &desc_tablet2;
|
||||
break;
|
||||
default:
|
||||
error_report("Invalid usb version %d for usb-tabler (must be 1 or 2)",
|
||||
us->usb_version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return usb_hid_initfn(dev, HID_TABLET);
|
||||
}
|
||||
|
||||
|
@ -562,8 +639,14 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
|
|||
uc->handle_control = usb_hid_handle_control;
|
||||
uc->handle_data = usb_hid_handle_data;
|
||||
uc->handle_destroy = usb_hid_handle_destroy;
|
||||
uc->handle_attach = usb_desc_attach;
|
||||
}
|
||||
|
||||
static Property usb_tablet_properties[] = {
|
||||
DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void usb_tablet_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -572,8 +655,8 @@ static void usb_tablet_class_initfn(ObjectClass *klass, void *data)
|
|||
usb_hid_class_initfn(klass, data);
|
||||
uc->init = usb_tablet_initfn;
|
||||
uc->product_desc = "QEMU USB Tablet";
|
||||
uc->usb_desc = &desc_tablet;
|
||||
dc->vmsd = &vmstate_usb_ptr;
|
||||
dc->props = usb_tablet_properties;
|
||||
}
|
||||
|
||||
static TypeInfo usb_tablet_info = {
|
||||
|
|
|
@ -184,6 +184,7 @@ static void usb_hub_detach(USBPort *port1)
|
|||
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
||||
port->wPortChange |= PORT_STAT_C_ENABLE;
|
||||
}
|
||||
usb_wakeup(s->intr);
|
||||
}
|
||||
|
||||
static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
|
||||
|
@ -363,6 +364,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
port->wPortChange |= PORT_STAT_C_RESET;
|
||||
/* set enable bit */
|
||||
port->wPortStatus |= PORT_STAT_ENABLE;
|
||||
usb_wakeup(s->intr);
|
||||
}
|
||||
break;
|
||||
case PORT_POWER:
|
||||
|
|
|
@ -639,6 +639,8 @@ typedef struct USBNetState {
|
|||
unsigned int in_ptr, in_len;
|
||||
uint8_t in_buf[2048];
|
||||
|
||||
USBEndpoint *intr;
|
||||
|
||||
char usbstring_mac[13];
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
|
@ -851,6 +853,10 @@ static void *rndis_queue_response(USBNetState *s, unsigned int length)
|
|||
struct rndis_response *r =
|
||||
g_malloc0(sizeof(struct rndis_response) + length);
|
||||
|
||||
if (QTAILQ_EMPTY(&s->rndis_resp)) {
|
||||
usb_wakeup(s->intr);
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries);
|
||||
r->length = length;
|
||||
|
||||
|
@ -1349,6 +1355,7 @@ static int usb_net_initfn(USBDevice *dev)
|
|||
s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */;
|
||||
s->filter = 0;
|
||||
s->vendorid = 0x1234;
|
||||
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||
|
||||
qemu_macaddr_default_if_unset(&s->conf.macaddr);
|
||||
s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
typedef struct USBWacomState {
|
||||
USBDevice dev;
|
||||
USBEndpoint *intr;
|
||||
QEMUPutMouseEntry *eh_entry;
|
||||
int dx, dy, dz, buttons_state;
|
||||
int x, y;
|
||||
|
@ -137,6 +138,7 @@ static void usb_mouse_event(void *opaque,
|
|||
s->dz += dz1;
|
||||
s->buttons_state = buttons_state;
|
||||
s->changed = 1;
|
||||
usb_wakeup(s->intr);
|
||||
}
|
||||
|
||||
static void usb_wacom_event(void *opaque,
|
||||
|
@ -150,6 +152,7 @@ static void usb_wacom_event(void *opaque,
|
|||
s->dz += dz;
|
||||
s->buttons_state = buttons_state;
|
||||
s->changed = 1;
|
||||
usb_wakeup(s->intr);
|
||||
}
|
||||
|
||||
static inline int int_clamp(int val, int vmin, int vmax)
|
||||
|
@ -337,6 +340,7 @@ static int usb_wacom_initfn(USBDevice *dev)
|
|||
USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
|
||||
usb_desc_create_serial(dev);
|
||||
usb_desc_init(dev);
|
||||
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||
s->changed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
|
||||
#define MAX_QH 100 // Max allowable queue heads in a chain
|
||||
#define MIN_FR_PER_TICK 3 // Min frames to process when catching up
|
||||
#define PERIODIC_ACTIVE 64
|
||||
|
||||
/* Internal periodic / asynchronous schedule state machine states
|
||||
*/
|
||||
|
@ -738,6 +739,19 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
|
||||
{
|
||||
EHCIState *s = container_of(bus, EHCIState, bus);
|
||||
uint32_t portsc = s->portsc[ep->dev->port->index];
|
||||
|
||||
if (portsc & PORTSC_POWNER) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->periodic_sched_active = PERIODIC_ACTIVE;
|
||||
qemu_bh_schedule(s->async_bh);
|
||||
}
|
||||
|
||||
static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr)
|
||||
{
|
||||
USBDevice *dev;
|
||||
|
@ -1188,9 +1202,10 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
|||
trace_usb_ehci_packet_action(p->queue, p, "wakeup");
|
||||
p->async = EHCI_ASYNC_FINISHED;
|
||||
|
||||
if (p->queue->async) {
|
||||
qemu_bh_schedule(p->queue->ehci->async_bh);
|
||||
if (!p->queue->async) {
|
||||
s->periodic_sched_active = PERIODIC_ACTIVE;
|
||||
}
|
||||
qemu_bh_schedule(s->async_bh);
|
||||
}
|
||||
|
||||
static void ehci_execute_complete(EHCIQueue *q)
|
||||
|
@ -1344,6 +1359,8 @@ static int ehci_process_itd(EHCIState *ehci,
|
|||
uint32_t i, len, pid, dir, devaddr, endp;
|
||||
uint32_t pg, off, ptr1, ptr2, max, mult;
|
||||
|
||||
ehci->periodic_sched_active = PERIODIC_ACTIVE;
|
||||
|
||||
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
|
||||
devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
|
||||
endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
|
||||
|
@ -2033,6 +2050,9 @@ static void ehci_advance_state(EHCIState *ehci, int async)
|
|||
case EST_WRITEBACK:
|
||||
assert(q != NULL);
|
||||
again = ehci_state_writeback(q);
|
||||
if (!async) {
|
||||
ehci->periodic_sched_active = PERIODIC_ACTIVE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -2198,7 +2218,6 @@ static void ehci_frame_timer(void *opaque)
|
|||
|
||||
if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
|
||||
need_timer++;
|
||||
ehci->async_stepdown = 0;
|
||||
|
||||
if (frames > ehci->maxframes) {
|
||||
skipped_frames = frames - ehci->maxframes;
|
||||
|
@ -2222,18 +2241,25 @@ static void ehci_frame_timer(void *opaque)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (ehci->periodic_sched_active) {
|
||||
ehci->periodic_sched_active--;
|
||||
}
|
||||
ehci_update_frindex(ehci, 1);
|
||||
ehci_advance_periodic_state(ehci);
|
||||
ehci->last_run_ns += FRAME_TIMER_NS;
|
||||
}
|
||||
} else {
|
||||
if (ehci->async_stepdown < ehci->maxframes / 2) {
|
||||
ehci->async_stepdown++;
|
||||
}
|
||||
ehci->periodic_sched_active = 0;
|
||||
ehci_update_frindex(ehci, frames);
|
||||
ehci->last_run_ns += FRAME_TIMER_NS * frames;
|
||||
}
|
||||
|
||||
if (ehci->periodic_sched_active) {
|
||||
ehci->async_stepdown = 0;
|
||||
} else if (ehci->async_stepdown < ehci->maxframes / 2) {
|
||||
ehci->async_stepdown++;
|
||||
}
|
||||
|
||||
/* Async is not inside loop since it executes everything it can once
|
||||
* called
|
||||
*/
|
||||
|
@ -2301,6 +2327,7 @@ static USBPortOps ehci_port_ops = {
|
|||
|
||||
static USBBusOps ehci_bus_ops = {
|
||||
.register_companion = ehci_register_companion,
|
||||
.wakeup_endpoint = ehci_wakeup_endpoint,
|
||||
};
|
||||
|
||||
static int usb_ehci_post_load(void *opaque, int version_id)
|
||||
|
|
|
@ -311,6 +311,7 @@ struct EHCIState {
|
|||
|
||||
uint64_t last_run_ns;
|
||||
uint32_t async_stepdown;
|
||||
uint32_t periodic_sched_active;
|
||||
bool int_req_by_async;
|
||||
};
|
||||
|
||||
|
|
|
@ -292,6 +292,7 @@ static void usb_host_handle_destroy(USBDevice *opaque)
|
|||
|
||||
static int usb_host_initfn(USBDevice *dev)
|
||||
{
|
||||
dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1476,6 +1476,7 @@ static int usb_host_initfn(USBDevice *dev)
|
|||
{
|
||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
||||
|
||||
dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
|
||||
dev->auto_attach = 0;
|
||||
s->fd = -1;
|
||||
s->hub_fd = -1;
|
||||
|
|
|
@ -1644,6 +1644,10 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
|||
return;
|
||||
}
|
||||
|
||||
if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) {
|
||||
usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f));
|
||||
}
|
||||
|
||||
/* bufp_alloc also adds the packet to the ep queue */
|
||||
bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue