*/
#include "hw/hw.h"
#include "hw/usb.h"
+#include "hw/usb/uhci-regs.h"
#include "hw/pci/pci.h"
#include "qemu/timer.h"
#include "qemu/iov.h"
#include "sysemu/dma.h"
#include "trace.h"
+#include "qemu/main-loop.h"
//#define DEBUG
//#define DEBUG_DUMP_DATA
-#define UHCI_CMD_FGR (1 << 4)
-#define UHCI_CMD_EGSM (1 << 3)
-#define UHCI_CMD_GRESET (1 << 2)
-#define UHCI_CMD_HCRESET (1 << 1)
-#define UHCI_CMD_RS (1 << 0)
-
-#define UHCI_STS_HCHALTED (1 << 5)
-#define UHCI_STS_HCPERR (1 << 4)
-#define UHCI_STS_HSERR (1 << 3)
-#define UHCI_STS_RD (1 << 2)
-#define UHCI_STS_USBERR (1 << 1)
-#define UHCI_STS_USBINT (1 << 0)
-
-#define TD_CTRL_SPD (1 << 29)
-#define TD_CTRL_ERROR_SHIFT 27
-#define TD_CTRL_IOS (1 << 25)
-#define TD_CTRL_IOC (1 << 24)
-#define TD_CTRL_ACTIVE (1 << 23)
-#define TD_CTRL_STALL (1 << 22)
-#define TD_CTRL_BABBLE (1 << 20)
-#define TD_CTRL_NAK (1 << 19)
-#define TD_CTRL_TIMEOUT (1 << 18)
-
-#define UHCI_PORT_SUSPEND (1 << 12)
-#define UHCI_PORT_RESET (1 << 9)
-#define UHCI_PORT_LSDA (1 << 8)
-#define UHCI_PORT_RD (1 << 6)
-#define UHCI_PORT_ENC (1 << 3)
-#define UHCI_PORT_EN (1 << 2)
-#define UHCI_PORT_CSC (1 << 1)
-#define UHCI_PORT_CCS (1 << 0)
-
-#define UHCI_PORT_READ_ONLY (0x1bb)
-#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC)
-
#define FRAME_TIMER_FREQ 1000
#define FRAME_MAX_LOOPS 256
struct UHCIAsync {
USBPacket packet;
- QEMUSGList sgl;
+ uint8_t static_buf[64]; /* 64 bytes is enough, except for isoc packets */
+ uint8_t *buf;
UHCIQueue *queue;
QTAILQ_ENTRY(UHCIAsync) next;
uint32_t td_addr;
/* Interrupts that should be raised at the end of the current frame. */
uint32_t pending_int_mask;
- int irq_pin;
/* Active packets */
QTAILQ_HEAD(, UHCIQueue) queues;
static void uhci_async_cancel(UHCIAsync *async);
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
+static void uhci_resume(void *opaque);
static inline int32_t uhci_queue_token(UHCI_TD *td)
{
uint32_t td_addr, bool queuing)
{
UHCIAsync *first = QTAILQ_FIRST(&queue->asyncs);
+ uint32_t queue_token_addr = (queue->token >> 8) & 0x7f;
return queue->qh_addr == qh_addr &&
queue->token == uhci_queue_token(td) &&
+ queue_token_addr == queue->ep->dev->addr &&
(queuing || !(td->ctrl & TD_CTRL_ACTIVE) || first == NULL ||
first->td_addr == td_addr);
}
async->queue = queue;
async->td_addr = td_addr;
usb_packet_init(&async->packet);
- pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
trace_usb_uhci_packet_add(async->queue->token, async->td_addr);
return async;
{
trace_usb_uhci_packet_del(async->queue->token, async->td_addr);
usb_packet_cleanup(&async->packet);
- qemu_sglist_destroy(&async->sgl);
+ if (async->buf != async->static_buf) {
+ g_free(async->buf);
+ }
g_free(async);
}
async->done);
if (!async->done)
usb_cancel_packet(&async->packet);
- usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
}
} else {
level = 0;
}
- qemu_set_irq(s->dev.irq[s->irq_pin], level);
+ pci_set_irq(&s->dev, level);
}
static void uhci_reset(void *opaque)
.name = "uhci port",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField []) {
+ .fields = (VMStateField[]) {
VMSTATE_UINT16(ctrl, UHCIPort),
VMSTATE_END_OF_LIST()
}
UHCIState *s = opaque;
if (version_id < 2) {
- s->expire_time = qemu_get_clock_ns(vm_clock) +
+ s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
(get_ticks_per_sec() / FRAME_TIMER_FREQ);
}
return 0;
.name = "uhci",
.version_id = 3,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.post_load = uhci_post_load,
- .fields = (VMStateField []) {
+ .fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, UHCIState),
VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1,
if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
/* start frame processing */
trace_usb_uhci_schedule_start();
- s->expire_time = qemu_get_clock_ns(vm_clock) +
+ s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
(get_ticks_per_sec() / FRAME_TIMER_FREQ);
- qemu_mod_timer(s->frame_timer, s->expire_time);
+ timer_mod(s->frame_timer, s->expire_time);
s->status &= ~UHCI_STS_HCHALTED;
} else if (!(val & UHCI_CMD_RS)) {
s->status |= UHCI_STS_HCHALTED;
return;
}
s->cmd = val;
+ if (val & UHCI_CMD_EGSM) {
+ if ((s->ports[0].ctrl & UHCI_PORT_RD) ||
+ (s->ports[1].ctrl & UHCI_PORT_RD)) {
+ uhci_resume(s);
+ }
+ }
break;
case 0x02:
s->status &= ~val;
*int_mask |= 0x01;
if (pid == USB_TOKEN_IN) {
+ pci_dma_write(&s->dev, td->buffer, async->buf, len);
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
*int_mask |= 0x02;
/* short packet: do not update QH */
max_len = ((td->token >> 21) + 1) & 0x7ff;
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
- usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd,
+ usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd,
(td->ctrl & TD_CTRL_IOC) != 0);
- qemu_sglist_add(&async->sgl, td->buffer, max_len);
- usb_packet_map(&async->packet, &async->sgl);
+ if (max_len <= sizeof(async->static_buf)) {
+ async->buf = async->static_buf;
+ } else {
+ async->buf = g_malloc(max_len);
+ }
+ usb_packet_addbuf(&async->packet, async->buf, max_len);
switch(pid) {
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
+ pci_dma_read(&s->dev, td->buffer, async->buf, max_len);
usb_handle_packet(q->ep->dev, &async->packet);
if (async->packet.status == USB_RET_SUCCESS) {
async->packet.actual_length = max_len;
default:
/* invalid pid : frame interrupted */
- usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
s->status |= UHCI_STS_HCPERR;
uhci_update_irq(s);
done:
ret = uhci_complete_td(s, td, async, int_mask);
- usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
return ret;
}
if (!(s->cmd & UHCI_CMD_RS)) {
/* Full stop */
trace_usb_uhci_schedule_stop();
- qemu_del_timer(s->frame_timer);
+ timer_del(s->frame_timer);
uhci_async_cancel_all(s);
/* set hchalted bit in status - UHCI11D 2.1.2 */
s->status |= UHCI_STS_HCHALTED;
/* We still store expire_time in our state, for migration */
t_last_run = s->expire_time - frame_t;
- t_now = qemu_get_clock_ns(vm_clock);
+ t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
/* Process up to MAX_FRAMES_PER_TICK frames */
frames = (t_now - t_last_run) / frame_t;
}
s->pending_int_mask = 0;
- qemu_mod_timer(s->frame_timer, t_now + frame_t);
+ timer_mod(s->frame_timer, t_now + frame_t);
}
static const MemoryRegionOps uhci_ioport_ops = {
/* TODO: reset value should be 0. */
pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
- s->irq_pin = u->info.irq_pin;
- pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1);
+ pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1);
if (s->masterbus) {
USBPort *ports[NB_PORTS];
return -1;
}
} else {
- usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
+ usb_bus_new(&s->bus, sizeof(s->bus), &uhci_bus_ops, DEVICE(dev));
for (i = 0; i < NB_PORTS; i++) {
usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
}
}
s->bh = qemu_bh_new(uhci_bh, s);
- s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
+ s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s);
s->num_ports_vmstate = NB_PORTS;
QTAILQ_INIT(&s->queues);
qemu_register_reset(uhci_reset, s);
- memory_region_init_io(&s->io_bar, &uhci_ioport_ops, s, "uhci", 0x20);
+ memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s,
+ "uhci", 0x20);
+
/* Use region 4 for consistency with real hardware. BSD guests seem
to rely on this. */
pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
k->device_id = info->device_id;
k->revision = info->revision;
k->class_id = PCI_CLASS_SERIAL_USB;
- k->no_hotplug = 1;
+ dc->hotpluggable = false;
dc->vmsd = &vmstate_uhci;
dc->props = uhci_properties;
+ set_bit(DEVICE_CATEGORY_USB, dc->categories);
u->info = *info;
}