Implemented S8000 USB stack
This commit is contained in:
1 parent
a0698a741e
commit
410b1ae746
11 files changed
+1699
-52
No files matched your search
+10
-9
@@ -174,13 +174,6 @@ static inline qemu_irq s5l8900_get_irq(IPodTouchMachineState *s, int n)
|
||||
return s->irq[n / S5L8720_VIC_SIZE][n % S5L8720_VIC_SIZE];
|
||||
}
|
||||
|
||||
static uint32_t s5l8720_usb_hwcfg[] = {
|
||||
0,
|
||||
0x7a8f60d0,
|
||||
0x082000e8,
|
||||
0x01f08024
|
||||
};
|
||||
|
||||
static void ipod_touch_key_event(void *opaque, int keycode)
|
||||
{
|
||||
bool do_irq = false;
|
||||
@@ -405,14 +398,22 @@ static void ipod_touch_machine_init(MachineState *machine)
|
||||
// init USB OTG
|
||||
dev = qdev_new("dwc2-usb");
|
||||
DWC2State *dwc2 = DWC2_USB(dev);
|
||||
nms->usb_otg = dwc2;
|
||||
nms->dwc2 = dwc2;
|
||||
memory_region_add_subregion(sysmem, USBOTG_MEM_BASE, &dwc2->container);
|
||||
// TODO add IRQ etc
|
||||
memory_region_init(&nms->usb_dma_container_mr, OBJECT(dev), "appleusb.dma-container-mr", UINT32_MAX);
|
||||
object_property_add_const_link(OBJECT(nms->usb_otg), "dma-mr", OBJECT(&nms->usb_dma_container_mr));
|
||||
object_property_add_const_link(OBJECT(nms->dwc2), "dma-mr", OBJECT(&nms->usb_dma_container_mr));
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
// create the TCP host and connect the busses
|
||||
dev = qdev_new(TYPE_USB_TCP_HOST);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
BusState *bus = QLIST_FIRST(&DEVICE(busdev)->child_bus);
|
||||
qdev_realize(DEVICE(nms->dwc2->device), bus, &error_fatal);
|
||||
|
||||
// init two pl080 DMAC0 devices
|
||||
dev = qdev_new("pl080");
|
||||
PL080State *pl080_1 = PL080(dev);
|
||||
|
||||
@@ -137,3 +137,8 @@ config USB_DWC3
|
||||
config XLNX_USB_SUBSYS
|
||||
bool
|
||||
select USB_DWC3
|
||||
|
||||
config USB_TCP
|
||||
bool
|
||||
default y
|
||||
depends on USB
|
||||
+1053
-36
File diff suppressed because it is too large.
Load diff
+65
-5
@@ -28,10 +28,12 @@
|
||||
|
||||
#define DWC2_MMIO_SIZE 0x11000
|
||||
|
||||
#define DWC2_NB_CHAN 8 /* Number of host channels */
|
||||
#define DWC2_MAX_XFER_SIZE 65536 /* Max transfer size expected in HCTSIZ */
|
||||
#define DWC2_NB_CHAN 16 /* Number of host channels */
|
||||
#define DWC2_MAX_XFER_SIZE 0x1000 /* Max transfer size expected in HCTSIZ */
|
||||
#define DWC2_NB_EP 16 /* Number of device endpoints */
|
||||
|
||||
typedef struct DWC2Packet DWC2Packet;
|
||||
typedef struct DWC2DeviceState DWC2DeviceState;
|
||||
typedef struct DWC2State DWC2State;
|
||||
typedef struct DWC2Class DWC2Class;
|
||||
|
||||
@@ -57,12 +59,19 @@ struct DWC2Packet {
|
||||
bool needs_service;
|
||||
};
|
||||
|
||||
struct DWC2DeviceState {
|
||||
USBDevice parent_obj;
|
||||
DWC2State *dwc2;
|
||||
};
|
||||
|
||||
struct DWC2State {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
USBBus bus;
|
||||
/* This device in device mode */
|
||||
DWC2DeviceState *device;
|
||||
qemu_irq irq;
|
||||
MemoryRegion *dma_mr;
|
||||
AddressSpace dma_as;
|
||||
@@ -112,6 +121,13 @@ struct DWC2State {
|
||||
uint32_t hptxfsiz; /* 100 */
|
||||
};
|
||||
};
|
||||
union {
|
||||
#define DWC2_DFSZREG_SIZE 0x3c
|
||||
uint32_t dfszreg[DWC2_DFSZREG_SIZE / sizeof(uint32_t)];
|
||||
struct {
|
||||
uint32_t dptxfsiz[DWC2_NB_EP]; /* 104 */
|
||||
};
|
||||
};
|
||||
|
||||
union {
|
||||
#define DWC2_HREG0_SIZE 0x44
|
||||
@@ -141,6 +157,44 @@ struct DWC2State {
|
||||
#define hcdma(_ch) hreg1[((_ch) << 3) + 5] /* 514, 534, ... */
|
||||
#define hcdmab(_ch) hreg1[((_ch) << 3) + 7] /* 51c, 53c, ... */
|
||||
|
||||
union {
|
||||
#define DWC2_DREG_SIZE 0x38
|
||||
uint32_t dreg[DWC2_DREG_SIZE / sizeof(uint32_t)];
|
||||
struct {
|
||||
uint32_t dcfg; /* 800 */
|
||||
uint32_t dctl; /* 804 */
|
||||
uint32_t dsts; /* 808 */
|
||||
uint32_t rsvd2; /* 80c */
|
||||
uint32_t diepmsk; /* 810 */
|
||||
uint32_t doepmsk; /* 814 */
|
||||
uint32_t daint; /* 818 */
|
||||
uint32_t daintmsk; /* 81c */
|
||||
uint32_t dtknqr1; /* 820 */
|
||||
uint32_t dtknqr2; /* 824 */
|
||||
uint32_t dvbusdis; /* 828 */
|
||||
uint32_t dvbuspulse; /* 82c */
|
||||
uint32_t dtknqr3; /* 830 */
|
||||
uint32_t dtknqr4; /* 834 */
|
||||
};
|
||||
};
|
||||
|
||||
#define DWC2_DIEPREG_SIZE (0x20 * DWC2_NB_EP)
|
||||
uint32_t diepreg[DWC2_DIEPREG_SIZE / sizeof(uint32_t)];
|
||||
|
||||
#define diepctl(_ch) diepreg[((_ch) << 3) + 0] /* 900, 920, ... */
|
||||
#define diepint(_ch) diepreg[((_ch) << 3) + 2] /* 908, 928, ... */
|
||||
#define dieptsiz(_ch) diepreg[((_ch) << 3) + 4] /* 910, 930, ... */
|
||||
#define diepdma(_ch) diepreg[((_ch) << 3) + 5] /* 914, 934, ... */
|
||||
#define dtxfsts(_ch) diepreg[((_ch) << 3) + 6] /* 918, 938, ... */
|
||||
|
||||
#define DWC2_DOEPREG_SIZE (0x20 * DWC2_NB_EP)
|
||||
uint32_t doepreg[DWC2_DIEPREG_SIZE / sizeof(uint32_t)];
|
||||
|
||||
#define doepctl(_ch) doepreg[((_ch) << 3) + 0] /* b00, b20, ... */
|
||||
#define doepint(_ch) doepreg[((_ch) << 3) + 2] /* b08, b28, ... */
|
||||
#define doeptsiz(_ch) doepreg[((_ch) << 3) + 4] /* b10, b30, ... */
|
||||
#define doepdma(_ch) doepreg[((_ch) << 3) + 5] /* b14, b34, ... */
|
||||
|
||||
union {
|
||||
#define DWC2_PCGREG_SIZE 0x08
|
||||
uint32_t pcgreg[DWC2_PCGREG_SIZE / sizeof(uint32_t)];
|
||||
@@ -152,13 +206,13 @@ struct DWC2State {
|
||||
|
||||
/* TODO - implement FIFO registers for slave mode */
|
||||
#define DWC2_HFIFO_SIZE (0x1000 * DWC2_NB_CHAN)
|
||||
|
||||
/*
|
||||
* Internal state
|
||||
*/
|
||||
QEMUTimer *eof_timer;
|
||||
QEMUTimer *frame_timer;
|
||||
QEMUBH *async_bh;
|
||||
QEMUBH *device_async_bh;
|
||||
int64_t sof_time;
|
||||
int64_t usb_frame_time;
|
||||
int64_t usb_bit_time;
|
||||
@@ -169,7 +223,10 @@ struct DWC2State {
|
||||
bool working;
|
||||
USBPort uport;
|
||||
DWC2Packet packet[DWC2_NB_CHAN]; /* one packet per chan */
|
||||
uint8_t usb_buf[DWC2_NB_CHAN][DWC2_MAX_XFER_SIZE]; /* one buffer per chan */
|
||||
union {
|
||||
uint8_t usb_buf[DWC2_NB_CHAN][DWC2_MAX_XFER_SIZE]; /* one buffer per chan */
|
||||
uint8_t fifos_buf[DWC2_HFIFO_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
struct DWC2Class {
|
||||
@@ -180,7 +237,10 @@ struct DWC2Class {
|
||||
/*< public >*/
|
||||
};
|
||||
|
||||
#define TYPE_DWC2_USB_DEVICE "dwc2-usb-device"
|
||||
OBJECT_DECLARE_TYPE(DWC2DeviceState, USBDeviceClass, DWC2_USB_DEVICE)
|
||||
|
||||
#define TYPE_DWC2_USB "dwc2-usb"
|
||||
OBJECT_DECLARE_TYPE(DWC2State, DWC2Class, DWC2_USB)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,458 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qom/object.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "hw/usb.h"
|
||||
#include "tcp-usb.h"
|
||||
#include "hw/usb/hcd-tcp.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "io/channel.h"
|
||||
#include "io/channel-util.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/blocker.h"
|
||||
|
||||
//#define DEBUG_HCD_TCP
|
||||
|
||||
#ifdef DEBUG_HCD_TCP
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stderr, "hcd-tcp: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
static void usb_tcp_host_closed(USBTCPHostState *s)
|
||||
{
|
||||
DPRINTF("%s\n", __func__);
|
||||
if (s->ioc) {
|
||||
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
qio_channel_close(s->ioc, NULL);
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = NULL;
|
||||
}
|
||||
s->closed = true;
|
||||
migrate_del_blocker(s->migration_blocker);
|
||||
}
|
||||
|
||||
static ssize_t tcp_usb_read(QIOChannel *ioc, void *buf, size_t len)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = len };
|
||||
bool iolock = qemu_mutex_iothread_locked();
|
||||
bool iothread = qemu_in_iothread();
|
||||
ssize_t ret = -1;
|
||||
Error *err = NULL;
|
||||
|
||||
/*
|
||||
* Dont use in IOThread out of co-routine context as
|
||||
* it will block IOThread.
|
||||
*/
|
||||
assert(qemu_in_coroutine() || !iothread);
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, NULL, 0, &err);
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
return (ret <= 0) ? ret : iov.iov_len;
|
||||
}
|
||||
|
||||
static bool tcp_usb_write(QIOChannel *ioc, void *buf, ssize_t len)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = len };
|
||||
bool iolock = qemu_mutex_iothread_locked();
|
||||
bool iothread = qemu_in_iothread();
|
||||
bool ret = false;
|
||||
Error *err = NULL;
|
||||
|
||||
/*
|
||||
* Dont use in IOThread out of co-routine context as
|
||||
* it will block IOThread.
|
||||
*/
|
||||
assert(qemu_in_coroutine() || !iothread);
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
if (!qio_channel_writev_full_all(ioc, &iov, 1, NULL, 0, 0, &err)) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (iolock && !iothread && !qemu_in_coroutine()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static USBPort *usb_tcp_host_find_active_port(USBTCPHostState *s)
|
||||
{
|
||||
for (int i = 0; i < G_N_ELEMENTS(s->uports) - 1; i++) {
|
||||
if (s->uports[i].dev->attached) {
|
||||
return &s->uports[i];
|
||||
}
|
||||
}
|
||||
return &s->uports[0];
|
||||
}
|
||||
|
||||
static void coroutine_fn usb_tcp_host_respond_packet_co(void *opaque)
|
||||
{
|
||||
USBTCPPacket *pkt = (USBTCPPacket *)opaque;
|
||||
USBTCPHostState *s = USB_TCP_HOST(pkt->s);
|
||||
USBPacket *p = &pkt->p;
|
||||
tcp_usb_header_t hdr = { 0 };
|
||||
tcp_usb_response_header resp = { 0 };
|
||||
g_autofree void *buffer = NULL;
|
||||
USBPort *uport = usb_tcp_host_find_active_port(s);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->write_mutex) {
|
||||
if (!s->closed) {
|
||||
hdr.type = TCP_USB_RESPONSE;
|
||||
resp.addr = uport->dev->addr;
|
||||
resp.pid = p->pid;
|
||||
resp.ep = p->ep->nr;
|
||||
resp.id = p->id;
|
||||
resp.status = p->status;
|
||||
resp.length = p->iov.size;
|
||||
|
||||
if (resp.length > p->actual_length) {
|
||||
resp.length = p->actual_length;
|
||||
}
|
||||
|
||||
if (p->pid == USB_TOKEN_IN && p->status != USB_RET_ASYNC) {
|
||||
buffer = g_malloc(resp.length);
|
||||
iov_to_buf(p->iov.iov, p->iov.niov, 0, buffer, resp.length);
|
||||
}
|
||||
|
||||
if (!tcp_usb_write(s->ioc, &hdr, sizeof(hdr))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tcp_usb_write(s->ioc, &resp, sizeof(resp))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
if (!tcp_usb_write(s->ioc, buffer, resp.length)) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!usb_packet_is_inflight(p)) {
|
||||
if (pkt->buffer) {
|
||||
g_free(pkt->buffer);
|
||||
}
|
||||
usb_packet_cleanup(p);
|
||||
g_free(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_tcp_host_respond_packet(USBTCPHostState *s, USBTCPPacket *pkt)
|
||||
{
|
||||
Coroutine *co = NULL;
|
||||
co = qemu_coroutine_create(usb_tcp_host_respond_packet_co, pkt);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
static void coroutine_fn usb_tcp_host_msg_loop_co(void *opaque)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(opaque);
|
||||
USBPort *uport = usb_tcp_host_find_active_port(s);
|
||||
QIOChannel *ioc = s->ioc;
|
||||
|
||||
for(;;) {
|
||||
tcp_usb_header_t hdr = { 0 };
|
||||
|
||||
if (unlikely((tcp_usb_read(ioc, &hdr, sizeof(hdr)) != sizeof(hdr)))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hdr.type) {
|
||||
case TCP_USB_REQUEST: {
|
||||
/* fprintf(stderr, "%s: TCP_USB_REQUEST\n", __func__); */
|
||||
tcp_usb_request_header pkt_hdr = {0};
|
||||
g_autofree void *buffer = NULL;
|
||||
g_autofree USBTCPPacket *pkt = (USBTCPPacket *) g_malloc0(sizeof(USBTCPPacket));
|
||||
USBEndpoint *ep = NULL;
|
||||
|
||||
if (unlikely(tcp_usb_read(ioc, &pkt_hdr, sizeof(pkt_hdr)) != sizeof(pkt_hdr))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
DPRINTF("%s: TCP_USB_REQUEST pid: 0x%x ep: %d id: 0x%lx\n", __func__, pkt_hdr.pid, pkt_hdr.ep, pkt_hdr.id);
|
||||
#endif
|
||||
ep = usb_ep_get(uport->dev, pkt_hdr.pid, pkt_hdr.ep);
|
||||
if (ep == NULL) {
|
||||
DPRINTF("%s: TCP_USB_REQUEST unknown EP\n", __func__);
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_packet_init(&pkt->p);
|
||||
usb_packet_setup(&pkt->p, pkt_hdr.pid, ep, pkt_hdr.stream, pkt_hdr.id, pkt_hdr.short_not_ok, pkt_hdr.int_req);
|
||||
|
||||
if (pkt_hdr.length > 0) {
|
||||
buffer = g_malloc0(pkt_hdr.length);
|
||||
|
||||
if (pkt_hdr.pid != USB_TOKEN_IN) {
|
||||
if (unlikely(tcp_usb_read(s->ioc, buffer, pkt_hdr.length) != pkt_hdr.length)) {
|
||||
usb_tcp_host_closed(s);
|
||||
usb_packet_cleanup(&pkt->p);
|
||||
return;
|
||||
}
|
||||
/* qemu_hexdump(stderr, __func__, buffer, pkt_hdr.length); */
|
||||
}
|
||||
|
||||
usb_packet_addbuf(&pkt->p, buffer, pkt_hdr.length);
|
||||
pkt->buffer = buffer;
|
||||
g_steal_pointer(&buffer);
|
||||
}
|
||||
|
||||
if (pkt_hdr.addr != uport->dev->addr) {
|
||||
/*
|
||||
* fprintf(stderr,
|
||||
* "%s: USB_RET_NODEV: pkt_hdr.addr != uport->dev->addr: %d != %d\n",
|
||||
* __func__, pkt_hdr.addr, uport->dev->addr);
|
||||
*/
|
||||
/* Can't enforce this check because dwc2 address transition time is slow */
|
||||
}
|
||||
pkt->dev = ep->dev;
|
||||
pkt->s = s;
|
||||
pkt->addr = pkt_hdr.addr;
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
usb_handle_packet(pkt->dev, &pkt->p);
|
||||
usb_tcp_host_respond_packet(s, pkt);
|
||||
g_steal_pointer(&pkt);
|
||||
break;
|
||||
}
|
||||
case TCP_USB_RESPONSE:
|
||||
fprintf(stderr, "%s: unexpected TCP_USB_RESPONSE\n", __func__);
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
case TCP_USB_CANCEL: {
|
||||
/* DPRINTF("%s: TCP_USB_CANCEL\n", __func__); */
|
||||
tcp_usb_cancel_header pkt_hdr = {0};
|
||||
USBTCPPacket *pkt = NULL;
|
||||
USBPacket *p = NULL;
|
||||
|
||||
if (unlikely(tcp_usb_read(ioc, &pkt_hdr, sizeof(pkt_hdr)) != sizeof(pkt_hdr))) {
|
||||
usb_tcp_host_closed(s);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1
|
||||
DPRINTF("%s: TCP_USB_CANCEL pid: 0x%x ep: %d\n", __func__, pkt_hdr.pid, pkt_hdr.ep);
|
||||
#endif
|
||||
|
||||
if (pkt_hdr.addr != uport->dev->addr) {
|
||||
/*
|
||||
* fprintf(stderr,
|
||||
* "%s: USB_RET_NODEV: pkt_hdr.addr != uport->dev->addr: %d != %d\n",
|
||||
* __func__, pkt_hdr.addr, uport->dev->addr);
|
||||
*/
|
||||
/* Can't enforce this check because dwc2 address transition time is slow */
|
||||
}
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
p = usb_ep_find_packet_by_id(uport->dev, pkt_hdr.pid,
|
||||
pkt_hdr.ep, pkt_hdr.id);
|
||||
if (p) {
|
||||
pkt = container_of(p, USBTCPPacket, p);
|
||||
usb_cancel_packet(&pkt->p);
|
||||
DPRINTF("%s: TCP_USB_CANCEL: packet"
|
||||
" pid: 0x%x ep: %d id: 0x%lx len: 0x%x\n",
|
||||
__func__, pkt_hdr.pid, pkt_hdr.ep, pkt_hdr.id, p->actual_length);
|
||||
usb_tcp_host_respond_packet(s, pkt);
|
||||
} else {
|
||||
warn_report("%s: TCP_USB_CANCEL: packet"
|
||||
" pid: 0x%x ep: %d id: 0x%lx not found",
|
||||
__func__, pkt_hdr.pid, pkt_hdr.ep, pkt_hdr.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TCP_USB_RESET:
|
||||
/* fprintf(stderr, "%s: TCP_USB_RESET\n", __func__); */
|
||||
DPRINTF("%s: TCP_USB_RESET\n", __func__);
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
usb_device_reset(uport->dev);
|
||||
break;;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void usb_tcp_host_attach(USBPort *uport)
|
||||
{
|
||||
struct sockaddr_un server_addr;
|
||||
USBTCPHostState *s = USB_TCP_HOST(uport->opaque);
|
||||
int sock = -1;
|
||||
Coroutine *co = NULL;
|
||||
QIOChannel *ioc = NULL;
|
||||
int ret;
|
||||
Error *err = NULL;
|
||||
printf("??000\n");
|
||||
|
||||
if (uport->index >= G_N_ELEMENTS(s->uports) - 1) {
|
||||
error_report("%s: attached to unused port\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usb_tcp_host_find_active_port(s) != uport) {
|
||||
error_report("%s: Attaching to 2 proxy ports at the same time. "
|
||||
"This port might not be able to receive packets.\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uport->dev || !uport->dev->attached) {
|
||||
return;
|
||||
}
|
||||
|
||||
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (socket < 0) {
|
||||
error_report("%s: cannot open socket", __func__);
|
||||
}
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sun_family = AF_UNIX;
|
||||
strncpy(server_addr.sun_path, socket_path, sizeof(server_addr.sun_path));
|
||||
server_addr.sun_path[sizeof(server_addr.sun_path) - 1] = '\0';
|
||||
|
||||
ret = connect(sock, (const struct sockaddr *) &server_addr, sizeof(server_addr));
|
||||
if (ret < 0) {
|
||||
error_report("%s: failed to connect to server: %d\n", __func__, ret);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
ioc = qio_channel_new_fd(sock, &err);
|
||||
if (!ioc) {
|
||||
error_report_err(err);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
qio_channel_set_blocking(ioc, false, NULL);
|
||||
s->closed = 0;
|
||||
s->ioc = ioc;
|
||||
|
||||
migrate_add_blocker(s->migration_blocker, NULL);
|
||||
co = qemu_coroutine_create(usb_tcp_host_msg_loop_co, s);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
static void usb_tcp_host_detach(USBPort *uport)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(uport->opaque);
|
||||
|
||||
usb_tcp_host_closed(s);
|
||||
}
|
||||
|
||||
static void usb_tcp_host_async_packet_complete(USBPort *port, USBPacket *p)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(port->opaque);
|
||||
usb_tcp_host_respond_packet(s, container_of(p, USBTCPPacket, p));
|
||||
}
|
||||
|
||||
static USBBusOps usb_tcp_bus_ops = { };
|
||||
|
||||
static USBPortOps usb_tcp_host_port_ops = {
|
||||
.attach = usb_tcp_host_attach,
|
||||
.detach = usb_tcp_host_detach,
|
||||
.child_detach = NULL,
|
||||
.wakeup = NULL,
|
||||
.complete = usb_tcp_host_async_packet_complete,
|
||||
};
|
||||
|
||||
static void usb_tcp_host_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(dev);
|
||||
|
||||
usb_bus_new(&s->bus, sizeof(s->bus), &usb_tcp_bus_ops, dev);
|
||||
for (int i = 0; i < G_N_ELEMENTS(s->uports); i++) {
|
||||
usb_register_port(&s->bus, &s->uports[i], s, i, &usb_tcp_host_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL |
|
||||
USB_SPEED_MASK_HIGH);
|
||||
}
|
||||
|
||||
s->closed = 1;
|
||||
qemu_co_mutex_init(&s->write_mutex);
|
||||
}
|
||||
|
||||
static void usb_tcp_host_unrealize(DeviceState *dev)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(dev);
|
||||
|
||||
if (s->ioc) {
|
||||
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
qio_channel_close(s->ioc, NULL);
|
||||
s->ioc = NULL;
|
||||
}
|
||||
|
||||
s->closed = 1;
|
||||
s->stopped = 1;
|
||||
}
|
||||
|
||||
static void usb_tcp_host_init(Object *obj)
|
||||
{
|
||||
USBTCPHostState *s = USB_TCP_HOST(obj);
|
||||
s->closed = 1;
|
||||
error_setg(&s->migration_blocker, "%s does not support migration "
|
||||
"while connected", TYPE_USB_TCP_HOST);
|
||||
}
|
||||
|
||||
static Property usb_tcp_host_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void usb_tcp_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = usb_tcp_host_realize;
|
||||
dc->unrealize = usb_tcp_host_unrealize;
|
||||
dc->desc = "QEMU USB Passthrough Host Controller";
|
||||
device_class_set_props(dc, usb_tcp_host_properties);
|
||||
set_bit(DEVICE_CATEGORY_USB, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo usb_tcp_host_type_info = {
|
||||
.name = TYPE_USB_TCP_HOST,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(USBTCPHostState),
|
||||
.class_init = usb_tcp_host_class_init,
|
||||
.instance_init = usb_tcp_host_init,
|
||||
};
|
||||
|
||||
static void usb_tcp_host_register_types(void)
|
||||
{
|
||||
type_register_static(&usb_tcp_host_type_info);
|
||||
}
|
||||
|
||||
type_init(usb_tcp_host_register_types)
|
||||
@@ -11,6 +11,8 @@ system_ss.add(when: 'CONFIG_USB', if_true: files(
|
||||
'pcap.c',
|
||||
))
|
||||
|
||||
system_ss.add(when: 'CONFIG_USB_TCP', if_true: files('hcd-tcp.c'))
|
||||
|
||||
# usb host adapters
|
||||
system_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c'))
|
||||
system_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c'))
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef HW_USB_TCP_USB_H
|
||||
#define HW_USB_TCP_USB_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/usb.h"
|
||||
|
||||
static const char *socket_path = "/tmp/usbqemu";
|
||||
|
||||
enum {
|
||||
TCP_USB_REQUEST = (1 << 0),
|
||||
TCP_USB_RESPONSE = (1 << 1),
|
||||
TCP_USB_RESET = (1 << 2),
|
||||
TCP_USB_CANCEL = (1 << 3)
|
||||
};
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_header {
|
||||
uint8_t type;
|
||||
} tcp_usb_header_t;
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_request_header {
|
||||
uint8_t addr;
|
||||
int pid;
|
||||
uint8_t ep;
|
||||
unsigned int stream;
|
||||
uint64_t id;
|
||||
uint8_t short_not_ok;
|
||||
uint8_t int_req;
|
||||
uint16_t length;
|
||||
} tcp_usb_request_header;
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_response_header {
|
||||
uint8_t addr;
|
||||
int pid;
|
||||
uint8_t ep;
|
||||
uint64_t id;
|
||||
uint32_t status;
|
||||
uint16_t length;
|
||||
} tcp_usb_response_header;
|
||||
|
||||
typedef struct QEMU_PACKED tcp_usb_cancel_header {
|
||||
uint8_t addr;
|
||||
int pid;
|
||||
uint8_t ep;
|
||||
uint64_t id;
|
||||
|
||||
} tcp_usb_cancel_header;
|
||||
|
||||
#endif //HW_USB_TCP_USB_H
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "hw/dma/pl080.h"
|
||||
#include "hw/i2c/ipod_touch_i2c.h"
|
||||
#include "hw/usb/hcd-dwc2.h"
|
||||
#include "hw/usb/hcd-tcp.h"
|
||||
#include "hw/arm/ipod_touch_timer.h"
|
||||
#include "hw/arm/ipod_touch_clock.h"
|
||||
#include "hw/arm/ipod_touch_chipid.h"
|
||||
@@ -155,7 +156,7 @@ typedef struct {
|
||||
IPodTouchGPIOState *gpio_state;
|
||||
IPodTouchSYSICState *sysic;
|
||||
MemoryRegion usb_dma_container_mr;
|
||||
DWC2State *usb_otg;
|
||||
DWC2State *dwc2;
|
||||
IPodTouchUSBPhysState *usb_phys_state;
|
||||
IPodTouchSHA1State *sha1_state;
|
||||
IPodTouchAESState *aes_state;
|
||||
|
||||
@@ -175,6 +175,15 @@
|
||||
|
||||
#define USB_INTERFACE_INVALID 255
|
||||
|
||||
/* binary representation */
|
||||
struct usb_control_packet {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct USBBusOps USBBusOps;
|
||||
typedef struct USBPort USBPort;
|
||||
typedef struct USBDevice USBDevice;
|
||||
|
||||
@@ -558,9 +558,11 @@
|
||||
#define DXEPCTL_TXFNUM_SHIFT 22
|
||||
#define DXEPCTL_TXFNUM_LIMIT 0xf
|
||||
#define DXEPCTL_TXFNUM(_x) ((_x) << 22)
|
||||
#define DXEPCTL_TXFNUM_GET(_x) (((_x) >> 22) & 0xf)
|
||||
#define DXEPCTL_STALL BIT(21)
|
||||
#define DXEPCTL_SNP BIT(20)
|
||||
#define DXEPCTL_EPTYPE_MASK (0x3 << 18)
|
||||
#define DXEPCTL_EPTYPE_SHIFT (18)
|
||||
#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18)
|
||||
#define DXEPCTL_EPTYPE_ISO (0x1 << 18)
|
||||
#define DXEPCTL_EPTYPE_BULK (0x2 << 18)
|
||||
@@ -578,6 +580,7 @@
|
||||
#define DXEPCTL_MPS_SHIFT 0
|
||||
#define DXEPCTL_MPS_LIMIT 0x7ff
|
||||
#define DXEPCTL_MPS(_x) ((_x) << 0)
|
||||
#define DXEPCTL_MPS_GET(_x) (((_x) >> 0) & 0x7ff)
|
||||
|
||||
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
|
||||
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
|
||||
@@ -607,19 +610,26 @@
|
||||
#define DIEPTSIZ0_PKTCNT_SHIFT 19
|
||||
#define DIEPTSIZ0_PKTCNT_LIMIT 0x3
|
||||
#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19)
|
||||
#define DIEPTSIZ0_PKTCNT_GET(_v) (((_v) >> 19) & 0x3)
|
||||
#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0)
|
||||
#define DIEPTSIZ0_XFERSIZE_SHIFT 0
|
||||
#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f
|
||||
#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0)
|
||||
#define DIEPTSIZ0_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7f)
|
||||
|
||||
#define DOEPTSIZ0 HSOTG_REG(0xB10)
|
||||
#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29)
|
||||
#define DOEPTSIZ0_SUPCNT_SHIFT 29
|
||||
#define DOEPTSIZ0_SUPCNT_LIMIT 0x3
|
||||
#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29)
|
||||
#define DOEPTSIZ0_PKTCNT BIT(19)
|
||||
#define DOEPTSIZ0_PKTCNT(_x) (((_x) & 0x1) << 19)
|
||||
#define DOEPTSIZ0_PKTCNT_MASK (0x1 << 19)
|
||||
#define DOEPTSIZ0_PKTCNT_SHIFT 19
|
||||
#define DOEPTSIZ0_PKTCNT_GET(_v) (((_v) >> 19) & 0x1)
|
||||
#define DOEPTSIZ0_XFERSIZE(_v) (((_v) & 0x7f) << 0)
|
||||
#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0)
|
||||
#define DOEPTSIZ0_XFERSIZE_SHIFT 0
|
||||
#define DOEPTSIZ0_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7f)
|
||||
|
||||
#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20))
|
||||
#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20))
|
||||
@@ -866,6 +876,7 @@ struct dwc2_dma_desc {
|
||||
#define DEV_DMA_BUFF_STS_DMABUSY 1
|
||||
#define DEV_DMA_BUFF_STS_DMADONE 2
|
||||
#define DEV_DMA_BUFF_STS_HBUSY 3
|
||||
#define DEV_DMA_BUFF_STS_GET(_x) (((_x) >> 30) & 0x3)
|
||||
#define DEV_DMA_STS_MASK (0x3 << 28)
|
||||
#define DEV_DMA_STS_SHIFT 28
|
||||
#define DEV_DMA_STS_SUCC 0
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef HW_USB_HCD_TCP_H
|
||||
#define HW_USB_HCD_TCP_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/usb.h"
|
||||
#include "io/channel.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#define TYPE_USB_TCP_HOST "usb-tcp-host"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(USBTCPHostState, USB_TCP_HOST)
|
||||
|
||||
typedef struct USBTCPPacket {
|
||||
USBPacket p;
|
||||
void *buffer;
|
||||
USBDevice *dev;
|
||||
USBTCPHostState *s;
|
||||
uint8_t addr;
|
||||
} USBTCPPacket;
|
||||
|
||||
struct USBTCPHostState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
USBBus bus;
|
||||
USBPort uports[3];
|
||||
QIOChannel *ioc;
|
||||
CoMutex write_mutex;
|
||||
Error *migration_blocker;
|
||||
bool closed;
|
||||
bool stopped;
|
||||
};
|
||||
|
||||
#endif /* HW_USB_HCD_TCP_H */
|
||||
Reference in new issue
Block a user