mirror of https://github.com/xemu-project/xemu.git
Merge remote-tracking branch 'kraxel/usb.28' into staging
This commit is contained in:
commit
bdc792d387
|
@ -880,6 +880,7 @@ static void ehci_reset(void *opaque)
|
|||
}
|
||||
if (devs[i] && devs[i]->attached) {
|
||||
usb_attach(&s->ports[i]);
|
||||
usb_send_msg(devs[i], USB_MSG_RESET);
|
||||
}
|
||||
}
|
||||
ehci_queues_rip_all(s);
|
||||
|
@ -978,8 +979,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
|
|||
if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
|
||||
trace_usb_ehci_port_reset(port, 0);
|
||||
if (dev && dev->attached) {
|
||||
usb_attach(&s->ports[port]);
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
usb_reset(&s->ports[port]);
|
||||
*portsc &= ~PORTSC_CSC;
|
||||
}
|
||||
|
||||
|
|
11
hw/usb-hid.c
11
hw/usb-hid.c
|
@ -527,10 +527,21 @@ static int usb_keyboard_initfn(USBDevice *dev)
|
|||
return usb_hid_initfn(dev, HID_KEYBOARD);
|
||||
}
|
||||
|
||||
static int usb_ptr_post_load(void *opaque, int version_id)
|
||||
{
|
||||
USBHIDState *s = opaque;
|
||||
|
||||
if (s->dev.remote_wakeup) {
|
||||
hid_pointer_activate(&s->hid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_usb_ptr = {
|
||||
.name = "usb-ptr",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = usb_ptr_post_load,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_USB_DEVICE(dev, USBHIDState),
|
||||
VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState),
|
||||
|
|
12
hw/usb-hub.c
12
hw/usb-hub.c
|
@ -207,10 +207,14 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet)
|
|||
/*
|
||||
* Just pass it along upstream for now.
|
||||
*
|
||||
* If we ever inplement usb 2.0 split transactions this will
|
||||
* If we ever implement usb 2.0 split transactions this will
|
||||
* become a little more complicated ...
|
||||
*
|
||||
* Can't use usb_packet_complete() here because packet->owner is
|
||||
* cleared already, go call the ->complete() callback directly
|
||||
* instead.
|
||||
*/
|
||||
usb_packet_complete(&s->dev, packet);
|
||||
s->dev.port->ops->complete(s->dev.port, packet);
|
||||
}
|
||||
|
||||
static void usb_hub_handle_reset(USBDevice *dev)
|
||||
|
@ -289,7 +293,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
port->wPortStatus |= PORT_STAT_SUSPEND;
|
||||
break;
|
||||
case PORT_RESET:
|
||||
if (dev) {
|
||||
if (dev && dev->attached) {
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
port->wPortChange |= PORT_STAT_C_RESET;
|
||||
/* set enable bit */
|
||||
|
@ -429,7 +433,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
|
|||
for(i = 0; i < NUM_PORTS; i++) {
|
||||
port = &s->ports[i];
|
||||
dev = port->port.dev;
|
||||
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||
if (dev && dev->attached && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||
ret = usb_handle_packet(dev, p);
|
||||
if (ret != USB_RET_NODEV) {
|
||||
return ret;
|
||||
|
|
|
@ -325,7 +325,10 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
|
|||
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
scsi_req_cancel(s->req);
|
||||
|
||||
if (s->req) {
|
||||
scsi_req_cancel(s->req);
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
|
|
|
@ -150,7 +150,7 @@ static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
|
|||
#define OHCI_TD_DI_SHIFT 21
|
||||
#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)
|
||||
#define OHCI_TD_T0 (1<<24)
|
||||
#define OHCI_TD_T1 (1<<24)
|
||||
#define OHCI_TD_T1 (1<<25)
|
||||
#define OHCI_TD_EC_SHIFT 26
|
||||
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
|
||||
#define OHCI_TD_CC_SHIFT 28
|
||||
|
@ -449,7 +449,7 @@ static void ohci_reset(void *opaque)
|
|||
port = &ohci->rhport[i];
|
||||
port->ctrl = 0;
|
||||
if (port->port.dev && port->port.dev->attached) {
|
||||
usb_attach(&port->port);
|
||||
usb_reset(&port->port);
|
||||
}
|
||||
}
|
||||
if (ohci->async_td) {
|
||||
|
@ -872,7 +872,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
{
|
||||
int dir;
|
||||
size_t len = 0;
|
||||
size_t len = 0, pktlen = 0;
|
||||
#ifdef DEBUG_PACKET
|
||||
const char *str = NULL;
|
||||
#endif
|
||||
|
@ -940,20 +940,30 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
len = (td.be - td.cbp) + 1;
|
||||
}
|
||||
|
||||
if (len && dir != OHCI_TD_DIR_IN && !completion) {
|
||||
ohci_copy_td(ohci, &td, ohci->usb_buf, len, 0);
|
||||
pktlen = len;
|
||||
if (len && dir != OHCI_TD_DIR_IN) {
|
||||
/* The endpoint may not allow us to transfer it all now */
|
||||
pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT;
|
||||
if (pktlen > len) {
|
||||
pktlen = len;
|
||||
}
|
||||
if (!completion) {
|
||||
ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flag_r = (td.flags & OHCI_TD_R) != 0;
|
||||
#ifdef DEBUG_PACKET
|
||||
DPRINTF(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
|
||||
addr, (int64_t)len, str, flag_r, td.cbp, td.be);
|
||||
DPRINTF(" TD @ 0x%.8x %" PRId64 " of %" PRId64
|
||||
" bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
|
||||
addr, (int64_t)pktlen, (int64_t)len, str, flag_r, td.cbp, td.be);
|
||||
|
||||
if (len > 0 && dir != OHCI_TD_DIR_IN) {
|
||||
if (pktlen > 0 && dir != OHCI_TD_DIR_IN) {
|
||||
DPRINTF(" data:");
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < pktlen; i++) {
|
||||
printf(" %.2x", ohci->usb_buf[i]);
|
||||
}
|
||||
DPRINTF("\n");
|
||||
}
|
||||
#endif
|
||||
|
@ -982,7 +992,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
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);
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
|
@ -1005,12 +1015,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
DPRINTF("\n");
|
||||
#endif
|
||||
} else {
|
||||
ret = len;
|
||||
ret = pktlen;
|
||||
}
|
||||
}
|
||||
|
||||
/* Writeback */
|
||||
if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
|
||||
if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
|
||||
/* Transmission succeeded. */
|
||||
if (ret == len) {
|
||||
td.cbp = 0;
|
||||
|
@ -1026,6 +1036,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
|
||||
OHCI_SET_BM(td.flags, TD_EC, 0);
|
||||
|
||||
if ((dir != OHCI_TD_DIR_IN) && (ret != len)) {
|
||||
/* Partial packet transfer: TD not ready to retire yet */
|
||||
goto exit_no_retire;
|
||||
}
|
||||
|
||||
/* Setting ED_C is part of the TD retirement process */
|
||||
ed->head &= ~OHCI_ED_C;
|
||||
if (td.flags & OHCI_TD_T0)
|
||||
ed->head |= OHCI_ED_C;
|
||||
|
@ -1066,6 +1082,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
i = OHCI_BM(td.flags, TD_DI);
|
||||
if (i < ohci->done_count)
|
||||
ohci->done_count = i;
|
||||
exit_no_retire:
|
||||
ohci_put_td(ohci, addr, &td);
|
||||
return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ static void uhci_reset(void *opaque)
|
|||
port = &s->ports[i];
|
||||
port->ctrl = 0x0080;
|
||||
if (port->port.dev && port->port.dev->attached) {
|
||||
usb_attach(&port->port);
|
||||
usb_reset(&port->port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
hw/usb.c
12
hw/usb.c
|
@ -33,6 +33,7 @@ void usb_attach(USBPort *port)
|
|||
|
||||
assert(dev != NULL);
|
||||
assert(dev->attached);
|
||||
assert(dev->state == USB_STATE_NOTATTACHED);
|
||||
port->ops->attach(port);
|
||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
||||
}
|
||||
|
@ -42,10 +43,21 @@ void usb_detach(USBPort *port)
|
|||
USBDevice *dev = port->dev;
|
||||
|
||||
assert(dev != NULL);
|
||||
assert(dev->state != USB_STATE_NOTATTACHED);
|
||||
port->ops->detach(port);
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
}
|
||||
|
||||
void usb_reset(USBPort *port)
|
||||
{
|
||||
USBDevice *dev = port->dev;
|
||||
|
||||
assert(dev != NULL);
|
||||
usb_detach(port);
|
||||
usb_attach(port);
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
}
|
||||
|
||||
void usb_wakeup(USBDevice *dev)
|
||||
{
|
||||
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
|
||||
|
|
1
hw/usb.h
1
hw/usb.h
|
@ -306,6 +306,7 @@ void usb_cancel_packet(USBPacket * p);
|
|||
|
||||
void usb_attach(USBPort *port);
|
||||
void usb_detach(USBPort *port);
|
||||
void usb_reset(USBPort *port);
|
||||
void usb_wakeup(USBDevice *dev);
|
||||
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
|
||||
|
|
176
usb-linux.c
176
usb-linux.c
|
@ -411,6 +411,100 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
|
|||
}
|
||||
}
|
||||
|
||||
static int usb_host_claim_port(USBHostDevice *s)
|
||||
{
|
||||
#ifdef USBDEVFS_CLAIM_PORT
|
||||
char *h, hub_name[64], line[1024];
|
||||
int hub_addr, portnr, ret;
|
||||
|
||||
snprintf(hub_name, sizeof(hub_name), "%d-%s",
|
||||
s->match.bus_num, s->match.port);
|
||||
|
||||
/* try strip off last ".$portnr" to get hub */
|
||||
h = strrchr(hub_name, '.');
|
||||
if (h != NULL) {
|
||||
portnr = atoi(h+1);
|
||||
*h = '\0';
|
||||
} else {
|
||||
/* no dot in there -> it is the root hub */
|
||||
snprintf(hub_name, sizeof(hub_name), "usb%d",
|
||||
s->match.bus_num);
|
||||
portnr = atoi(s->match.port);
|
||||
}
|
||||
|
||||
if (!usb_host_read_file(line, sizeof(line), "devnum",
|
||||
hub_name)) {
|
||||
return -1;
|
||||
}
|
||||
if (sscanf(line, "%d", &hub_addr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!usb_host_device_path) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(line, sizeof(line), "%s/%03d/%03d",
|
||||
usb_host_device_path, s->match.bus_num, hub_addr);
|
||||
s->hub_fd = open(line, O_RDWR | O_NONBLOCK);
|
||||
if (s->hub_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &portnr);
|
||||
if (ret < 0) {
|
||||
close(s->hub_fd);
|
||||
s->hub_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
trace_usb_host_claim_port(s->match.bus_num, hub_addr, portnr);
|
||||
return 0;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
|
||||
{
|
||||
/* earlier Linux 2.4 do not support that */
|
||||
#ifdef USBDEVFS_DISCONNECT
|
||||
struct usbdevfs_ioctl ctrl;
|
||||
int ret, interface;
|
||||
|
||||
for (interface = 0; interface < nb_interfaces; interface++) {
|
||||
ctrl.ioctl_code = USBDEVFS_DISCONNECT;
|
||||
ctrl.ifno = interface;
|
||||
ctrl.data = 0;
|
||||
ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
|
||||
if (ret < 0 && errno != ENODATA) {
|
||||
perror("USBDEVFS_DISCONNECT");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_linux_get_num_interfaces(USBHostDevice *s)
|
||||
{
|
||||
char device_name[64], line[1024];
|
||||
int num_interfaces = 0;
|
||||
|
||||
if (usb_fs_type != USB_FS_SYS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(device_name, "%d-%s", s->bus_num, s->port);
|
||||
if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
|
||||
device_name)) {
|
||||
return -1;
|
||||
}
|
||||
if (sscanf(line, "%d", &num_interfaces) != 1) {
|
||||
return -1;
|
||||
}
|
||||
return num_interfaces;
|
||||
}
|
||||
|
||||
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
||||
{
|
||||
const char *op = NULL;
|
||||
|
@ -462,22 +556,9 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
|
|||
}
|
||||
nb_interfaces = dev->descr[i + 4];
|
||||
|
||||
#ifdef USBDEVFS_DISCONNECT
|
||||
/* earlier Linux 2.4 do not support that */
|
||||
{
|
||||
struct usbdevfs_ioctl ctrl;
|
||||
for (interface = 0; interface < nb_interfaces; interface++) {
|
||||
ctrl.ioctl_code = USBDEVFS_DISCONNECT;
|
||||
ctrl.ifno = interface;
|
||||
ctrl.data = 0;
|
||||
op = "USBDEVFS_DISCONNECT";
|
||||
ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
|
||||
if (ret < 0 && errno != ENODATA) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: only grab if all interfaces are free */
|
||||
for (interface = 0; interface < nb_interfaces; interface++) {
|
||||
|
@ -840,14 +921,28 @@ static int usb_host_set_address(USBHostDevice *s, int addr)
|
|||
|
||||
static int usb_host_set_config(USBHostDevice *s, int config)
|
||||
{
|
||||
int ret, first = 1;
|
||||
|
||||
trace_usb_host_set_config(s->bus_num, s->addr, config);
|
||||
|
||||
usb_host_release_interfaces(s);
|
||||
|
||||
int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
|
||||
again:
|
||||
ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
|
||||
|
||||
DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
|
||||
|
||||
if (ret < 0 && errno == EBUSY && first) {
|
||||
/* happens if usb device is in use by host drivers */
|
||||
int count = usb_linux_get_num_interfaces(s);
|
||||
if (count > 0) {
|
||||
DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
|
||||
usb_host_disconnect_ifaces(s, count);
|
||||
first = 0;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return ctrl_error();
|
||||
}
|
||||
|
@ -1301,56 +1396,9 @@ static int usb_host_initfn(USBDevice *dev)
|
|||
qemu_add_exit_notifier(&s->exit);
|
||||
usb_host_auto_check(NULL);
|
||||
|
||||
#ifdef USBDEVFS_CLAIM_PORT
|
||||
if (s->match.bus_num != 0 && s->match.port != NULL) {
|
||||
char *h, hub_name[64], line[1024];
|
||||
int hub_addr, portnr, ret;
|
||||
|
||||
snprintf(hub_name, sizeof(hub_name), "%d-%s",
|
||||
s->match.bus_num, s->match.port);
|
||||
|
||||
/* try strip off last ".$portnr" to get hub */
|
||||
h = strrchr(hub_name, '.');
|
||||
if (h != NULL) {
|
||||
portnr = atoi(h+1);
|
||||
*h = '\0';
|
||||
} else {
|
||||
/* no dot in there -> it is the root hub */
|
||||
snprintf(hub_name, sizeof(hub_name), "usb%d",
|
||||
s->match.bus_num);
|
||||
portnr = atoi(s->match.port);
|
||||
}
|
||||
|
||||
if (!usb_host_read_file(line, sizeof(line), "devnum",
|
||||
hub_name)) {
|
||||
goto out;
|
||||
}
|
||||
if (sscanf(line, "%d", &hub_addr) != 1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!usb_host_device_path) {
|
||||
goto out;
|
||||
}
|
||||
snprintf(line, sizeof(line), "%s/%03d/%03d",
|
||||
usb_host_device_path, s->match.bus_num, hub_addr);
|
||||
s->hub_fd = open(line, O_RDWR | O_NONBLOCK);
|
||||
if (s->hub_fd < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &portnr);
|
||||
if (ret < 0) {
|
||||
close(s->hub_fd);
|
||||
s->hub_fd = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
trace_usb_host_claim_port(s->match.bus_num, hub_addr, portnr);
|
||||
usb_host_claim_port(s);
|
||||
}
|
||||
out:
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue