mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
Merge remote-tracking branch 'kraxel/usb.22' into staging
This commit is contained in:
commit
5df0a2a5ba
@ -100,6 +100,7 @@ common-obj-y += i2c.o smbus.o smbus_eeprom.o
|
||||
common-obj-y += eeprom93xx.o
|
||||
common-obj-y += scsi-disk.o cdrom.o
|
||||
common-obj-y += scsi-generic.o scsi-bus.o
|
||||
common-obj-y += hid.o
|
||||
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
|
||||
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
|
||||
common-obj-$(CONFIG_SSI) += ssi.o
|
||||
@ -183,6 +184,7 @@ user-obj-y += cutils.o cache-utils.o
|
||||
hw-obj-y =
|
||||
hw-obj-y += vl.o loader.o
|
||||
hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
|
||||
hw-obj-y += usb-libhw.o
|
||||
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
hw-obj-y += fw_cfg.o
|
||||
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
|
||||
|
4
dma.h
4
dma.h
@ -20,12 +20,12 @@ typedef struct {
|
||||
target_phys_addr_t len;
|
||||
} ScatterGatherEntry;
|
||||
|
||||
typedef struct {
|
||||
struct QEMUSGList {
|
||||
ScatterGatherEntry *sg;
|
||||
int nsg;
|
||||
int nalloc;
|
||||
target_phys_addr_t size;
|
||||
} QEMUSGList;
|
||||
};
|
||||
|
||||
void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint);
|
||||
void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base,
|
||||
|
62
hw/bt-hid.c
62
hw/bt-hid.c
@ -19,7 +19,9 @@
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "usb.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "console.h"
|
||||
#include "hid.h"
|
||||
#include "bt.h"
|
||||
|
||||
enum hid_transaction_req {
|
||||
@ -86,7 +88,7 @@ struct bt_hid_device_s {
|
||||
struct bt_l2cap_device_s btdev;
|
||||
struct bt_l2cap_conn_params_s *control;
|
||||
struct bt_l2cap_conn_params_s *interrupt;
|
||||
USBDevice *usbdev;
|
||||
HIDState hid;
|
||||
|
||||
int proto;
|
||||
int connected;
|
||||
@ -111,7 +113,7 @@ static void bt_hid_reset(struct bt_hid_device_s *s)
|
||||
bt_l2cap_device_done(&s->btdev);
|
||||
bt_l2cap_device_init(&s->btdev, net);
|
||||
|
||||
s->usbdev->info->handle_reset(s->usbdev);
|
||||
hid_reset(&s->hid);
|
||||
s->proto = BT_HID_PROTO_REPORT;
|
||||
s->state = bt_state_ready;
|
||||
s->dataother.len = 0;
|
||||
@ -124,23 +126,16 @@ static void bt_hid_reset(struct bt_hid_device_s *s)
|
||||
|
||||
static int bt_hid_out(struct bt_hid_device_s *s)
|
||||
{
|
||||
USBPacket p;
|
||||
|
||||
if (s->data_type == BT_DATA_OUTPUT) {
|
||||
p.pid = USB_TOKEN_OUT;
|
||||
p.devep = 1;
|
||||
p.data = s->dataout.buffer;
|
||||
p.len = s->dataout.len;
|
||||
s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p);
|
||||
|
||||
return s->dataout.len;
|
||||
/* nothing */
|
||||
;
|
||||
}
|
||||
|
||||
if (s->data_type == BT_DATA_FEATURE) {
|
||||
/* XXX:
|
||||
* does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
|
||||
* or a SET_REPORT? */
|
||||
p.devep = 0;
|
||||
;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -148,14 +143,8 @@ static int bt_hid_out(struct bt_hid_device_s *s)
|
||||
|
||||
static int bt_hid_in(struct bt_hid_device_s *s)
|
||||
{
|
||||
USBPacket p;
|
||||
|
||||
p.pid = USB_TOKEN_IN;
|
||||
p.devep = 1;
|
||||
p.data = s->datain.buffer;
|
||||
p.len = sizeof(s->datain.buffer);
|
||||
s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p);
|
||||
|
||||
s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer,
|
||||
sizeof(s->datain.buffer));
|
||||
return s->datain.len;
|
||||
}
|
||||
|
||||
@ -323,8 +312,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
|
||||
break;
|
||||
}
|
||||
s->proto = parameter;
|
||||
s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0,
|
||||
NULL);
|
||||
s->hid.protocol = parameter;
|
||||
ret = BT_HS_SUCCESSFUL;
|
||||
break;
|
||||
|
||||
@ -333,8 +321,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
|
||||
ret = BT_HS_ERR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1,
|
||||
s->control->sdu_out(s->control, 1));
|
||||
*s->control->sdu_out(s->control, 1) = s->hid.idle;
|
||||
s->control->sdu_submit(s->control);
|
||||
break;
|
||||
|
||||
@ -344,11 +331,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
|
||||
break;
|
||||
}
|
||||
|
||||
/* We don't need to know about the Idle Rate here really,
|
||||
* so just pass it on to the device. */
|
||||
ret = s->usbdev->info->handle_control(s->usbdev, NULL,
|
||||
SET_IDLE, data[1], 0, 0, NULL) ?
|
||||
BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
|
||||
s->hid.idle = data[1];
|
||||
/* XXX: Does this generate a handshake? */
|
||||
break;
|
||||
|
||||
@ -385,9 +368,10 @@ static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
|
||||
bt_hid_control_transaction(hid, data, len);
|
||||
}
|
||||
|
||||
static void bt_hid_datain(void *opaque)
|
||||
static void bt_hid_datain(HIDState *hs)
|
||||
{
|
||||
struct bt_hid_device_s *hid = opaque;
|
||||
struct bt_hid_device_s *hid =
|
||||
container_of(hs, struct bt_hid_device_s, hid);
|
||||
|
||||
/* If suspended, wake-up and send a wake-up event first. We might
|
||||
* want to also inspect the input report and ignore event like
|
||||
@ -450,7 +434,7 @@ static void bt_hid_connected_update(struct bt_hid_device_s *hid)
|
||||
hid->btdev.device.inquiry_scan = !hid->connected;
|
||||
|
||||
if (hid->connected && !prev) {
|
||||
hid->usbdev->info->handle_reset(hid->usbdev);
|
||||
hid_reset(&hid->hid);
|
||||
hid->proto = BT_HID_PROTO_REPORT;
|
||||
}
|
||||
|
||||
@ -518,7 +502,7 @@ static void bt_hid_destroy(struct bt_device_s *dev)
|
||||
bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
|
||||
bt_l2cap_device_done(&hid->btdev);
|
||||
|
||||
hid->usbdev->info->handle_destroy(hid->usbdev);
|
||||
hid_free(&hid->hid);
|
||||
|
||||
qemu_free(hid);
|
||||
}
|
||||
@ -531,7 +515,7 @@ enum peripheral_minor_class {
|
||||
};
|
||||
|
||||
static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
|
||||
USBDevice *dev, enum peripheral_minor_class minor)
|
||||
enum peripheral_minor_class minor)
|
||||
{
|
||||
struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s));
|
||||
uint32_t class =
|
||||
@ -551,9 +535,8 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
|
||||
bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
|
||||
BT_HID_MTU, bt_hid_new_interrupt_ch);
|
||||
|
||||
s->usbdev = dev;
|
||||
s->btdev.device.lmp_name = s->usbdev->product_desc;
|
||||
usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
|
||||
hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain);
|
||||
s->btdev.device.lmp_name = "BT Keyboard";
|
||||
|
||||
s->btdev.device.handle_destroy = bt_hid_destroy;
|
||||
|
||||
@ -566,6 +549,5 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
|
||||
|
||||
struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
|
||||
{
|
||||
USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd");
|
||||
return bt_hid_init(net, dev, class_keyboard);
|
||||
return bt_hid_init(net, class_keyboard);
|
||||
}
|
||||
|
403
hw/hid.c
Normal file
403
hw/hid.c
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* QEMU HID devices
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
* Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw.h"
|
||||
#include "console.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "hid.h"
|
||||
|
||||
#define HID_USAGE_ERROR_ROLLOVER 0x01
|
||||
#define HID_USAGE_POSTFAIL 0x02
|
||||
#define HID_USAGE_ERROR_UNDEFINED 0x03
|
||||
|
||||
/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
|
||||
* above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
|
||||
static const uint8_t hid_usage_keys[0x100] = {
|
||||
0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
|
||||
0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
|
||||
0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
|
||||
0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
|
||||
0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
|
||||
0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
|
||||
0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
|
||||
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
|
||||
0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
|
||||
0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
|
||||
0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
|
||||
0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
|
||||
0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
|
||||
0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
bool hid_has_events(HIDState *hs)
|
||||
{
|
||||
return hs->n > 0;
|
||||
}
|
||||
|
||||
void hid_set_next_idle(HIDState *hs, int64_t curtime)
|
||||
{
|
||||
hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000;
|
||||
}
|
||||
|
||||
static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
|
||||
{
|
||||
e->xdx = e->ydy = e->dz = 0;
|
||||
e->buttons_state = buttons;
|
||||
}
|
||||
|
||||
static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
|
||||
int x1, int y1, int z1) {
|
||||
if (xyrel) {
|
||||
e->xdx += x1;
|
||||
e->ydy += y1;
|
||||
} else {
|
||||
e->xdx = x1;
|
||||
e->ydy = y1;
|
||||
/* Windows drivers do not like the 0/0 position and ignore such
|
||||
* events. */
|
||||
if (!(x1 | y1)) {
|
||||
x1 = 1;
|
||||
}
|
||||
}
|
||||
e->dz += z1;
|
||||
}
|
||||
|
||||
static void hid_pointer_event(void *opaque,
|
||||
int x1, int y1, int z1, int buttons_state)
|
||||
{
|
||||
HIDState *hs = opaque;
|
||||
unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
|
||||
unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
|
||||
|
||||
/* We combine events where feasible to keep the queue small. We shouldn't
|
||||
* combine anything with the first event of a particular button state, as
|
||||
* that would change the location of the button state change. When the
|
||||
* queue is empty, a second event is needed because we don't know if
|
||||
* the first event changed the button state. */
|
||||
if (hs->n == QUEUE_LENGTH) {
|
||||
/* Queue full. Discard old button state, combine motion normally. */
|
||||
hs->ptr.queue[use_slot].buttons_state = buttons_state;
|
||||
} else if (hs->n < 2 ||
|
||||
hs->ptr.queue[use_slot].buttons_state != buttons_state ||
|
||||
hs->ptr.queue[previous_slot].buttons_state !=
|
||||
hs->ptr.queue[use_slot].buttons_state) {
|
||||
/* Cannot or should not combine, so add an empty item to the queue. */
|
||||
QUEUE_INCR(use_slot);
|
||||
hs->n++;
|
||||
hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
|
||||
}
|
||||
hid_pointer_event_combine(&hs->ptr.queue[use_slot],
|
||||
hs->kind == HID_MOUSE,
|
||||
x1, y1, z1);
|
||||
hs->event(hs);
|
||||
}
|
||||
|
||||
static void hid_keyboard_event(void *opaque, int keycode)
|
||||
{
|
||||
HIDState *hs = opaque;
|
||||
int slot;
|
||||
|
||||
if (hs->n == QUEUE_LENGTH) {
|
||||
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
||||
return;
|
||||
}
|
||||
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
|
||||
hs->kbd.keycodes[slot] = keycode;
|
||||
hs->event(hs);
|
||||
}
|
||||
|
||||
static void hid_keyboard_process_keycode(HIDState *hs)
|
||||
{
|
||||
uint8_t hid_code, key;
|
||||
int i, keycode, slot;
|
||||
|
||||
if (hs->n == 0) {
|
||||
return;
|
||||
}
|
||||
slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
|
||||
keycode = hs->kbd.keycodes[slot];
|
||||
|
||||
key = keycode & 0x7f;
|
||||
hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
|
||||
hs->kbd.modifiers &= ~(1 << 8);
|
||||
|
||||
switch (hid_code) {
|
||||
case 0x00:
|
||||
return;
|
||||
|
||||
case 0xe0:
|
||||
if (hs->kbd.modifiers & (1 << 9)) {
|
||||
hs->kbd.modifiers ^= 3 << 8;
|
||||
return;
|
||||
}
|
||||
case 0xe1 ... 0xe7:
|
||||
if (keycode & (1 << 7)) {
|
||||
hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
|
||||
return;
|
||||
}
|
||||
case 0xe8 ... 0xef:
|
||||
hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (keycode & (1 << 7)) {
|
||||
for (i = hs->kbd.keys - 1; i >= 0; i--) {
|
||||
if (hs->kbd.key[i] == hid_code) {
|
||||
hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
|
||||
hs->kbd.key[hs->kbd.keys] = 0x00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for (i = hs->kbd.keys - 1; i >= 0; i--) {
|
||||
if (hs->kbd.key[i] == hid_code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
if (hs->kbd.keys < sizeof(hs->kbd.key)) {
|
||||
hs->kbd.key[hs->kbd.keys++] = hid_code;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int int_clamp(int val, int vmin, int vmax)
|
||||
{
|
||||
if (val < vmin) {
|
||||
return vmin;
|
||||
} else if (val > vmax) {
|
||||
return vmax;
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
||||
{
|
||||
int dx, dy, dz, b, l;
|
||||
int index;
|
||||
HIDPointerEvent *e;
|
||||
|
||||
if (!hs->ptr.mouse_grabbed) {
|
||||
qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
|
||||
hs->ptr.mouse_grabbed = 1;
|
||||
}
|
||||
|
||||
/* When the buffer is empty, return the last event. Relative
|
||||
movements will all be zero. */
|
||||
index = (hs->n ? hs->head : hs->head - 1);
|
||||
e = &hs->ptr.queue[index & QUEUE_MASK];
|
||||
|
||||
if (hs->kind == HID_MOUSE) {
|
||||
dx = int_clamp(e->xdx, -127, 127);
|
||||
dy = int_clamp(e->ydy, -127, 127);
|
||||
e->xdx -= dx;
|
||||
e->ydy -= dy;
|
||||
} else {
|
||||
dx = e->xdx;
|
||||
dy = e->ydy;
|
||||
}
|
||||
dz = int_clamp(e->dz, -127, 127);
|
||||
e->dz -= dz;
|
||||
|
||||
b = 0;
|
||||
if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
|
||||
b |= 0x01;
|
||||
}
|
||||
if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
|
||||
b |= 0x02;
|
||||
}
|
||||
if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
|
||||
b |= 0x04;
|
||||
}
|
||||
|
||||
if (hs->n &&
|
||||
!e->dz &&
|
||||
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
|
||||
/* that deals with this event */
|
||||
QUEUE_INCR(hs->head);
|
||||
hs->n--;
|
||||
}
|
||||
|
||||
/* Appears we have to invert the wheel direction */
|
||||
dz = 0 - dz;
|
||||
l = 0;
|
||||
switch (hs->kind) {
|
||||
case HID_MOUSE:
|
||||
if (len > l) {
|
||||
buf[l++] = b;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dx;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dy;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dz;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_TABLET:
|
||||
if (len > l) {
|
||||
buf[l++] = b;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dx & 0xff;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dx >> 8;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dy & 0xff;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dy >> 8;
|
||||
}
|
||||
if (len > l) {
|
||||
buf[l++] = dz;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
|
||||
{
|
||||
if (len < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hid_keyboard_process_keycode(hs);
|
||||
|
||||
buf[0] = hs->kbd.modifiers & 0xff;
|
||||
buf[1] = 0;
|
||||
if (hs->kbd.keys > 6) {
|
||||
memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
|
||||
} else {
|
||||
memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
|
||||
}
|
||||
|
||||
return MIN(8, len);
|
||||
}
|
||||
|
||||
int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
|
||||
{
|
||||
if (len > 0) {
|
||||
int ledstate = 0;
|
||||
/* 0x01: Num Lock LED
|
||||
* 0x02: Caps Lock LED
|
||||
* 0x04: Scroll Lock LED
|
||||
* 0x08: Compose LED
|
||||
* 0x10: Kana LED */
|
||||
hs->kbd.leds = buf[0];
|
||||
if (hs->kbd.leds & 0x04) {
|
||||
ledstate |= QEMU_SCROLL_LOCK_LED;
|
||||
}
|
||||
if (hs->kbd.leds & 0x01) {
|
||||
ledstate |= QEMU_NUM_LOCK_LED;
|
||||
}
|
||||
if (hs->kbd.leds & 0x02) {
|
||||
ledstate |= QEMU_CAPS_LOCK_LED;
|
||||
}
|
||||
kbd_put_ledstate(ledstate);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hid_reset(HIDState *hs)
|
||||
{
|
||||
switch (hs->kind) {
|
||||
case HID_KEYBOARD:
|
||||
qemu_add_kbd_event_handler(hid_keyboard_event, hs);
|
||||
memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
|
||||
memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
|
||||
hs->kbd.keys = 0;
|
||||
break;
|
||||
case HID_MOUSE:
|
||||
case HID_TABLET:
|
||||
memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
|
||||
break;
|
||||
}
|
||||
hs->head = 0;
|
||||
hs->n = 0;
|
||||
hs->protocol = 1;
|
||||
hs->idle = 0;
|
||||
}
|
||||
|
||||
void hid_free(HIDState *hs)
|
||||
{
|
||||
switch (hs->kind) {
|
||||
case HID_KEYBOARD:
|
||||
qemu_remove_kbd_event_handler();
|
||||
break;
|
||||
case HID_MOUSE:
|
||||
case HID_TABLET:
|
||||
qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
|
||||
{
|
||||
hs->kind = kind;
|
||||
hs->event = event;
|
||||
|
||||
if (hs->kind == HID_MOUSE) {
|
||||
hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
|
||||
0, "QEMU HID Mouse");
|
||||
} else if (hs->kind == HID_TABLET) {
|
||||
hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
|
||||
1, "QEMU HID Tablet");
|
||||
}
|
||||
}
|
58
hw/hid.h
Normal file
58
hw/hid.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef QEMU_HID_H
|
||||
#define QEMU_HID_H
|
||||
|
||||
#define HID_MOUSE 1
|
||||
#define HID_TABLET 2
|
||||
#define HID_KEYBOARD 3
|
||||
|
||||
typedef struct HIDPointerEvent {
|
||||
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
|
||||
int32_t dz, buttons_state;
|
||||
} HIDPointerEvent;
|
||||
|
||||
#define QUEUE_LENGTH 16 /* should be enough for a triple-click */
|
||||
#define QUEUE_MASK (QUEUE_LENGTH-1u)
|
||||
#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK)
|
||||
|
||||
typedef struct HIDState HIDState;
|
||||
typedef void (*HIDEventFunc)(HIDState *s);
|
||||
|
||||
typedef struct HIDMouseState {
|
||||
HIDPointerEvent queue[QUEUE_LENGTH];
|
||||
int mouse_grabbed;
|
||||
QEMUPutMouseEntry *eh_entry;
|
||||
} HIDMouseState;
|
||||
|
||||
typedef struct HIDKeyboardState {
|
||||
uint32_t keycodes[QUEUE_LENGTH];
|
||||
uint16_t modifiers;
|
||||
uint8_t leds;
|
||||
uint8_t key[16];
|
||||
int32_t keys;
|
||||
} HIDKeyboardState;
|
||||
|
||||
struct HIDState {
|
||||
union {
|
||||
HIDMouseState ptr;
|
||||
HIDKeyboardState kbd;
|
||||
};
|
||||
uint32_t head; /* index into circular queue */
|
||||
uint32_t n;
|
||||
int kind;
|
||||
int32_t protocol;
|
||||
uint8_t idle;
|
||||
int64_t next_idle_clock;
|
||||
HIDEventFunc event;
|
||||
};
|
||||
|
||||
void hid_init(HIDState *hs, int kind, HIDEventFunc event);
|
||||
void hid_reset(HIDState *hs);
|
||||
void hid_free(HIDState *hs);
|
||||
|
||||
bool hid_has_events(HIDState *hs);
|
||||
void hid_set_next_idle(HIDState *hs, int64_t curtime);
|
||||
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len);
|
||||
int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len);
|
||||
int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len);
|
||||
|
||||
#endif /* QEMU_HID_H */
|
@ -234,11 +234,11 @@ static void softusb_usbdev_datain(void *opaque)
|
||||
|
||||
USBPacket p;
|
||||
|
||||
p.pid = USB_TOKEN_IN;
|
||||
p.devep = 1;
|
||||
p.data = s->kbd_usb_buffer;
|
||||
p.len = sizeof(s->kbd_usb_buffer);
|
||||
usb_packet_init(&p);
|
||||
usb_packet_setup(&p, USB_TOKEN_IN, 0, 1);
|
||||
usb_packet_addbuf(&p, s->kbd_usb_buffer, sizeof(s->kbd_usb_buffer));
|
||||
s->usbdev->info->handle_data(s->usbdev, &p);
|
||||
usb_packet_cleanup(&p);
|
||||
|
||||
softusb_kbd_changed(s);
|
||||
}
|
||||
|
31
hw/usb-bt.c
31
hw/usb-bt.c
@ -294,9 +294,9 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
|
||||
if (likely(!fifo->len))
|
||||
return USB_RET_STALL;
|
||||
|
||||
len = MIN(p->len, fifo->fifo[fifo->start].len);
|
||||
memcpy(p->data, fifo->fifo[fifo->start].data, len);
|
||||
if (len == p->len) {
|
||||
len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
|
||||
usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
|
||||
if (len == p->iov.size) {
|
||||
fifo->fifo[fifo->start].len -= len;
|
||||
fifo->fifo[fifo->start].data += len;
|
||||
} else {
|
||||
@ -319,20 +319,13 @@ static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
|
||||
struct usb_hci_out_fifo_s *fifo,
|
||||
void (*send)(struct HCIInfo *, const uint8_t *, int),
|
||||
int (*complete)(const uint8_t *, int),
|
||||
const uint8_t *data, int len)
|
||||
USBPacket *p)
|
||||
{
|
||||
if (fifo->len) {
|
||||
memcpy(fifo->data + fifo->len, data, len);
|
||||
fifo->len += len;
|
||||
if (complete(fifo->data, fifo->len)) {
|
||||
send(s->hci, fifo->data, fifo->len);
|
||||
fifo->len = 0;
|
||||
}
|
||||
} else if (complete(data, len))
|
||||
send(s->hci, data, len);
|
||||
else {
|
||||
memcpy(fifo->data, data, len);
|
||||
fifo->len = len;
|
||||
usb_packet_copy(p, fifo->data + fifo->len, p->iov.size);
|
||||
fifo->len += p->iov.size;
|
||||
if (complete(fifo->data, fifo->len)) {
|
||||
send(s->hci, fifo->data, fifo->len);
|
||||
fifo->len = 0;
|
||||
}
|
||||
|
||||
/* TODO: do we need to loop? */
|
||||
@ -432,7 +425,7 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
|
||||
if (s->config)
|
||||
usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
|
||||
usb_bt_hci_cmd_complete, data, length);
|
||||
usb_bt_hci_cmd_complete, p);
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
@ -474,12 +467,12 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
|
||||
switch (p->devep & 0xf) {
|
||||
case USB_ACL_EP:
|
||||
usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
|
||||
usb_bt_hci_acl_complete, p->data, p->len);
|
||||
usb_bt_hci_acl_complete, p);
|
||||
break;
|
||||
|
||||
case USB_SCO_EP:
|
||||
usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
|
||||
usb_bt_hci_sco_complete, p->data, p->len);
|
||||
usb_bt_hci_sco_complete, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -934,16 +934,16 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
||||
{
|
||||
CCID_Header *ccid_header;
|
||||
|
||||
if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
|
||||
if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
ccid_header = (CCID_Header *)s->bulk_out_data;
|
||||
memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len);
|
||||
s->bulk_out_pos += p->len;
|
||||
if (p->len == CCID_MAX_PACKET_SIZE) {
|
||||
usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
|
||||
s->bulk_out_pos += p->iov.size;
|
||||
if (p->iov.size == CCID_MAX_PACKET_SIZE) {
|
||||
DPRINTF(s, D_VERBOSE,
|
||||
"usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
|
||||
p->len, ccid_header->dwLength);
|
||||
"usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
|
||||
p->iov.size, ccid_header->dwLength);
|
||||
return 0;
|
||||
}
|
||||
if (s->bulk_out_pos < 10) {
|
||||
@ -1006,15 +1006,17 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
|
||||
static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
assert(len > 0);
|
||||
assert(p->iov.size > 0);
|
||||
ccid_bulk_in_get(s);
|
||||
if (s->current_bulk_in != NULL) {
|
||||
ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len);
|
||||
memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret);
|
||||
ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
|
||||
p->iov.size);
|
||||
usb_packet_copy(p, s->current_bulk_in->data +
|
||||
s->current_bulk_in->pos, ret);
|
||||
s->current_bulk_in->pos += ret;
|
||||
if (s->current_bulk_in->pos == s->current_bulk_in->len) {
|
||||
ccid_bulk_in_release(s);
|
||||
@ -1025,11 +1027,13 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
|
||||
}
|
||||
if (ret > 0) {
|
||||
DPRINTF(s, D_MORE_INFO,
|
||||
"%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret);
|
||||
"%s: %zd/%d req/act to guest (BULK_IN)\n",
|
||||
__func__, p->iov.size, ret);
|
||||
}
|
||||
if (ret != USB_RET_NAK && ret < len) {
|
||||
if (ret != USB_RET_NAK && ret < p->iov.size) {
|
||||
DPRINTF(s, 1,
|
||||
"%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len);
|
||||
"%s: returning short (EREMOTEIO) %d < %zd\n",
|
||||
__func__, ret, p->iov.size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -1038,8 +1042,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
|
||||
int ret = 0;
|
||||
uint8_t *data = p->data;
|
||||
int len = p->len;
|
||||
uint8_t buf[2];
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
@ -1049,24 +1052,25 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
|
||||
case USB_TOKEN_IN:
|
||||
switch (p->devep & 0xf) {
|
||||
case CCID_BULK_IN_EP:
|
||||
if (!len) {
|
||||
if (!p->iov.size) {
|
||||
ret = USB_RET_NAK;
|
||||
} else {
|
||||
ret = ccid_bulk_in_copy_to_guest(s, data, len);
|
||||
ret = ccid_bulk_in_copy_to_guest(s, p);
|
||||
}
|
||||
break;
|
||||
case CCID_INT_IN_EP:
|
||||
if (s->notify_slot_change) {
|
||||
/* page 56, RDR_to_PC_NotifySlotChange */
|
||||
data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
|
||||
data[1] = s->bmSlotICCState;
|
||||
buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
|
||||
buf[1] = s->bmSlotICCState;
|
||||
usb_packet_copy(p, buf, 2);
|
||||
ret = 2;
|
||||
s->notify_slot_change = false;
|
||||
s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
|
||||
DPRINTF(s, D_INFO,
|
||||
"handle_data: int_in: notify_slot_change %X, "
|
||||
"requested len %d\n",
|
||||
s->bmSlotICCState, len);
|
||||
"requested len %zd\n",
|
||||
s->bmSlotICCState, p->iov.size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
164
hw/usb-ehci.c
164
hw/usb-ehci.c
@ -28,6 +28,7 @@
|
||||
#include "pci.h"
|
||||
#include "monitor.h"
|
||||
#include "trace.h"
|
||||
#include "dma.h"
|
||||
|
||||
#define EHCI_DEBUG 0
|
||||
|
||||
@ -269,6 +270,7 @@ typedef struct EHCIqtd {
|
||||
|
||||
uint32_t bufptr[5]; // Standard buffer pointer
|
||||
#define QTD_BUFPTR_MASK 0xfffff000
|
||||
#define QTD_BUFPTR_SH 12
|
||||
} EHCIqtd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.6
|
||||
@ -357,7 +359,7 @@ struct EHCIQueue {
|
||||
uint32_t qtdaddr; // address QTD read from
|
||||
|
||||
USBPacket packet;
|
||||
uint8_t buffer[BUFF_SIZE];
|
||||
QEMUSGList sgl;
|
||||
int pid;
|
||||
uint32_t tbytes;
|
||||
enum async_state async;
|
||||
@ -414,7 +416,7 @@ struct EHCIState {
|
||||
uint32_t p_fetch_addr; // which address to look at next
|
||||
|
||||
USBPacket ipacket;
|
||||
uint8_t ibuffer[BUFF_SIZE];
|
||||
QEMUSGList isgl;
|
||||
int isoch_pause;
|
||||
|
||||
uint64_t last_run_ns;
|
||||
@ -1165,60 +1167,58 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw)
|
||||
static int ehci_init_transfer(EHCIQueue *q)
|
||||
{
|
||||
int bufpos = 0;
|
||||
int cpage, offset;
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
|
||||
|
||||
if (!bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
|
||||
if (cpage > 4) {
|
||||
fprintf(stderr, "cpage out of range (%d)\n", cpage);
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
uint32_t cpage, offset, bytes, plen;
|
||||
target_phys_addr_t page;
|
||||
|
||||
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
|
||||
bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
|
||||
offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
|
||||
qemu_sglist_init(&q->sgl, 5);
|
||||
|
||||
do {
|
||||
/* start and end of this page */
|
||||
head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
|
||||
tail = head + ~QTD_BUFPTR_MASK + 1;
|
||||
/* add offset into page */
|
||||
head |= offset;
|
||||
|
||||
if (bytes <= (tail - head)) {
|
||||
tail = head + bytes;
|
||||
while (bytes > 0) {
|
||||
if (cpage > 4) {
|
||||
fprintf(stderr, "cpage out of range (%d)\n", cpage);
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
|
||||
trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos);
|
||||
cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw);
|
||||
|
||||
bufpos += (tail - head);
|
||||
offset += (tail - head);
|
||||
bytes -= (tail - head);
|
||||
|
||||
if (bytes > 0) {
|
||||
cpage++;
|
||||
page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
|
||||
page += offset;
|
||||
plen = bytes;
|
||||
if (plen > 4096 - offset) {
|
||||
plen = 4096 - offset;
|
||||
offset = 0;
|
||||
cpage++;
|
||||
}
|
||||
} while (bytes > 0);
|
||||
|
||||
/* save cpage */
|
||||
set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
|
||||
|
||||
/* save offset into cpage */
|
||||
q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
|
||||
q->qh.bufptr[0] |= offset;
|
||||
|
||||
qemu_sglist_add(&q->sgl, page, plen);
|
||||
bytes -= plen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_finish_transfer(EHCIQueue *q, int status)
|
||||
{
|
||||
uint32_t cpage, offset;
|
||||
|
||||
qemu_sglist_destroy(&q->sgl);
|
||||
|
||||
if (status > 0) {
|
||||
/* update cpage & offset */
|
||||
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
|
||||
offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
|
||||
|
||||
offset += status;
|
||||
cpage += offset >> QTD_BUFPTR_SH;
|
||||
offset &= ~QTD_BUFPTR_MASK;
|
||||
|
||||
set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
|
||||
q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
|
||||
q->qh.bufptr[0] |= offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
EHCIQueue *q;
|
||||
@ -1235,7 +1235,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||
trace_usb_ehci_queue_action(q, "wakeup");
|
||||
assert(q->async == EHCI_ASYNC_INFLIGHT);
|
||||
q->async = EHCI_ASYNC_FINISHED;
|
||||
q->usb_status = packet->len;
|
||||
q->usb_status = packet->result;
|
||||
}
|
||||
|
||||
static void ehci_execute_complete(EHCIQueue *q)
|
||||
@ -1295,10 +1295,6 @@ err:
|
||||
}
|
||||
|
||||
if (q->tbytes && q->pid == USB_TOKEN_IN) {
|
||||
if (ehci_buffer_rw(q, q->usb_status, 1) != 0) {
|
||||
q->usb_status = USB_RET_PROCERR;
|
||||
return;
|
||||
}
|
||||
q->tbytes -= q->usb_status;
|
||||
} else {
|
||||
q->tbytes = 0;
|
||||
@ -1307,6 +1303,8 @@ err:
|
||||
DPRINTF("updating tbytes to %d\n", q->tbytes);
|
||||
set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
|
||||
}
|
||||
ehci_finish_transfer(q, q->usb_status);
|
||||
usb_packet_unmap(&q->packet);
|
||||
|
||||
q->qh.token ^= QTD_TOKEN_DTOGGLE;
|
||||
q->qh.token &= ~QTD_TOKEN_ACTIVE;
|
||||
@ -1346,8 +1344,7 @@ static int ehci_execute(EHCIQueue *q)
|
||||
default: fprintf(stderr, "bad token\n"); break;
|
||||
}
|
||||
|
||||
if ((q->tbytes && q->pid != USB_TOKEN_IN) &&
|
||||
(ehci_buffer_rw(q, q->tbytes, 0) != 0)) {
|
||||
if (ehci_init_transfer(q) != 0) {
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
|
||||
@ -1356,6 +1353,9 @@ static int ehci_execute(EHCIQueue *q)
|
||||
|
||||
ret = USB_RET_NODEV;
|
||||
|
||||
usb_packet_setup(&q->packet, q->pid, devadr, endp);
|
||||
usb_packet_map(&q->packet, &q->sgl);
|
||||
|
||||
// TO-DO: associating device with ehci port
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
port = &q->ehci->ports[i];
|
||||
@ -1367,17 +1367,12 @@ static int ehci_execute(EHCIQueue *q)
|
||||
continue;
|
||||
}
|
||||
|
||||
q->packet.pid = q->pid;
|
||||
q->packet.devaddr = devadr;
|
||||
q->packet.devep = endp;
|
||||
q->packet.data = q->buffer;
|
||||
q->packet.len = q->tbytes;
|
||||
|
||||
ret = usb_handle_packet(dev, &q->packet);
|
||||
|
||||
DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
|
||||
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
|
||||
"(total %d) endp %x ret %d\n",
|
||||
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
|
||||
q->packet.len, q->tbytes, endp, ret);
|
||||
q->packet.iov.size, q->tbytes, endp, ret);
|
||||
|
||||
if (ret != USB_RET_NODEV) {
|
||||
break;
|
||||
@ -1401,7 +1396,7 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
USBPort *port;
|
||||
USBDevice *dev;
|
||||
int ret;
|
||||
uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp;
|
||||
uint32_t i, j, len, pid, dir, devaddr, endp;
|
||||
uint32_t pg, off, ptr1, ptr2, max, mult;
|
||||
|
||||
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
|
||||
@ -1426,29 +1421,23 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
|
||||
qemu_sglist_init(&ehci->isgl, 2);
|
||||
if (off + len > 4096) {
|
||||
/* transfer crosses page border */
|
||||
len2 = off + len - 4096;
|
||||
len1 = len - len2;
|
||||
uint32_t len2 = off + len - 4096;
|
||||
uint32_t len1 = len - len2;
|
||||
qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
|
||||
qemu_sglist_add(&ehci->isgl, ptr2, len2);
|
||||
} else {
|
||||
len1 = len;
|
||||
len2 = 0;
|
||||
qemu_sglist_add(&ehci->isgl, ptr1 + off, len);
|
||||
}
|
||||
|
||||
if (!dir) {
|
||||
pid = USB_TOKEN_OUT;
|
||||
trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0);
|
||||
cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0);
|
||||
if (len2) {
|
||||
trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1);
|
||||
cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0);
|
||||
}
|
||||
} else {
|
||||
pid = USB_TOKEN_IN;
|
||||
}
|
||||
pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
||||
|
||||
usb_packet_setup(&ehci->ipacket, pid, devaddr, endp);
|
||||
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
||||
|
||||
ret = USB_RET_NODEV;
|
||||
|
||||
for (j = 0; j < NB_PORTS; j++) {
|
||||
port = &ehci->ports[j];
|
||||
dev = port->dev;
|
||||
@ -1457,12 +1446,6 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
continue;
|
||||
}
|
||||
|
||||
ehci->ipacket.pid = pid;
|
||||
ehci->ipacket.devaddr = devaddr;
|
||||
ehci->ipacket.devep = endp;
|
||||
ehci->ipacket.data = ehci->ibuffer;
|
||||
ehci->ipacket.len = len;
|
||||
|
||||
ret = usb_handle_packet(dev, &ehci->ipacket);
|
||||
|
||||
if (ret != USB_RET_NODEV) {
|
||||
@ -1470,6 +1453,9 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
}
|
||||
}
|
||||
|
||||
usb_packet_unmap(&ehci->ipacket);
|
||||
qemu_sglist_destroy(&ehci->isgl);
|
||||
|
||||
#if 0
|
||||
/* In isoch, there is no facility to indicate a NAK so let's
|
||||
* instead just complete a zero-byte transaction. Setting
|
||||
@ -1507,20 +1493,6 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
|
||||
} else {
|
||||
/* IN */
|
||||
if (len1 > ret) {
|
||||
len1 = ret;
|
||||
}
|
||||
if (len2 > ret - len1) {
|
||||
len2 = ret - len1;
|
||||
}
|
||||
if (len1) {
|
||||
trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0);
|
||||
cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1);
|
||||
}
|
||||
if (len2) {
|
||||
trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1);
|
||||
cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1);
|
||||
}
|
||||
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
|
||||
}
|
||||
|
||||
|
519
hw/usb-hid.c
519
hw/usb-hid.c
@ -27,6 +27,7 @@
|
||||
#include "usb.h"
|
||||
#include "usb-desc.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "hid.h"
|
||||
|
||||
/* HID interface requests */
|
||||
#define GET_REPORT 0xa101
|
||||
@ -41,46 +42,9 @@
|
||||
#define USB_DT_REPORT 0x22
|
||||
#define USB_DT_PHY 0x23
|
||||
|
||||
#define USB_MOUSE 1
|
||||
#define USB_TABLET 2
|
||||
#define USB_KEYBOARD 3
|
||||
|
||||
typedef struct USBPointerEvent {
|
||||
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
|
||||
int32_t dz, buttons_state;
|
||||
} USBPointerEvent;
|
||||
|
||||
#define QUEUE_LENGTH 16 /* should be enough for a triple-click */
|
||||
#define QUEUE_MASK (QUEUE_LENGTH-1u)
|
||||
#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK)
|
||||
|
||||
typedef struct USBMouseState {
|
||||
USBPointerEvent queue[QUEUE_LENGTH];
|
||||
int mouse_grabbed;
|
||||
QEMUPutMouseEntry *eh_entry;
|
||||
} USBMouseState;
|
||||
|
||||
typedef struct USBKeyboardState {
|
||||
uint32_t keycodes[QUEUE_LENGTH];
|
||||
uint16_t modifiers;
|
||||
uint8_t leds;
|
||||
uint8_t key[16];
|
||||
int32_t keys;
|
||||
} USBKeyboardState;
|
||||
|
||||
typedef struct USBHIDState {
|
||||
USBDevice dev;
|
||||
union {
|
||||
USBMouseState ptr;
|
||||
USBKeyboardState kbd;
|
||||
};
|
||||
uint32_t head; /* index into circular queue */
|
||||
uint32_t n;
|
||||
int kind;
|
||||
int32_t protocol;
|
||||
uint8_t idle;
|
||||
int64_t next_idle_clock;
|
||||
int changed;
|
||||
HIDState hid;
|
||||
void *datain_opaque;
|
||||
void (*datain)(void *);
|
||||
} USBHIDState;
|
||||
@ -394,344 +358,29 @@ static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
|
||||
0xc0, /* End Collection */
|
||||
};
|
||||
|
||||
#define USB_HID_USAGE_ERROR_ROLLOVER 0x01
|
||||
#define USB_HID_USAGE_POSTFAIL 0x02
|
||||
#define USB_HID_USAGE_ERROR_UNDEFINED 0x03
|
||||
|
||||
/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
|
||||
* above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
|
||||
static const uint8_t usb_hid_usage_keys[0x100] = {
|
||||
0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
|
||||
0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
|
||||
0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
|
||||
0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
|
||||
0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
|
||||
0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
|
||||
0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
|
||||
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
|
||||
0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
|
||||
0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
|
||||
0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
|
||||
0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
|
||||
0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
|
||||
0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static void usb_hid_changed(USBHIDState *hs)
|
||||
static void usb_hid_changed(HIDState *hs)
|
||||
{
|
||||
hs->changed = 1;
|
||||
USBHIDState *us = container_of(hs, USBHIDState, hid);
|
||||
|
||||
if (hs->datain)
|
||||
hs->datain(hs->datain_opaque);
|
||||
|
||||
usb_wakeup(&hs->dev);
|
||||
}
|
||||
|
||||
static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) {
|
||||
e->xdx = e->ydy = e->dz = 0;
|
||||
e->buttons_state = buttons;
|
||||
}
|
||||
|
||||
static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel,
|
||||
int x1, int y1, int z1) {
|
||||
if (xyrel) {
|
||||
e->xdx += x1;
|
||||
e->ydy += y1;
|
||||
} else {
|
||||
e->xdx = x1;
|
||||
e->ydy = y1;
|
||||
/* Windows drivers do not like the 0/0 position and ignore such
|
||||
* events. */
|
||||
if (!(x1 | y1)) {
|
||||
x1 = 1;
|
||||
}
|
||||
}
|
||||
e->dz += z1;
|
||||
}
|
||||
|
||||
static void usb_pointer_event(void *opaque,
|
||||
int x1, int y1, int z1, int buttons_state)
|
||||
{
|
||||
USBHIDState *hs = opaque;
|
||||
USBMouseState *s = &hs->ptr;
|
||||
unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
|
||||
unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
|
||||
|
||||
/* We combine events where feasible to keep the queue small. We shouldn't
|
||||
* combine anything with the first event of a particular button state, as
|
||||
* that would change the location of the button state change. When the
|
||||
* queue is empty, a second event is needed because we don't know if
|
||||
* the first event changed the button state. */
|
||||
if (hs->n == QUEUE_LENGTH) {
|
||||
/* Queue full. Discard old button state, combine motion normally. */
|
||||
s->queue[use_slot].buttons_state = buttons_state;
|
||||
} else if (hs->n < 2 ||
|
||||
s->queue[use_slot].buttons_state != buttons_state ||
|
||||
s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) {
|
||||
/* Cannot or should not combine, so add an empty item to the queue. */
|
||||
QUEUE_INCR(use_slot);
|
||||
hs->n++;
|
||||
usb_pointer_event_clear(&s->queue[use_slot], buttons_state);
|
||||
}
|
||||
usb_pointer_event_combine(&s->queue[use_slot],
|
||||
hs->kind == USB_MOUSE,
|
||||
x1, y1, z1);
|
||||
usb_hid_changed(hs);
|
||||
}
|
||||
|
||||
static void usb_keyboard_event(void *opaque, int keycode)
|
||||
{
|
||||
USBHIDState *hs = opaque;
|
||||
USBKeyboardState *s = &hs->kbd;
|
||||
int slot;
|
||||
|
||||
if (hs->n == QUEUE_LENGTH) {
|
||||
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
||||
return;
|
||||
}
|
||||
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
|
||||
s->keycodes[slot] = keycode;
|
||||
usb_hid_changed(hs);
|
||||
}
|
||||
|
||||
static void usb_keyboard_process_keycode(USBHIDState *hs)
|
||||
{
|
||||
USBKeyboardState *s = &hs->kbd;
|
||||
uint8_t hid_code, key;
|
||||
int i, keycode, slot;
|
||||
|
||||
if (hs->n == 0) {
|
||||
return;
|
||||
}
|
||||
slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
|
||||
keycode = s->keycodes[slot];
|
||||
|
||||
key = keycode & 0x7f;
|
||||
hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
|
||||
s->modifiers &= ~(1 << 8);
|
||||
|
||||
switch (hid_code) {
|
||||
case 0x00:
|
||||
return;
|
||||
|
||||
case 0xe0:
|
||||
if (s->modifiers & (1 << 9)) {
|
||||
s->modifiers ^= 3 << 8;
|
||||
return;
|
||||
}
|
||||
case 0xe1 ... 0xe7:
|
||||
if (keycode & (1 << 7)) {
|
||||
s->modifiers &= ~(1 << (hid_code & 0x0f));
|
||||
return;
|
||||
}
|
||||
case 0xe8 ... 0xef:
|
||||
s->modifiers |= 1 << (hid_code & 0x0f);
|
||||
return;
|
||||
if (us->datain) {
|
||||
us->datain(us->datain_opaque);
|
||||
}
|
||||
|
||||
if (keycode & (1 << 7)) {
|
||||
for (i = s->keys - 1; i >= 0; i --)
|
||||
if (s->key[i] == hid_code) {
|
||||
s->key[i] = s->key[-- s->keys];
|
||||
s->key[s->keys] = 0x00;
|
||||
break;
|
||||
}
|
||||
if (i < 0)
|
||||
return;
|
||||
} else {
|
||||
for (i = s->keys - 1; i >= 0; i --)
|
||||
if (s->key[i] == hid_code)
|
||||
break;
|
||||
if (i < 0) {
|
||||
if (s->keys < sizeof(s->key))
|
||||
s->key[s->keys ++] = hid_code;
|
||||
} else
|
||||
return;
|
||||
}
|
||||
usb_wakeup(&us->dev);
|
||||
}
|
||||
|
||||
static inline int int_clamp(int val, int vmin, int vmax)
|
||||
static void usb_hid_handle_reset(USBDevice *dev)
|
||||
{
|
||||
if (val < vmin)
|
||||
return vmin;
|
||||
else if (val > vmax)
|
||||
return vmax;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
|
||||
|
||||
static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len)
|
||||
{
|
||||
int dx, dy, dz, b, l;
|
||||
int index;
|
||||
USBMouseState *s = &hs->ptr;
|
||||
USBPointerEvent *e;
|
||||
|
||||
if (!s->mouse_grabbed) {
|
||||
qemu_activate_mouse_event_handler(s->eh_entry);
|
||||
s->mouse_grabbed = 1;
|
||||
}
|
||||
|
||||
/* When the buffer is empty, return the last event. Relative
|
||||
movements will all be zero. */
|
||||
index = (hs->n ? hs->head : hs->head - 1);
|
||||
e = &s->queue[index & QUEUE_MASK];
|
||||
|
||||
if (hs->kind == USB_MOUSE) {
|
||||
dx = int_clamp(e->xdx, -127, 127);
|
||||
dy = int_clamp(e->ydy, -127, 127);
|
||||
e->xdx -= dx;
|
||||
e->ydy -= dy;
|
||||
} else {
|
||||
dx = e->xdx;
|
||||
dy = e->ydy;
|
||||
}
|
||||
dz = int_clamp(e->dz, -127, 127);
|
||||
e->dz -= dz;
|
||||
|
||||
b = 0;
|
||||
if (e->buttons_state & MOUSE_EVENT_LBUTTON)
|
||||
b |= 0x01;
|
||||
if (e->buttons_state & MOUSE_EVENT_RBUTTON)
|
||||
b |= 0x02;
|
||||
if (e->buttons_state & MOUSE_EVENT_MBUTTON)
|
||||
b |= 0x04;
|
||||
|
||||
if (hs->n &&
|
||||
!e->dz &&
|
||||
(hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) {
|
||||
/* that deals with this event */
|
||||
QUEUE_INCR(hs->head);
|
||||
hs->n--;
|
||||
}
|
||||
|
||||
/* Appears we have to invert the wheel direction */
|
||||
dz = 0 - dz;
|
||||
l = 0;
|
||||
switch (hs->kind) {
|
||||
case USB_MOUSE:
|
||||
if (len > l)
|
||||
buf[l++] = b;
|
||||
if (len > l)
|
||||
buf[l++] = dx;
|
||||
if (len > l)
|
||||
buf[l++] = dy;
|
||||
if (len > l)
|
||||
buf[l++] = dz;
|
||||
break;
|
||||
|
||||
case USB_TABLET:
|
||||
if (len > l)
|
||||
buf[l++] = b;
|
||||
if (len > l)
|
||||
buf[l++] = dx & 0xff;
|
||||
if (len > l)
|
||||
buf[l++] = dx >> 8;
|
||||
if (len > l)
|
||||
buf[l++] = dy & 0xff;
|
||||
if (len > l)
|
||||
buf[l++] = dy >> 8;
|
||||
if (len > l)
|
||||
buf[l++] = dz;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len)
|
||||
{
|
||||
USBKeyboardState *s = &hs->kbd;
|
||||
if (len < 2)
|
||||
return 0;
|
||||
|
||||
usb_keyboard_process_keycode(hs);
|
||||
|
||||
buf[0] = s->modifiers & 0xff;
|
||||
buf[1] = 0;
|
||||
if (s->keys > 6)
|
||||
memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
|
||||
else
|
||||
memcpy(buf + 2, s->key, MIN(8, len) - 2);
|
||||
|
||||
return MIN(8, len);
|
||||
}
|
||||
|
||||
static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
|
||||
{
|
||||
if (len > 0) {
|
||||
int ledstate = 0;
|
||||
/* 0x01: Num Lock LED
|
||||
* 0x02: Caps Lock LED
|
||||
* 0x04: Scroll Lock LED
|
||||
* 0x08: Compose LED
|
||||
* 0x10: Kana LED */
|
||||
s->leds = buf[0];
|
||||
if (s->leds & 0x04)
|
||||
ledstate |= QEMU_SCROLL_LOCK_LED;
|
||||
if (s->leds & 0x01)
|
||||
ledstate |= QEMU_NUM_LOCK_LED;
|
||||
if (s->leds & 0x02)
|
||||
ledstate |= QEMU_CAPS_LOCK_LED;
|
||||
kbd_put_ledstate(ledstate);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_mouse_handle_reset(USBDevice *dev)
|
||||
{
|
||||
USBHIDState *s = (USBHIDState *)dev;
|
||||
|
||||
memset(s->ptr.queue, 0, sizeof (s->ptr.queue));
|
||||
s->head = 0;
|
||||
s->n = 0;
|
||||
s->protocol = 1;
|
||||
}
|
||||
|
||||
static void usb_keyboard_handle_reset(USBDevice *dev)
|
||||
{
|
||||
USBHIDState *s = (USBHIDState *)dev;
|
||||
|
||||
qemu_add_kbd_event_handler(usb_keyboard_event, s);
|
||||
memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes));
|
||||
s->head = 0;
|
||||
s->n = 0;
|
||||
memset(s->kbd.key, 0, sizeof (s->kbd.key));
|
||||
s->kbd.keys = 0;
|
||||
s->protocol = 1;
|
||||
}
|
||||
|
||||
static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime)
|
||||
{
|
||||
s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000;
|
||||
hid_reset(&us->hid);
|
||||
}
|
||||
|
||||
static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBHIDState *s = (USBHIDState *)dev;
|
||||
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
|
||||
HIDState *hs = &us->hid;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
@ -740,7 +389,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch(request) {
|
||||
switch (request) {
|
||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
@ -750,17 +399,17 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
break;
|
||||
/* hid specific requests */
|
||||
case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
|
||||
switch(value >> 8) {
|
||||
switch (value >> 8) {
|
||||
case 0x22:
|
||||
if (s->kind == USB_MOUSE) {
|
||||
if (hs->kind == HID_MOUSE) {
|
||||
memcpy(data, qemu_mouse_hid_report_descriptor,
|
||||
sizeof(qemu_mouse_hid_report_descriptor));
|
||||
ret = sizeof(qemu_mouse_hid_report_descriptor);
|
||||
} else if (s->kind == USB_TABLET) {
|
||||
memcpy(data, qemu_tablet_hid_report_descriptor,
|
||||
} else if (hs->kind == HID_TABLET) {
|
||||
memcpy(data, qemu_tablet_hid_report_descriptor,
|
||||
sizeof(qemu_tablet_hid_report_descriptor));
|
||||
ret = sizeof(qemu_tablet_hid_report_descriptor);
|
||||
} else if (s->kind == USB_KEYBOARD) {
|
||||
} else if (hs->kind == HID_KEYBOARD) {
|
||||
memcpy(data, qemu_keyboard_hid_report_descriptor,
|
||||
sizeof(qemu_keyboard_hid_report_descriptor));
|
||||
ret = sizeof(qemu_keyboard_hid_report_descriptor);
|
||||
@ -771,38 +420,40 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
}
|
||||
break;
|
||||
case GET_REPORT:
|
||||
if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
|
||||
ret = usb_pointer_poll(s, data, length);
|
||||
} else if (s->kind == USB_KEYBOARD) {
|
||||
ret = usb_keyboard_poll(s, data, length);
|
||||
if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
|
||||
ret = hid_pointer_poll(hs, data, length);
|
||||
} else if (hs->kind == HID_KEYBOARD) {
|
||||
ret = hid_keyboard_poll(hs, data, length);
|
||||
}
|
||||
s->changed = s->n > 0;
|
||||
break;
|
||||
case SET_REPORT:
|
||||
if (s->kind == USB_KEYBOARD)
|
||||
ret = usb_keyboard_write(&s->kbd, data, length);
|
||||
else
|
||||
if (hs->kind == HID_KEYBOARD) {
|
||||
ret = hid_keyboard_write(hs, data, length);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case GET_PROTOCOL:
|
||||
if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE)
|
||||
if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 1;
|
||||
data[0] = s->protocol;
|
||||
data[0] = hs->protocol;
|
||||
break;
|
||||
case SET_PROTOCOL:
|
||||
if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE)
|
||||
if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
s->protocol = value;
|
||||
hs->protocol = value;
|
||||
break;
|
||||
case GET_IDLE:
|
||||
ret = 1;
|
||||
data[0] = s->idle;
|
||||
data[0] = hs->idle;
|
||||
break;
|
||||
case SET_IDLE:
|
||||
s->idle = (uint8_t) (value >> 8);
|
||||
usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock));
|
||||
hs->idle = (uint8_t) (value >> 8);
|
||||
hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock));
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
@ -815,23 +466,26 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
||||
|
||||
static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHIDState *s = (USBHIDState *)dev;
|
||||
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
|
||||
HIDState *hs = &us->hid;
|
||||
uint8_t buf[p->iov.size];
|
||||
int ret = 0;
|
||||
|
||||
switch(p->pid) {
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_IN:
|
||||
if (p->devep == 1) {
|
||||
int64_t curtime = qemu_get_clock_ns(vm_clock);
|
||||
if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0))
|
||||
if (!hid_has_events(hs) &&
|
||||
(!hs->idle || hs->next_idle_clock - curtime > 0)) {
|
||||
return USB_RET_NAK;
|
||||
usb_hid_set_next_idle(s, curtime);
|
||||
if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
|
||||
ret = usb_pointer_poll(s, p->data, p->len);
|
||||
}
|
||||
else if (s->kind == USB_KEYBOARD) {
|
||||
ret = usb_keyboard_poll(s, p->data, p->len);
|
||||
hid_set_next_idle(hs, curtime);
|
||||
if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
|
||||
ret = hid_pointer_poll(hs, buf, p->iov.size);
|
||||
} else if (hs->kind == HID_KEYBOARD) {
|
||||
ret = hid_keyboard_poll(hs, buf, p->iov.size);
|
||||
}
|
||||
s->changed = s->n > 0;
|
||||
usb_packet_copy(p, buf, ret);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
@ -847,50 +501,33 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
|
||||
|
||||
static void usb_hid_handle_destroy(USBDevice *dev)
|
||||
{
|
||||
USBHIDState *s = (USBHIDState *)dev;
|
||||
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
|
||||
|
||||
switch(s->kind) {
|
||||
case USB_KEYBOARD:
|
||||
qemu_remove_kbd_event_handler();
|
||||
break;
|
||||
default:
|
||||
qemu_remove_mouse_event_handler(s->ptr.eh_entry);
|
||||
}
|
||||
hid_free(&us->hid);
|
||||
}
|
||||
|
||||
static int usb_hid_initfn(USBDevice *dev, int kind)
|
||||
{
|
||||
USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
|
||||
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
|
||||
|
||||
usb_desc_init(dev);
|
||||
s->kind = kind;
|
||||
|
||||
if (s->kind == USB_MOUSE) {
|
||||
s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s,
|
||||
0, "QEMU USB Mouse");
|
||||
} else if (s->kind == USB_TABLET) {
|
||||
s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s,
|
||||
1, "QEMU USB Tablet");
|
||||
}
|
||||
|
||||
/* Force poll routine to be run and grab input the first time. */
|
||||
s->changed = 1;
|
||||
hid_init(&us->hid, kind, usb_hid_changed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_tablet_initfn(USBDevice *dev)
|
||||
{
|
||||
return usb_hid_initfn(dev, USB_TABLET);
|
||||
return usb_hid_initfn(dev, HID_TABLET);
|
||||
}
|
||||
|
||||
static int usb_mouse_initfn(USBDevice *dev)
|
||||
{
|
||||
return usb_hid_initfn(dev, USB_MOUSE);
|
||||
return usb_hid_initfn(dev, HID_MOUSE);
|
||||
}
|
||||
|
||||
static int usb_keyboard_initfn(USBDevice *dev)
|
||||
{
|
||||
return usb_hid_initfn(dev, USB_KEYBOARD);
|
||||
return usb_hid_initfn(dev, HID_KEYBOARD);
|
||||
}
|
||||
|
||||
void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *))
|
||||
@ -905,8 +542,8 @@ static int usb_hid_post_load(void *opaque, int version_id)
|
||||
{
|
||||
USBHIDState *s = opaque;
|
||||
|
||||
if (s->idle) {
|
||||
usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock));
|
||||
if (s->hid.idle) {
|
||||
hid_set_next_idle(&s->hid, qemu_get_clock_ns(vm_clock));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -916,10 +553,10 @@ static const VMStateDescription vmstate_usb_ptr_queue = {
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_INT32(xdx, USBPointerEvent),
|
||||
VMSTATE_INT32(ydy, USBPointerEvent),
|
||||
VMSTATE_INT32(dz, USBPointerEvent),
|
||||
VMSTATE_INT32(buttons_state, USBPointerEvent),
|
||||
VMSTATE_INT32(xdx, HIDPointerEvent),
|
||||
VMSTATE_INT32(ydy, HIDPointerEvent),
|
||||
VMSTATE_INT32(dz, HIDPointerEvent),
|
||||
VMSTATE_INT32(buttons_state, HIDPointerEvent),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
@ -930,12 +567,12 @@ static const VMStateDescription vmstate_usb_ptr = {
|
||||
.post_load = usb_hid_post_load,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_USB_DEVICE(dev, USBHIDState),
|
||||
VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0,
|
||||
vmstate_usb_ptr_queue, USBPointerEvent),
|
||||
VMSTATE_UINT32(head, USBHIDState),
|
||||
VMSTATE_UINT32(n, USBHIDState),
|
||||
VMSTATE_INT32(protocol, USBHIDState),
|
||||
VMSTATE_UINT8(idle, USBHIDState),
|
||||
VMSTATE_STRUCT_ARRAY(hid.ptr.queue, USBHIDState, QUEUE_LENGTH, 0,
|
||||
vmstate_usb_ptr_queue, HIDPointerEvent),
|
||||
VMSTATE_UINT32(hid.head, USBHIDState),
|
||||
VMSTATE_UINT32(hid.n, USBHIDState),
|
||||
VMSTATE_INT32(hid.protocol, USBHIDState),
|
||||
VMSTATE_UINT8(hid.idle, USBHIDState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
@ -947,15 +584,15 @@ static const VMStateDescription vmstate_usb_kbd = {
|
||||
.post_load = usb_hid_post_load,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_USB_DEVICE(dev, USBHIDState),
|
||||
VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH),
|
||||
VMSTATE_UINT32(head, USBHIDState),
|
||||
VMSTATE_UINT32(n, USBHIDState),
|
||||
VMSTATE_UINT16(kbd.modifiers, USBHIDState),
|
||||
VMSTATE_UINT8(kbd.leds, USBHIDState),
|
||||
VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16),
|
||||
VMSTATE_INT32(kbd.keys, USBHIDState),
|
||||
VMSTATE_INT32(protocol, USBHIDState),
|
||||
VMSTATE_UINT8(idle, USBHIDState),
|
||||
VMSTATE_UINT32_ARRAY(hid.kbd.keycodes, USBHIDState, QUEUE_LENGTH),
|
||||
VMSTATE_UINT32(hid.head, USBHIDState),
|
||||
VMSTATE_UINT32(hid.n, USBHIDState),
|
||||
VMSTATE_UINT16(hid.kbd.modifiers, USBHIDState),
|
||||
VMSTATE_UINT8(hid.kbd.leds, USBHIDState),
|
||||
VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16),
|
||||
VMSTATE_INT32(hid.kbd.keys, USBHIDState),
|
||||
VMSTATE_INT32(hid.protocol, USBHIDState),
|
||||
VMSTATE_UINT8(hid.idle, USBHIDState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
@ -970,7 +607,7 @@ static struct USBDeviceInfo hid_info[] = {
|
||||
.usb_desc = &desc_tablet,
|
||||
.init = usb_tablet_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_mouse_handle_reset,
|
||||
.handle_reset = usb_hid_handle_reset,
|
||||
.handle_control = usb_hid_handle_control,
|
||||
.handle_data = usb_hid_handle_data,
|
||||
.handle_destroy = usb_hid_handle_destroy,
|
||||
@ -983,7 +620,7 @@ static struct USBDeviceInfo hid_info[] = {
|
||||
.usb_desc = &desc_mouse,
|
||||
.init = usb_mouse_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_mouse_handle_reset,
|
||||
.handle_reset = usb_hid_handle_reset,
|
||||
.handle_control = usb_hid_handle_control,
|
||||
.handle_data = usb_hid_handle_data,
|
||||
.handle_destroy = usb_hid_handle_destroy,
|
||||
@ -996,7 +633,7 @@ static struct USBDeviceInfo hid_info[] = {
|
||||
.usb_desc = &desc_keyboard,
|
||||
.init = usb_keyboard_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_keyboard_handle_reset,
|
||||
.handle_reset = usb_hid_handle_reset,
|
||||
.handle_control = usb_hid_handle_control,
|
||||
.handle_data = usb_hid_handle_data,
|
||||
.handle_destroy = usb_hid_handle_destroy,
|
||||
|
@ -394,11 +394,12 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
||||
if (p->devep == 1) {
|
||||
USBHubPort *port;
|
||||
unsigned int status;
|
||||
uint8_t buf[4];
|
||||
int i, n;
|
||||
n = (NUM_PORTS + 1 + 7) / 8;
|
||||
if (p->len == 1) { /* FreeBSD workaround */
|
||||
if (p->iov.size == 1) { /* FreeBSD workaround */
|
||||
n = 1;
|
||||
} else if (n > p->len) {
|
||||
} else if (n > p->iov.size) {
|
||||
return USB_RET_BABBLE;
|
||||
}
|
||||
status = 0;
|
||||
@ -409,8 +410,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
if (status != 0) {
|
||||
for(i = 0; i < n; i++) {
|
||||
p->data[i] = status >> (8 * i);
|
||||
buf[i] = status >> (8 * i);
|
||||
}
|
||||
usb_packet_copy(p, buf, n);
|
||||
ret = n;
|
||||
} else {
|
||||
ret = USB_RET_NAK; /* usb11 11.13.1 */
|
||||
|
63
hw/usb-libhw.c
Normal file
63
hw/usb-libhw.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* QEMU USB emulation, libhw bits.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "cpu-common.h"
|
||||
#include "usb.h"
|
||||
#include "dma.h"
|
||||
|
||||
int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
|
||||
{
|
||||
int is_write = (p->pid == USB_TOKEN_IN);
|
||||
target_phys_addr_t len;
|
||||
void *mem;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sgl->nsg; i++) {
|
||||
len = sgl->sg[i].len;
|
||||
mem = cpu_physical_memory_map(sgl->sg[i].base, &len,
|
||||
is_write);
|
||||
if (!mem) {
|
||||
goto err;
|
||||
}
|
||||
qemu_iovec_add(&p->iov, mem, len);
|
||||
if (len != sgl->sg[i].len) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
usb_packet_unmap(p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_packet_unmap(USBPacket *p)
|
||||
{
|
||||
int is_write = (p->pid == USB_TOKEN_IN);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < p->iov.niov; i++) {
|
||||
cpu_physical_memory_unmap(p->iov.iov[i].iov_base,
|
||||
p->iov.iov[i].iov_len, is_write,
|
||||
p->iov.iov[i].iov_len);
|
||||
}
|
||||
}
|
109
hw/usb-msd.c
109
hw/usb-msd.c
@ -43,8 +43,6 @@ typedef struct {
|
||||
enum USBMSDMode mode;
|
||||
uint32_t scsi_len;
|
||||
uint8_t *scsi_buf;
|
||||
uint32_t usb_len;
|
||||
uint8_t *usb_buf;
|
||||
uint32_t data_len;
|
||||
uint32_t residue;
|
||||
uint32_t tag;
|
||||
@ -176,20 +174,14 @@ static const USBDesc desc = {
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
static void usb_msd_copy_data(MSDState *s)
|
||||
static void usb_msd_copy_data(MSDState *s, USBPacket *p)
|
||||
{
|
||||
uint32_t len;
|
||||
len = s->usb_len;
|
||||
len = p->iov.size - p->result;
|
||||
if (len > s->scsi_len)
|
||||
len = s->scsi_len;
|
||||
if (s->mode == USB_MSDM_DATAIN) {
|
||||
memcpy(s->usb_buf, s->scsi_buf, len);
|
||||
} else {
|
||||
memcpy(s->scsi_buf, s->usb_buf, len);
|
||||
}
|
||||
s->usb_len -= len;
|
||||
usb_packet_copy(p, s->scsi_buf, len);
|
||||
s->scsi_len -= len;
|
||||
s->usb_buf += len;
|
||||
s->scsi_buf += len;
|
||||
s->data_len -= len;
|
||||
if (s->scsi_len == 0 || s->data_len == 0) {
|
||||
@ -207,8 +199,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
|
||||
csw.residue = s->residue;
|
||||
csw.status = s->result;
|
||||
|
||||
len = MIN(sizeof(csw), p->len);
|
||||
memcpy(p->data, &csw, len);
|
||||
len = MIN(sizeof(csw), p->iov.size);
|
||||
usb_packet_copy(p, &csw, len);
|
||||
p->result = len;
|
||||
}
|
||||
|
||||
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
@ -220,8 +213,9 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
s->scsi_len = len;
|
||||
s->scsi_buf = scsi_req_get_buf(req);
|
||||
if (p) {
|
||||
usb_msd_copy_data(s);
|
||||
if (s->packet && s->usb_len == 0) {
|
||||
usb_msd_copy_data(s, p);
|
||||
p = s->packet;
|
||||
if (p && p->result == p->iov.size) {
|
||||
/* Set s->packet to NULL before calling usb_packet_complete
|
||||
because another request may be issued before
|
||||
usb_packet_complete returns. */
|
||||
@ -248,11 +242,9 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
|
||||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
if (s->data_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
if (s->mode == USB_MSDM_DATAIN) {
|
||||
memset(s->usb_buf, 0, s->usb_len);
|
||||
}
|
||||
s->usb_len = 0;
|
||||
int len = (p->iov.size - p->result);
|
||||
usb_packet_skip(p, len);
|
||||
s->data_len -= len;
|
||||
}
|
||||
if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
@ -342,8 +334,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
int ret = 0;
|
||||
struct usb_msd_cbw cbw;
|
||||
uint8_t devep = p->devep;
|
||||
uint8_t *data = p->data;
|
||||
int len = p->len;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
@ -352,11 +342,11 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
|
||||
switch (s->mode) {
|
||||
case USB_MSDM_CBW:
|
||||
if (len != 31) {
|
||||
if (p->iov.size != 31) {
|
||||
fprintf(stderr, "usb-msd: Bad CBW size");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(&cbw, data, 31);
|
||||
usb_packet_copy(p, &cbw, 31);
|
||||
if (le32_to_cpu(cbw.sig) != 0x43425355) {
|
||||
fprintf(stderr, "usb-msd: Bad signature %08x\n",
|
||||
le32_to_cpu(cbw.sig));
|
||||
@ -387,36 +377,39 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
if (s->mode != USB_MSDM_CSW && s->residue == 0) {
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
ret = len;
|
||||
ret = p->result;
|
||||
break;
|
||||
|
||||
case USB_MSDM_DATAOUT:
|
||||
DPRINTF("Data out %d/%d\n", len, s->data_len);
|
||||
if (len > s->data_len)
|
||||
DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len);
|
||||
if (p->iov.size > s->data_len) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->usb_buf = data;
|
||||
s->usb_len = len;
|
||||
if (s->scsi_len) {
|
||||
usb_msd_copy_data(s);
|
||||
usb_msd_copy_data(s, p);
|
||||
}
|
||||
if (s->residue && s->usb_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
if (s->data_len == 0)
|
||||
s->mode = USB_MSDM_CSW;
|
||||
s->usb_len = 0;
|
||||
if (s->residue) {
|
||||
int len = p->iov.size - p->result;
|
||||
if (len) {
|
||||
usb_packet_skip(p, len);
|
||||
s->data_len -= len;
|
||||
if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s->usb_len) {
|
||||
if (p->result < p->iov.size) {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
ret = len;
|
||||
ret = p->result;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTF("Unexpected write (len %d)\n", len);
|
||||
DPRINTF("Unexpected write (len %zd)\n", p->iov.size);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
@ -427,18 +420,20 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
|
||||
switch (s->mode) {
|
||||
case USB_MSDM_DATAOUT:
|
||||
if (s->data_len != 0 || len < 13)
|
||||
if (s->data_len != 0 || p->iov.size < 13) {
|
||||
goto fail;
|
||||
}
|
||||
/* Waiting for SCSI write to complete. */
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
break;
|
||||
|
||||
case USB_MSDM_CSW:
|
||||
DPRINTF("Command status %d tag 0x%x, len %d\n",
|
||||
s->result, s->tag, len);
|
||||
if (len < 13)
|
||||
DPRINTF("Command status %d tag 0x%x, len %zd\n",
|
||||
s->result, s->tag, p->iov.size);
|
||||
if (p->iov.size < 13) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
usb_msd_send_status(s, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
@ -446,32 +441,32 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
break;
|
||||
|
||||
case USB_MSDM_DATAIN:
|
||||
DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len);
|
||||
if (len > s->data_len)
|
||||
len = s->data_len;
|
||||
s->usb_buf = data;
|
||||
s->usb_len = len;
|
||||
DPRINTF("Data in %zd/%d, scsi_len %d\n",
|
||||
p->iov.size, s->data_len, s->scsi_len);
|
||||
if (s->scsi_len) {
|
||||
usb_msd_copy_data(s);
|
||||
usb_msd_copy_data(s, p);
|
||||
}
|
||||
if (s->residue && s->usb_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
memset(s->usb_buf, 0, s->usb_len);
|
||||
if (s->data_len == 0)
|
||||
s->mode = USB_MSDM_CSW;
|
||||
s->usb_len = 0;
|
||||
if (s->residue) {
|
||||
int len = p->iov.size - p->result;
|
||||
if (len) {
|
||||
usb_packet_skip(p, len);
|
||||
s->data_len -= len;
|
||||
if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s->usb_len) {
|
||||
if (p->result < p->iov.size) {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
ret = len;
|
||||
ret = p->result;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTF("Unexpected read (len %d)\n", len);
|
||||
DPRINTF("Unexpected read (len %zd)\n", p->iov.size);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
@ -365,6 +365,8 @@ struct MUSBState *musb_init(qemu_irq *irqs)
|
||||
s->ep[i].maxp[1] = 0x40;
|
||||
s->ep[i].musb = s;
|
||||
s->ep[i].epnum = i;
|
||||
usb_packet_init(&s->ep[i].packey[0].p);
|
||||
usb_packet_init(&s->ep[i].packey[1].p);
|
||||
}
|
||||
|
||||
usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */);
|
||||
@ -605,12 +607,10 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||
ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
|
||||
ep->delayed_cb[dir] = cb;
|
||||
|
||||
ep->packey[dir].p.pid = pid;
|
||||
/* A wild guess on the FADDR semantics... */
|
||||
ep->packey[dir].p.devaddr = ep->faddr[idx];
|
||||
ep->packey[dir].p.devep = ep->type[idx] & 0xf;
|
||||
ep->packey[dir].p.data = (void *) ep->buf[idx];
|
||||
ep->packey[dir].p.len = len;
|
||||
usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx],
|
||||
ep->type[idx] & 0xf);
|
||||
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
|
||||
ep->packey[dir].ep = ep;
|
||||
ep->packey[dir].dir = dir;
|
||||
|
||||
@ -738,7 +738,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
|
||||
|
||||
if (ep->status[1] == USB_RET_STALL) {
|
||||
ep->status[1] = 0;
|
||||
packey->len = 0;
|
||||
packey->result = 0;
|
||||
|
||||
ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
|
||||
if (!epnum)
|
||||
@ -752,7 +752,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
|
||||
* Data-errors in Isochronous. */
|
||||
if (ep->interrupt[1])
|
||||
return musb_packet(s, ep, epnum, USB_TOKEN_IN,
|
||||
packey->len, musb_rx_packet_complete, 1);
|
||||
packey->iov.size, musb_rx_packet_complete, 1);
|
||||
|
||||
ep->csr[1] |= MGC_M_RXCSR_DATAERROR;
|
||||
if (!epnum)
|
||||
@ -777,14 +777,14 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
|
||||
/* TODO: check len for over/underruns of an OUT packet? */
|
||||
/* TODO: perhaps make use of e->ext_size[1] here. */
|
||||
|
||||
packey->len = ep->status[1];
|
||||
packey->result = ep->status[1];
|
||||
|
||||
if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
|
||||
ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
|
||||
if (!epnum)
|
||||
ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
|
||||
|
||||
ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */
|
||||
ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */
|
||||
/* In DMA mode: assert DMA request for this EP */
|
||||
}
|
||||
|
||||
@ -856,12 +856,12 @@ static void musb_rx_req(MUSBState *s, int epnum)
|
||||
* 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
|
||||
if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
|
||||
(ep->fifostart[1]) + ep->rxcount <
|
||||
ep->packey[1].p.len) {
|
||||
ep->packey[1].p.iov.size) {
|
||||
TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount );
|
||||
ep->fifostart[1] += ep->rxcount;
|
||||
ep->fifolen[1] = 0;
|
||||
|
||||
ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]),
|
||||
ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]),
|
||||
ep->maxp[1]);
|
||||
|
||||
ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
|
||||
|
65
hw/usb-net.c
65
hw/usb-net.c
@ -29,6 +29,7 @@
|
||||
#include "net.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "sysemu.h"
|
||||
#include "iov.h"
|
||||
|
||||
/*#define TRAFFIC_DEBUG*/
|
||||
/* Thanks to NetChip Technologies for donating this product ID.
|
||||
@ -1121,28 +1122,23 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
|
||||
|
||||
static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
|
||||
{
|
||||
le32 buf[2];
|
||||
int ret = 8;
|
||||
|
||||
if (p->len < 8)
|
||||
if (p->iov.size < 8) {
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
|
||||
((le32 *) p->data)[0] = cpu_to_le32(1);
|
||||
((le32 *) p->data)[1] = cpu_to_le32(0);
|
||||
buf[0] = cpu_to_le32(1);
|
||||
buf[1] = cpu_to_le32(0);
|
||||
usb_packet_copy(p, buf, 8);
|
||||
if (!s->rndis_resp.tqh_first)
|
||||
ret = USB_RET_NAK;
|
||||
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret);
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, ":");
|
||||
for (i = 0; i < ret; i++) {
|
||||
if (!(i & 15))
|
||||
fprintf(stderr, "\n%04x:", i);
|
||||
fprintf(stderr, " %02x", p->data[i]);
|
||||
}
|
||||
fprintf(stderr, "\n\n");
|
||||
}
|
||||
fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
|
||||
p->iov.size, ret);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
@ -1162,9 +1158,10 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
|
||||
return ret;
|
||||
}
|
||||
ret = s->in_len - s->in_ptr;
|
||||
if (ret > p->len)
|
||||
ret = p->len;
|
||||
memcpy(p->data, &s->in_buf[s->in_ptr], ret);
|
||||
if (ret > p->iov.size) {
|
||||
ret = p->iov.size;
|
||||
}
|
||||
usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
|
||||
s->in_ptr += ret;
|
||||
if (s->in_ptr >= s->in_len &&
|
||||
(is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
|
||||
@ -1173,17 +1170,8 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
|
||||
}
|
||||
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret);
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, ":");
|
||||
for (i = 0; i < ret; i++) {
|
||||
if (!(i & 15))
|
||||
fprintf(stderr, "\n%04x:", i);
|
||||
fprintf(stderr, " %02x", p->data[i]);
|
||||
}
|
||||
fprintf(stderr, "\n\n");
|
||||
}
|
||||
fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
@ -1191,29 +1179,20 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
|
||||
|
||||
static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
|
||||
{
|
||||
int ret = p->len;
|
||||
int ret = p->iov.size;
|
||||
int sz = sizeof(s->out_buf) - s->out_ptr;
|
||||
struct rndis_packet_msg_type *msg =
|
||||
(struct rndis_packet_msg_type *) s->out_buf;
|
||||
uint32_t len;
|
||||
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
fprintf(stderr, "usbnet: data out len %u\n", p->len);
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, ":");
|
||||
for (i = 0; i < p->len; i++) {
|
||||
if (!(i & 15))
|
||||
fprintf(stderr, "\n%04x:", i);
|
||||
fprintf(stderr, " %02x", p->data[i]);
|
||||
}
|
||||
fprintf(stderr, "\n\n");
|
||||
}
|
||||
fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
|
||||
#endif
|
||||
|
||||
if (sz > ret)
|
||||
sz = ret;
|
||||
memcpy(&s->out_buf[s->out_ptr], p->data, sz);
|
||||
usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
|
||||
s->out_ptr += sz;
|
||||
|
||||
if (!is_rndis(s)) {
|
||||
@ -1277,8 +1256,8 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
if (ret == USB_RET_STALL)
|
||||
fprintf(stderr, "usbnet: failed data transaction: "
|
||||
"pid 0x%x ep 0x%x len 0x%x\n",
|
||||
p->pid, p->devep, p->len);
|
||||
"pid 0x%x ep 0x%x len 0x%zx\n",
|
||||
p->pid, p->devep, p->iov.size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -777,18 +777,17 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||
}
|
||||
|
||||
if (completion) {
|
||||
ret = ohci->usb_packet.len;
|
||||
ret = ohci->usb_packet.result;
|
||||
} else {
|
||||
ret = USB_RET_NODEV;
|
||||
for (i = 0; i < ohci->num_ports; i++) {
|
||||
dev = ohci->rhport[i].port.dev;
|
||||
if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
|
||||
continue;
|
||||
ohci->usb_packet.pid = pid;
|
||||
ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
|
||||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
usb_packet_setup(&ohci->usb_packet, pid,
|
||||
OHCI_BM(ed->flags, ED_FA),
|
||||
OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
@ -959,7 +958,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
}
|
||||
#endif
|
||||
if (completion) {
|
||||
ret = ohci->usb_packet.len;
|
||||
ret = ohci->usb_packet.result;
|
||||
ohci->async_td = 0;
|
||||
ohci->async_complete = 0;
|
||||
} else {
|
||||
@ -980,11 +979,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
ohci->usb_packet.pid = pid;
|
||||
ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
|
||||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
usb_packet_setup(&ohci->usb_packet, pid,
|
||||
OHCI_BM(ed->flags, ED_FA),
|
||||
OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
@ -1761,6 +1759,7 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
ohci->localmem_base = localmem_base;
|
||||
|
||||
ohci->name = dev->info->name;
|
||||
usb_packet_init(&ohci->usb_packet);
|
||||
|
||||
ohci->async_td = 0;
|
||||
qemu_register_reset(ohci_reset, ohci);
|
||||
|
@ -359,37 +359,42 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
|
||||
static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBSerialState *s = (USBSerialState *)dev;
|
||||
int ret = 0;
|
||||
int i, ret = 0;
|
||||
uint8_t devep = p->devep;
|
||||
uint8_t *data = p->data;
|
||||
int len = p->len;
|
||||
int first_len;
|
||||
struct iovec *iov;
|
||||
uint8_t header[2];
|
||||
int first_len, len;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
if (devep != 2)
|
||||
goto fail;
|
||||
qemu_chr_write(s->cs, data, len);
|
||||
for (i = 0; i < p->iov.niov; i++) {
|
||||
iov = p->iov.iov + i;
|
||||
qemu_chr_write(s->cs, iov->iov_base, iov->iov_len);
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_TOKEN_IN:
|
||||
if (devep != 1)
|
||||
goto fail;
|
||||
first_len = RECV_BUF - s->recv_ptr;
|
||||
len = p->iov.size;
|
||||
if (len <= 2) {
|
||||
ret = USB_RET_NAK;
|
||||
break;
|
||||
}
|
||||
*data++ = usb_get_modem_lines(s) | 1;
|
||||
header[0] = usb_get_modem_lines(s) | 1;
|
||||
/* We do not have the uart details */
|
||||
/* handle serial break */
|
||||
if (s->event_trigger && s->event_trigger & FTDI_BI) {
|
||||
s->event_trigger &= ~FTDI_BI;
|
||||
*data = FTDI_BI;
|
||||
header[1] = FTDI_BI;
|
||||
usb_packet_copy(p, header, 2);
|
||||
ret = 2;
|
||||
break;
|
||||
} else {
|
||||
*data++ = 0;
|
||||
header[1] = 0;
|
||||
}
|
||||
len -= 2;
|
||||
if (len > s->recv_used)
|
||||
@ -400,9 +405,10 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
if (first_len > len)
|
||||
first_len = len;
|
||||
memcpy(data, s->recv_buf + s->recv_ptr, first_len);
|
||||
usb_packet_copy(p, header, 2);
|
||||
usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len);
|
||||
if (len > first_len)
|
||||
memcpy(data + first_len, s->recv_buf, len - first_len);
|
||||
usb_packet_copy(p, s->recv_buf, len - first_len);
|
||||
s->recv_used -= len;
|
||||
s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
|
||||
ret = len + 2;
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "pci.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "usb-uhci.h"
|
||||
#include "iov.h"
|
||||
#include "dma.h"
|
||||
|
||||
//#define DEBUG
|
||||
//#define DEBUG_DUMP_DATA
|
||||
@ -93,17 +95,12 @@ static const char *pid2str(int pid)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_DUMP_DATA
|
||||
static void dump_data(const uint8_t *data, int len)
|
||||
static void dump_data(USBPacket *p, int ret)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("uhci: data: ");
|
||||
for(i = 0; i < len; i++)
|
||||
printf(" %02x", data[i]);
|
||||
printf("\n");
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret);
|
||||
}
|
||||
#else
|
||||
static void dump_data(const uint8_t *data, int len) {}
|
||||
static void dump_data(USBPacket *p, int ret) {}
|
||||
#endif
|
||||
|
||||
typedef struct UHCIState UHCIState;
|
||||
@ -115,6 +112,7 @@ typedef struct UHCIState UHCIState;
|
||||
*/
|
||||
typedef struct UHCIAsync {
|
||||
USBPacket packet;
|
||||
QEMUSGList sgl;
|
||||
UHCIState *uhci;
|
||||
QTAILQ_ENTRY(UHCIAsync) next;
|
||||
uint32_t td;
|
||||
@ -122,7 +120,6 @@ typedef struct UHCIAsync {
|
||||
int8_t valid;
|
||||
uint8_t isoc;
|
||||
uint8_t done;
|
||||
uint8_t buffer[2048];
|
||||
} UHCIAsync;
|
||||
|
||||
typedef struct UHCIPort {
|
||||
@ -179,12 +176,16 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s)
|
||||
async->token = 0;
|
||||
async->done = 0;
|
||||
async->isoc = 0;
|
||||
usb_packet_init(&async->packet);
|
||||
qemu_sglist_init(&async->sgl, 1);
|
||||
|
||||
return async;
|
||||
}
|
||||
|
||||
static void uhci_async_free(UHCIState *s, UHCIAsync *async)
|
||||
{
|
||||
usb_packet_cleanup(&async->packet);
|
||||
qemu_sglist_destroy(&async->sgl);
|
||||
qemu_free(async);
|
||||
}
|
||||
|
||||
@ -648,10 +649,10 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n",
|
||||
pid2str(p->pid), p->devaddr, p->devep, p->len);
|
||||
DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n",
|
||||
pid2str(p->pid), p->devaddr, p->devep, p->iov.size);
|
||||
if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
|
||||
dump_data(p->data, p->len);
|
||||
dump_data(p, 0);
|
||||
|
||||
ret = USB_RET_NODEV;
|
||||
for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) {
|
||||
@ -662,9 +663,9 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
|
||||
ret = usb_handle_packet(dev, p);
|
||||
}
|
||||
|
||||
DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
|
||||
DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size);
|
||||
if (p->pid == USB_TOKEN_IN && ret > 0)
|
||||
dump_data(p->data, ret);
|
||||
dump_data(p, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -684,7 +685,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
pid = td->token & 0xff;
|
||||
|
||||
ret = async->packet.len;
|
||||
ret = async->packet.result;
|
||||
|
||||
if (td->ctrl & TD_CTRL_IOS)
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
@ -692,7 +693,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
len = async->packet.len;
|
||||
len = async->packet.result;
|
||||
td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
|
||||
|
||||
/* The NAK bit may have been set by a previous frame, so clear it
|
||||
@ -708,11 +709,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
/* write the data back */
|
||||
cpu_physical_memory_write(td->buffer, async->buffer, len);
|
||||
}
|
||||
|
||||
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
|
||||
*int_mask |= 0x02;
|
||||
/* short packet: do not update QH */
|
||||
@ -827,16 +823,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
pid = td->token & 0xff;
|
||||
|
||||
async->packet.pid = pid;
|
||||
async->packet.devaddr = (td->token >> 8) & 0x7f;
|
||||
async->packet.devep = (td->token >> 15) & 0xf;
|
||||
async->packet.data = async->buffer;
|
||||
async->packet.len = max_len;
|
||||
usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f,
|
||||
(td->token >> 15) & 0xf);
|
||||
qemu_sglist_add(&async->sgl, td->buffer, max_len);
|
||||
usb_packet_map(&async->packet, &async->sgl);
|
||||
|
||||
switch(pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
cpu_physical_memory_read(td->buffer, async->buffer, max_len);
|
||||
len = uhci_broadcast_packet(s, &async->packet);
|
||||
if (len >= 0)
|
||||
len = max_len;
|
||||
@ -859,10 +853,11 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
|
||||
return 2;
|
||||
}
|
||||
|
||||
async->packet.len = len;
|
||||
async->packet.result = len;
|
||||
|
||||
done:
|
||||
len = uhci_complete_td(s, td, async, int_mask);
|
||||
usb_packet_unmap(&async->packet);
|
||||
uhci_async_free(s, async);
|
||||
return len;
|
||||
}
|
||||
|
@ -308,6 +308,7 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
|
||||
static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBWacomState *s = (USBWacomState *) dev;
|
||||
uint8_t buf[p->iov.size];
|
||||
int ret = 0;
|
||||
|
||||
switch (p->pid) {
|
||||
@ -317,9 +318,10 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
|
||||
return USB_RET_NAK;
|
||||
s->changed = 0;
|
||||
if (s->mode == WACOM_MODE_HID)
|
||||
ret = usb_mouse_poll(s, p->data, p->len);
|
||||
ret = usb_mouse_poll(s, buf, p->iov.size);
|
||||
else if (s->mode == WACOM_MODE_WACOM)
|
||||
ret = usb_wacom_poll(s, p->data, p->len);
|
||||
ret = usb_wacom_poll(s, buf, p->iov.size);
|
||||
usb_packet_copy(p, buf, ret);
|
||||
break;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
86
hw/usb.c
86
hw/usb.c
@ -25,6 +25,7 @@
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "usb.h"
|
||||
#include "iov.h"
|
||||
|
||||
void usb_attach(USBPort *port, USBDevice *dev)
|
||||
{
|
||||
@ -72,10 +73,11 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
|
||||
int request, value, index;
|
||||
int ret = 0;
|
||||
|
||||
if (p->len != 8)
|
||||
if (p->iov.size != 8) {
|
||||
return USB_RET_STALL;
|
||||
|
||||
memcpy(s->setup_buf, p->data, 8);
|
||||
}
|
||||
|
||||
usb_packet_copy(p, s->setup_buf, p->iov.size);
|
||||
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
|
||||
s->setup_index = 0;
|
||||
|
||||
@ -144,9 +146,10 @@ static int do_token_in(USBDevice *s, USBPacket *p)
|
||||
case SETUP_STATE_DATA:
|
||||
if (s->setup_buf[0] & USB_DIR_IN) {
|
||||
int len = s->setup_len - s->setup_index;
|
||||
if (len > p->len)
|
||||
len = p->len;
|
||||
memcpy(p->data, s->data_buf + s->setup_index, len);
|
||||
if (len > p->iov.size) {
|
||||
len = p->iov.size;
|
||||
}
|
||||
usb_packet_copy(p, s->data_buf + s->setup_index, len);
|
||||
s->setup_index += len;
|
||||
if (s->setup_index >= s->setup_len)
|
||||
s->setup_state = SETUP_STATE_ACK;
|
||||
@ -179,9 +182,10 @@ static int do_token_out(USBDevice *s, USBPacket *p)
|
||||
case SETUP_STATE_DATA:
|
||||
if (!(s->setup_buf[0] & USB_DIR_IN)) {
|
||||
int len = s->setup_len - s->setup_index;
|
||||
if (len > p->len)
|
||||
len = p->len;
|
||||
memcpy(s->data_buf + s->setup_index, p->data, len);
|
||||
if (len > p->iov.size) {
|
||||
len = p->iov.size;
|
||||
}
|
||||
usb_packet_copy(p, s->data_buf + s->setup_index, len);
|
||||
s->setup_index += len;
|
||||
if (s->setup_index >= s->setup_len)
|
||||
s->setup_state = SETUP_STATE_ACK;
|
||||
@ -251,22 +255,22 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
||||
usb_packet_complete to complete their async control packets. */
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
if (p->len < 0) {
|
||||
if (p->result < 0) {
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
}
|
||||
|
||||
switch (s->setup_state) {
|
||||
case SETUP_STATE_SETUP:
|
||||
if (p->len < s->setup_len) {
|
||||
s->setup_len = p->len;
|
||||
if (p->result < s->setup_len) {
|
||||
s->setup_len = p->result;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
p->len = 8;
|
||||
p->result = 8;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_ACK:
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
p->len = 0;
|
||||
p->result = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -347,3 +351,57 @@ void usb_cancel_packet(USBPacket * p)
|
||||
p->owner->info->cancel_packet(p->owner, p);
|
||||
p->owner = NULL;
|
||||
}
|
||||
|
||||
|
||||
void usb_packet_init(USBPacket *p)
|
||||
{
|
||||
qemu_iovec_init(&p->iov, 1);
|
||||
}
|
||||
|
||||
void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep)
|
||||
{
|
||||
p->pid = pid;
|
||||
p->devaddr = addr;
|
||||
p->devep = ep;
|
||||
p->result = 0;
|
||||
qemu_iovec_reset(&p->iov);
|
||||
}
|
||||
|
||||
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
|
||||
{
|
||||
qemu_iovec_add(&p->iov, ptr, len);
|
||||
}
|
||||
|
||||
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
|
||||
{
|
||||
assert(p->result >= 0);
|
||||
assert(p->result + bytes <= p->iov.size);
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
case USB_TOKEN_OUT:
|
||||
iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
|
||||
abort();
|
||||
}
|
||||
p->result += bytes;
|
||||
}
|
||||
|
||||
void usb_packet_skip(USBPacket *p, size_t bytes)
|
||||
{
|
||||
assert(p->result >= 0);
|
||||
assert(p->result + bytes <= p->iov.size);
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
iov_clear(p->iov.iov, p->iov.niov, p->result, bytes);
|
||||
}
|
||||
p->result += bytes;
|
||||
}
|
||||
|
||||
void usb_packet_cleanup(USBPacket *p)
|
||||
{
|
||||
qemu_iovec_destroy(&p->iov);
|
||||
}
|
||||
|
13
hw/usb.h
13
hw/usb.h
@ -285,12 +285,21 @@ struct USBPacket {
|
||||
int pid;
|
||||
uint8_t devaddr;
|
||||
uint8_t devep;
|
||||
uint8_t *data;
|
||||
int len;
|
||||
QEMUIOVector iov;
|
||||
int result; /* transfer length or USB_RET_* status code */
|
||||
/* Internal use by the USB layer. */
|
||||
USBDevice *owner;
|
||||
};
|
||||
|
||||
void usb_packet_init(USBPacket *p);
|
||||
void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep);
|
||||
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
|
||||
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
|
||||
void usb_packet_unmap(USBPacket *p);
|
||||
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
|
||||
void usb_packet_skip(USBPacket *p, size_t bytes);
|
||||
void usb_packet_cleanup(USBPacket *p);
|
||||
|
||||
int usb_handle_packet(USBDevice *dev, USBPacket *p);
|
||||
void usb_packet_complete(USBDevice *dev, USBPacket *p);
|
||||
void usb_cancel_packet(USBPacket * p);
|
||||
|
54
iov.c
54
iov.c
@ -62,6 +62,29 @@ size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
return buf_off;
|
||||
}
|
||||
|
||||
size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
size_t iov_off, size_t size)
|
||||
{
|
||||
size_t iovec_off, buf_off;
|
||||
unsigned int i;
|
||||
|
||||
iovec_off = 0;
|
||||
buf_off = 0;
|
||||
for (i = 0; i < iov_cnt && size; i++) {
|
||||
if (iov_off < (iovec_off + iov[i].iov_len)) {
|
||||
size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
|
||||
|
||||
memset(iov[i].iov_base + (iov_off - iovec_off), 0, len);
|
||||
|
||||
buf_off += len;
|
||||
iov_off += len;
|
||||
size -= len;
|
||||
}
|
||||
iovec_off += iov[i].iov_len;
|
||||
}
|
||||
return buf_off;
|
||||
}
|
||||
|
||||
size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
|
||||
{
|
||||
size_t len;
|
||||
@ -73,3 +96,34 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
FILE *fp, const char *prefix, size_t limit)
|
||||
{
|
||||
unsigned int i, v, b;
|
||||
uint8_t *c;
|
||||
|
||||
c = iov[0].iov_base;
|
||||
for (i = 0, v = 0, b = 0; b < limit; i++, b++) {
|
||||
if (i == iov[v].iov_len) {
|
||||
i = 0; v++;
|
||||
if (v == iov_cnt) {
|
||||
break;
|
||||
}
|
||||
c = iov[v].iov_base;
|
||||
}
|
||||
if ((b % 16) == 0) {
|
||||
fprintf(fp, "%s: %04x:", prefix, b);
|
||||
}
|
||||
if ((b % 4) == 0) {
|
||||
fprintf(fp, " ");
|
||||
}
|
||||
fprintf(fp, " %02x", c[i]);
|
||||
if ((b % 16) == 15) {
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
if ((b % 16) != 0) {
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
4
iov.h
4
iov.h
@ -17,3 +17,7 @@ size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt,
|
||||
size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
void *buf, size_t iov_off, size_t size);
|
||||
size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt);
|
||||
size_t iov_clear(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
size_t iov_off, size_t size);
|
||||
void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
FILE *fp, const char *prefix, size_t limit);
|
||||
|
@ -266,6 +266,7 @@ typedef struct I2SCodec I2SCodec;
|
||||
typedef struct SSIBus SSIBus;
|
||||
typedef struct EventNotifier EventNotifier;
|
||||
typedef struct VirtIODevice VirtIODevice;
|
||||
typedef struct QEMUSGList QEMUSGList;
|
||||
|
||||
typedef uint64_t pcibus_t;
|
||||
|
||||
|
14
usb-bsd.c
14
usb-bsd.c
@ -62,7 +62,6 @@ typedef struct USBHostDevice {
|
||||
} USBHostDevice;
|
||||
|
||||
|
||||
#if 0
|
||||
static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
|
||||
{
|
||||
char buf[32];
|
||||
@ -110,7 +109,6 @@ static void ensure_eps_closed(USBHostDevice *dev)
|
||||
epnum++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void usb_host_handle_reset(USBDevice *dev)
|
||||
{
|
||||
@ -119,7 +117,6 @@ static void usb_host_handle_reset(USBDevice *dev)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* XXX:
|
||||
* -check device states against transfer requests
|
||||
* and return appropriate response
|
||||
@ -256,9 +253,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
|
||||
if (p->pid == USB_TOKEN_IN)
|
||||
ret = read(fd, p->data, p->len);
|
||||
ret = readv(fd, p->iov.iov, p->iov.niov);
|
||||
else
|
||||
ret = write(fd, p->data, p->len);
|
||||
ret = writev(fd, p->iov.iov, p->iov.niov);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &old_mask, NULL);
|
||||
|
||||
@ -278,7 +275,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void usb_host_handle_destroy(USBDevice *opaque)
|
||||
{
|
||||
@ -305,8 +301,8 @@ static int usb_host_initfn(USBDevice *dev)
|
||||
USBDevice *usb_host_device_open(const char *devname)
|
||||
{
|
||||
struct usb_device_info bus_info, dev_info;
|
||||
USBDevice *d = NULL;
|
||||
USBHostDevice *dev, *ret = NULL;
|
||||
USBDevice *d = NULL, *ret = NULL;
|
||||
USBHostDevice *dev;
|
||||
char ctlpath[PATH_MAX + 1];
|
||||
char buspath[PATH_MAX + 1];
|
||||
int bfd, dfd, bus, address, i;
|
||||
@ -408,10 +404,8 @@ static struct USBDeviceInfo usb_host_dev_info = {
|
||||
.init = usb_host_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_host_handle_reset,
|
||||
#if 0
|
||||
.handle_control = usb_host_handle_control,
|
||||
.handle_data = usb_host_handle_data,
|
||||
#endif
|
||||
.handle_destroy = usb_host_handle_destroy,
|
||||
};
|
||||
|
||||
|
48
usb-linux.c
48
usb-linux.c
@ -341,16 +341,16 @@ static void async_complete(void *opaque)
|
||||
if (p) {
|
||||
switch (aurb->urb.status) {
|
||||
case 0:
|
||||
p->len += aurb->urb.actual_length;
|
||||
p->result += aurb->urb.actual_length;
|
||||
break;
|
||||
|
||||
case -EPIPE:
|
||||
set_halt(s, p->devep);
|
||||
p->len = USB_RET_STALL;
|
||||
p->result = USB_RET_STALL;
|
||||
break;
|
||||
|
||||
default:
|
||||
p->len = USB_RET_NAK;
|
||||
p->result = USB_RET_NAK;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -604,6 +604,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
||||
{
|
||||
AsyncURB *aurb;
|
||||
int i, j, ret, max_packet_size, offset, len = 0;
|
||||
uint8_t *buf;
|
||||
|
||||
max_packet_size = get_max_packet_size(s, p->devep);
|
||||
if (max_packet_size == 0)
|
||||
@ -628,19 +629,19 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
||||
len = urb_status_to_usb_ret(
|
||||
aurb[i].urb.iso_frame_desc[j].status);
|
||||
/* Check the frame fits */
|
||||
} else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) {
|
||||
} else if (aurb[i].urb.iso_frame_desc[j].actual_length
|
||||
> p->iov.size) {
|
||||
printf("husb: received iso data is larger then packet\n");
|
||||
len = USB_RET_NAK;
|
||||
/* All good copy data over */
|
||||
} else {
|
||||
len = aurb[i].urb.iso_frame_desc[j].actual_length;
|
||||
memcpy(p->data,
|
||||
aurb[i].urb.buffer +
|
||||
j * aurb[i].urb.iso_frame_desc[0].length,
|
||||
len);
|
||||
buf = aurb[i].urb.buffer +
|
||||
j * aurb[i].urb.iso_frame_desc[0].length;
|
||||
usb_packet_copy(p, buf, len);
|
||||
}
|
||||
} else {
|
||||
len = p->len;
|
||||
len = p->iov.size;
|
||||
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep);
|
||||
|
||||
/* Check the frame fits */
|
||||
@ -650,7 +651,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
||||
}
|
||||
|
||||
/* All good copy data over */
|
||||
memcpy(aurb[i].urb.buffer + offset, p->data, len);
|
||||
usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
|
||||
aurb[i].urb.iso_frame_desc[j].length = len;
|
||||
offset += len;
|
||||
set_iso_buffer_used(s, p->devep, offset);
|
||||
@ -706,7 +707,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
||||
struct usbdevfs_urb *urb;
|
||||
AsyncURB *aurb;
|
||||
int ret, rem;
|
||||
int ret, rem, prem, v;
|
||||
uint8_t *pbuf;
|
||||
uint8_t ep;
|
||||
|
||||
@ -734,10 +735,18 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
|
||||
}
|
||||
|
||||
rem = p->len;
|
||||
pbuf = p->data;
|
||||
p->len = 0;
|
||||
v = 0;
|
||||
prem = p->iov.iov[v].iov_len;
|
||||
pbuf = p->iov.iov[v].iov_base;
|
||||
rem = p->iov.size;
|
||||
while (rem) {
|
||||
if (prem == 0) {
|
||||
v++;
|
||||
assert(v < p->iov.niov);
|
||||
prem = p->iov.iov[v].iov_len;
|
||||
pbuf = p->iov.iov[v].iov_base;
|
||||
assert(prem <= rem);
|
||||
}
|
||||
aurb = async_alloc(s);
|
||||
aurb->packet = p;
|
||||
|
||||
@ -746,16 +755,17 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->usercontext = s;
|
||||
urb->buffer = pbuf;
|
||||
urb->buffer_length = prem;
|
||||
|
||||
if (rem > MAX_USBFS_BUFFER_SIZE) {
|
||||
if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
|
||||
urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
|
||||
aurb->more = 1;
|
||||
} else {
|
||||
urb->buffer_length = rem;
|
||||
aurb->more = 0;
|
||||
}
|
||||
pbuf += urb->buffer_length;
|
||||
prem -= urb->buffer_length;
|
||||
rem -= urb->buffer_length;
|
||||
if (rem) {
|
||||
aurb->more = 1;
|
||||
}
|
||||
|
||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
|
||||
|
||||
|
59
usb-redir.c
59
usb-redir.c
@ -365,12 +365,12 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
|
||||
}
|
||||
|
||||
len = isop->len;
|
||||
if (len > p->len) {
|
||||
if (len > p->iov.size) {
|
||||
ERROR("received iso data is larger then packet ep %02X\n", ep);
|
||||
bufp_free(dev, isop, ep);
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
memcpy(p->data, isop->data, len);
|
||||
usb_packet_copy(p, isop->data, len);
|
||||
bufp_free(dev, isop, ep);
|
||||
return len;
|
||||
} else {
|
||||
@ -379,18 +379,20 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
|
||||
if (dev->endpoint[EP2I(ep)].iso_started) {
|
||||
struct usb_redir_iso_packet_header iso_packet = {
|
||||
.endpoint = ep,
|
||||
.length = p->len
|
||||
.length = p->iov.size
|
||||
};
|
||||
uint8_t buf[p->iov.size];
|
||||
/* No id, we look at the ep when receiving a status back */
|
||||
usb_packet_copy(p, buf, p->iov.size);
|
||||
usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
|
||||
p->data, p->len);
|
||||
buf, p->iov.size);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
}
|
||||
status = dev->endpoint[EP2I(ep)].iso_error;
|
||||
dev->endpoint[EP2I(ep)].iso_error = 0;
|
||||
DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status,
|
||||
p->len);
|
||||
return usbredir_handle_status(dev, status, p->len);
|
||||
DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
|
||||
p->iov.size);
|
||||
return usbredir_handle_status(dev, status, p->iov.size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,10 +415,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
struct usb_redir_bulk_packet_header bulk_packet;
|
||||
|
||||
DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id);
|
||||
DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
|
||||
p->iov.size, aurb->packet_id);
|
||||
|
||||
bulk_packet.endpoint = ep;
|
||||
bulk_packet.length = p->len;
|
||||
bulk_packet.length = p->iov.size;
|
||||
bulk_packet.stream_id = 0;
|
||||
aurb->bulk_packet = bulk_packet;
|
||||
|
||||
@ -424,9 +427,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
|
||||
&bulk_packet, NULL, 0);
|
||||
} else {
|
||||
usbredir_log_data(dev, "bulk data out:", p->data, p->len);
|
||||
uint8_t buf[p->iov.size];
|
||||
usb_packet_copy(p, buf, p->iov.size);
|
||||
usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
|
||||
usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
|
||||
&bulk_packet, p->data, p->len);
|
||||
&bulk_packet, buf, p->iov.size);
|
||||
}
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
@ -471,29 +476,31 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
|
||||
}
|
||||
|
||||
len = intp->len;
|
||||
if (len > p->len) {
|
||||
if (len > p->iov.size) {
|
||||
ERROR("received int data is larger then packet ep %02X\n", ep);
|
||||
bufp_free(dev, intp, ep);
|
||||
return USB_RET_NAK;
|
||||
}
|
||||
memcpy(p->data, intp->data, len);
|
||||
usb_packet_copy(p, intp->data, len);
|
||||
bufp_free(dev, intp, ep);
|
||||
return len;
|
||||
} else {
|
||||
/* Output interrupt endpoint, normal async operation */
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
struct usb_redir_interrupt_packet_header interrupt_packet;
|
||||
uint8_t buf[p->iov.size];
|
||||
|
||||
DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len,
|
||||
DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
|
||||
aurb->packet_id);
|
||||
|
||||
interrupt_packet.endpoint = ep;
|
||||
interrupt_packet.length = p->len;
|
||||
interrupt_packet.length = p->iov.size;
|
||||
aurb->interrupt_packet = interrupt_packet;
|
||||
|
||||
usbredir_log_data(dev, "interrupt data out:", p->data, p->len);
|
||||
usb_packet_copy(p, buf, p->iov.size);
|
||||
usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
|
||||
usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
|
||||
&interrupt_packet, p->data, p->len);
|
||||
&interrupt_packet, buf, p->iov.size);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
@ -959,7 +966,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id,
|
||||
dev->dev.data_buf[0] = config_status->configuration;
|
||||
len = 1;
|
||||
}
|
||||
aurb->packet->len =
|
||||
aurb->packet->result =
|
||||
usbredir_handle_status(dev, config_status->status, len);
|
||||
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
|
||||
}
|
||||
@ -987,7 +994,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id,
|
||||
dev->dev.data_buf[0] = alt_setting_status->alt;
|
||||
len = 1;
|
||||
}
|
||||
aurb->packet->len =
|
||||
aurb->packet->result =
|
||||
usbredir_handle_status(dev, alt_setting_status->status, len);
|
||||
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
|
||||
}
|
||||
@ -1070,7 +1077,7 @@ static void usbredir_control_packet(void *priv, uint32_t id,
|
||||
len = USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
aurb->packet->len = len;
|
||||
aurb->packet->result = len;
|
||||
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
@ -1105,15 +1112,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
|
||||
len = usbredir_handle_status(dev, bulk_packet->status, len);
|
||||
if (len > 0) {
|
||||
usbredir_log_data(dev, "bulk data in:", data, data_len);
|
||||
if (data_len <= aurb->packet->len) {
|
||||
memcpy(aurb->packet->data, data, data_len);
|
||||
if (data_len <= aurb->packet->iov.size) {
|
||||
usb_packet_copy(aurb->packet, data, data_len);
|
||||
} else {
|
||||
ERROR("bulk buffer too small (%d > %d)\n", data_len,
|
||||
aurb->packet->len);
|
||||
ERROR("bulk buffer too small (%d > %zd)\n", data_len,
|
||||
aurb->packet->iov.size);
|
||||
len = USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
aurb->packet->len = len;
|
||||
aurb->packet->result = len;
|
||||
usb_packet_complete(&dev->dev, aurb->packet);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
@ -1185,7 +1192,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
|
||||
}
|
||||
|
||||
if (aurb->packet) {
|
||||
aurb->packet->len = usbredir_handle_status(dev,
|
||||
aurb->packet->result = usbredir_handle_status(dev,
|
||||
interrupt_packet->status, len);
|
||||
usb_packet_complete(&dev->dev, aurb->packet);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user