From patchwork Mon Mar 7 22:32:37 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Walle X-Patchwork-Id: 85826 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by ozlabs.org (Postfix) with ESMTP id 99FB4B70FF for ; Tue, 8 Mar 2011 09:40:25 +1100 (EST) Received: from localhost ([127.0.0.1]:47945 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pwj0C-0006s8-MC for incoming@patchwork.ozlabs.org; Mon, 07 Mar 2011 17:34:08 -0500 Received: from [140.186.70.92] (port=55122 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PwizA-0006qs-Kb for qemu-devel@nongnu.org; Mon, 07 Mar 2011 17:33:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pwiz8-0003pv-J0 for qemu-devel@nongnu.org; Mon, 07 Mar 2011 17:33:04 -0500 Received: from mail.serverraum.org ([78.47.150.89]:50919) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pwiz8-0003p3-4h for qemu-devel@nongnu.org; Mon, 07 Mar 2011 17:33:02 -0500 Received: from localhost (localhost [127.0.0.1]) by mail.serverraum.org (Postfix) with ESMTP id EECB83EF0E; Mon, 7 Mar 2011 23:33:01 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mail.serverraum.org Received: from mail.serverraum.org ([127.0.0.1]) by localhost (web.serverraum.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2nThzf2QvHkx; Mon, 7 Mar 2011 23:33:01 +0100 (CET) Received: from thanatos.fritz.box (91-67-5-72-dynip.superkabel.de [91.67.5.72]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.serverraum.org (Postfix) with ESMTPSA id 945AA3EEFA; Mon, 7 Mar 2011 23:33:01 +0100 (CET) From: Michael Walle To: qemu-devel@nongnu.org Date: Mon, 7 Mar 2011 23:32:37 +0100 Message-Id: <1299537165-16711-7-git-send-email-michael@walle.cc> X-Mailer: git-send-email 1.7.2.3 In-Reply-To: <1299537165-16711-1-git-send-email-michael@walle.cc> References: <1299537165-16711-1-git-send-email-michael@walle.cc> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) X-Received-From: 78.47.150.89 Cc: "Edgar E. Iglesias" , Michael Walle , Alexander Graf Subject: [Qemu-devel] [PATCH 06/14] lm32: add Milkymist SoftUSB support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch adds support for Milkymist's SoftUSB core. This model differ from the real hardware in its functionality. The real hardware consits of a tiny freely programmable microcontroller which controls the USB ports. For simplicity reasons, this model emulates only keyboard and mouse input devices, eg. input events translates directly to the corresponding expected messages. Signed-off-by: Michael Walle --- Makefile.target | 1 + hw/milkymist-softusb.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++++ trace-events | 8 + 3 files changed, 366 insertions(+), 0 deletions(-) create mode 100644 hw/milkymist-softusb.c diff --git a/Makefile.target b/Makefile.target index 13f9e72..280a91d 100644 --- a/Makefile.target +++ b/Makefile.target @@ -261,6 +261,7 @@ obj-lm32-y += milkymist-hpdmc.o obj-lm32-y += milkymist-memcard.o obj-lm32-y += milkymist-minimac.o obj-lm32-y += milkymist-pfpu.o +obj-lm32-y += milkymist-softusb.o obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o obj-mips-y += mips_addr.o mips_timer.o mips_int.o diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c new file mode 100644 index 0000000..1565260 --- /dev/null +++ b/hw/milkymist-softusb.c @@ -0,0 +1,357 @@ +/* + * QEMU model of the Milkymist SoftUSB block. + * + * Copyright (c) 2010 Michael Walle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * Specification available at: + * not available yet + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "console.h" +#include "usb.h" +#include "qemu-error.h" + +enum { + R_CTRL = 0, + R_MAX +}; + +enum { + CTRL_RESET = (1<<0), +}; + +#define COMLOC_DEBUG_PRODUCE 0x1000 +#define COMLOC_DEBUG_BASE 0x1001 +#define COMLOC_MEVT_PRODUCE 0x1101 +#define COMLOC_MEVT_BASE 0x1102 +#define COMLOC_KEVT_PRODUCE 0x1142 +#define COMLOC_KEVT_BASE 0x1143 + +struct MilkymistSoftUsbState { + SysBusDevice busdev; + USBBus usbbus; + USBPort usbport[2]; + USBDevice *usbdev; + + qemu_irq irq; + + /* device properties */ + uint32_t pmem_base; + uint32_t pmem_size; + uint32_t dmem_base; + uint32_t dmem_size; + + /* device registers */ + uint32_t regs[R_MAX]; + + /* mouse state */ + int mouse_dx; + int mouse_dy; + int mouse_dz; + uint8_t mouse_buttons_state; + + /* keyboard state */ + uint8_t kbd_usb_buffer[8]; +}; +typedef struct MilkymistSoftUsbState MilkymistSoftUsbState; + +static uint32_t softusb_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistSoftUsbState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTRL: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_softusb: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_softusb_memory_read(addr << 2, r); + + return r; +} + +static void +softusb_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistSoftUsbState *s = opaque; + + trace_milkymist_softusb_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTRL: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_softusb: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const softusb_read_fn[] = { + NULL, + NULL, + &softusb_read, +}; + +static CPUWriteMemoryFunc * const softusb_write_fn[] = { + NULL, + NULL, + &softusb_write, +}; + +static inline void softusb_read_dmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->dmem_size) { + error_report("milkymist_softusb: read dmem out of bounds " + "at offset 0x%x, len %d\n", offset, len); + return; + } + + cpu_physical_memory_read(s->dmem_base + offset, buf, len); +} + +static inline void softusb_write_dmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->dmem_size) { + error_report("milkymist_softusb: write dmem out of bounds " + "at offset 0x%x, len %d\n", offset, len); + return; + } + + cpu_physical_memory_write(s->dmem_base + offset, buf, len); +} + +static inline void softusb_read_pmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->pmem_size) { + error_report("milkymist_softusb: read pmem out of bounds " + "at offset 0x%x, len %d\n", offset, len); + return; + } + + cpu_physical_memory_read(s->pmem_base + offset, buf, len); +} + +static inline void softusb_write_pmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->pmem_size) { + error_report("milkymist_softusb: write pmem out of bounds " + "at offset 0x%x, len %d\n", offset, len); + return; + } + + cpu_physical_memory_write(s->pmem_base + offset, buf, len); +} + +static void softusb_mouse_changed(MilkymistSoftUsbState *s) +{ + uint8_t m; + uint8_t buf[4]; + + buf[0] = s->mouse_buttons_state; + buf[1] = s->mouse_dx; + buf[2] = s->mouse_dy; + buf[3] = s->mouse_dz; + + softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); + trace_milkymist_softusb_mevt(m); + softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, buf, 4); + m = (m + 1) & 0xf; + softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); + + trace_milkymist_softusb_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static void softusb_kbd_changed(MilkymistSoftUsbState *s) +{ + uint8_t m; + + softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); + trace_milkymist_softusb_kevt(m); + softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_usb_buffer, 8); + m = (m + 1) & 0x7; + softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); + + trace_milkymist_softusb_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static void softusb_mouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + MilkymistSoftUsbState *s = opaque; + + /* if device is in reset, do nothing */ + if (s->regs[R_CTRL] & CTRL_RESET) { + return; + } + + trace_milkymist_softusb_mouse_event(dx, dy, dz, buttons_state); + + s->mouse_dx = dx; + s->mouse_dy = dy; + s->mouse_dz = dz; + s->mouse_buttons_state = buttons_state; + + softusb_mouse_changed(s); +} + +static void softusb_usbdev_datain(void *opaque) +{ + MilkymistSoftUsbState *s = opaque; + + USBPacket p; + + p.pid = USB_TOKEN_IN; + p.devep = 1; + p.data = s->kbd_usb_buffer; + p.len = sizeof(s->kbd_usb_buffer); + s->usbdev->info->handle_data(s->usbdev, &p); + + softusb_kbd_changed(s); +} + +static void softusb_attach(USBPort *port) +{ +} + +static USBPortOps softusb_ops = { + .attach = softusb_attach, +}; + +static void milkymist_softusb_reset(DeviceState *d) +{ + MilkymistSoftUsbState *s = + container_of(d, MilkymistSoftUsbState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + s->mouse_dx = 0; + s->mouse_dy = 0; + s->mouse_dz = 0; + s->mouse_buttons_state = 0; + memset(s->kbd_usb_buffer, 0, sizeof(s->kbd_usb_buffer)); + + /* defaults */ + s->regs[R_CTRL] = CTRL_RESET; +} + +static int milkymist_softusb_init(SysBusDevice *dev) +{ + MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev); + int softusb_regs; + ram_addr_t pmem_ram; + ram_addr_t dmem_ram; + + sysbus_init_irq(dev, &s->irq); + + softusb_regs = cpu_register_io_memory(softusb_read_fn, softusb_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, softusb_regs); + + /* register pmem and dmem */ + pmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.pmem", s->pmem_size); + cpu_register_physical_memory(s->pmem_base, s->pmem_size, + pmem_ram | IO_MEM_RAM); + dmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.dmem", s->dmem_size); + cpu_register_physical_memory(s->dmem_base, s->dmem_size, + dmem_ram | IO_MEM_RAM); + + qemu_add_mouse_event_handler(softusb_mouse_event, s, 0, "Milkymist Mouse"); + + /* create our usb bus */ + usb_bus_new(&s->usbbus, NULL); + + /* our two ports */ + usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops, + USB_SPEED_MASK_LOW); + usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops, + USB_SPEED_MASK_LOW); + + /* and finally create an usb keyboard */ + s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd"); + usb_hid_datain_cb(s->usbdev, s, softusb_usbdev_datain); + s->usbdev->info->handle_reset(s->usbdev); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_softusb = { + .name = "milkymist-softusb", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX), + VMSTATE_INT32(mouse_dx, MilkymistSoftUsbState), + VMSTATE_INT32(mouse_dy, MilkymistSoftUsbState), + VMSTATE_INT32(mouse_dz, MilkymistSoftUsbState), + VMSTATE_UINT8(mouse_buttons_state, MilkymistSoftUsbState), + VMSTATE_BUFFER(kbd_usb_buffer, MilkymistSoftUsbState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_softusb_info = { + .init = milkymist_softusb_init, + .qdev.name = "milkymist-softusb", + .qdev.size = sizeof(MilkymistSoftUsbState), + .qdev.vmsd = &vmstate_milkymist_softusb, + .qdev.reset = milkymist_softusb_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32( + "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000 + ), + DEFINE_PROP_UINT32( + "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000 + ), + DEFINE_PROP_UINT32( + "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000 + ), + DEFINE_PROP_UINT32( + "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000 + ), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void milkymist_softusb_register(void) +{ + sysbus_register_withprop(&milkymist_softusb_info); +} + +device_init(milkymist_softusb_register) diff --git a/trace-events b/trace-events index a410e93..9766f98 100644 --- a/trace-events +++ b/trace-events @@ -321,3 +321,11 @@ disable milkymist_pfpu_memory_read(uint32_t addr, uint32_t value) "addr %08x val disable milkymist_pfpu_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" disable milkymist_pfpu_vectout(uint32_t a, uint32_t b, uint32_t dma_ptr) "a %08x b %08x dma_ptr %08x" disable milkymist_pfpu_pulse_irq(void) "Pulse IRQ" + +# hw/milkymist-softusb.c +disable milkymist_softusb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_softusb_mevt(uint8_t m) "m %d" +disable milkymist_softusb_kevt(uint8_t m) "m %d" +disable milkymist_softusb_mouse_event(int dx, int dy, int dz, int bs) "dx %d dy %d dz %d bs %02x" +disable milkymist_softusb_pulse_irq(void) "Pulse IRQ"