e00a5d0761
- Moved main option from the portX parameter to the optionsX parameter. Added new option "path" for the disk, cdrom and floppy devices, "ports" for the USB hub and "file" for the USB printer. The legacy syntax is still supported. - Register USB device state after init() to ensure all options that may have effect on the save/restore list are applied. - Report available devices on top of log file similar to hdimage, network and sound modules. - Changed USB device type variable typein host controllers to int. - Prepared improved usbdev_ctl init / exit implementation. - TODO: Create list of USB devices dynamicly from available plugins.
632 lines
17 KiB
C++
632 lines
17 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Generic USB emulation code
|
|
//
|
|
// Copyright (c) 2005 Fabrice Bellard
|
|
// Copyright (C) 2009-2015 Benjamin D Lunt (fys at fysnet net)
|
|
// 2009-2021 The Bochs Project
|
|
//
|
|
// 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 "iodev.h"
|
|
|
|
#if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
|
|
|
|
#include "usb_common.h"
|
|
|
|
#define LOG_THIS bx_usbdev_ctl.
|
|
|
|
bx_usbdev_ctl_c bx_usbdev_ctl;
|
|
|
|
bx_usbdev_ctl_c::bx_usbdev_ctl_c()
|
|
{
|
|
put("usbdevctl", "USBCTL");
|
|
}
|
|
|
|
void bx_usbdev_ctl_c::init(void)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
void bx_usbdev_ctl_c::exit(void)
|
|
{
|
|
// TODO
|
|
usbdev_locator_c::cleanup();
|
|
}
|
|
|
|
const char *usbmod_names[] =
|
|
{
|
|
"none",
|
|
"usb_cbi",
|
|
"usb_hid",
|
|
"usb_hub",
|
|
"usb_msd",
|
|
"usb_printer"
|
|
};
|
|
|
|
const char *usb_device_names[] =
|
|
{
|
|
"none",
|
|
"mouse",
|
|
"tablet",
|
|
"keypad",
|
|
"keyboard",
|
|
"disk",
|
|
"cdrom",
|
|
"hub",
|
|
"printer",
|
|
"floppy"
|
|
};
|
|
|
|
void bx_usbdev_ctl_c::list_devices(void)
|
|
{
|
|
char list[60];
|
|
Bit8u i = 1; // skip "none"
|
|
size_t len = 0, len1;
|
|
|
|
BX_INFO(("Pluggable USB devices"));
|
|
list[0] = 0;
|
|
while (usb_device_names[i] != NULL) {
|
|
len1 = strlen(usb_device_names[i]);
|
|
if ((len + len1 + 1) > 58) {
|
|
BX_INFO((" %s", list));
|
|
list[0] = 0;
|
|
len = 0;
|
|
}
|
|
strcat(list, " ");
|
|
strcat(list, usb_device_names[i]);
|
|
len = strlen(list);
|
|
i++;
|
|
}
|
|
if (len > 0) {
|
|
BX_INFO((" %s", list));
|
|
}
|
|
}
|
|
|
|
int bx_usbdev_ctl_c::init_device(bx_list_c *portconf, logfunctions *hub, void **dev)
|
|
{
|
|
usbmod_type modtype = USB_MOD_TYPE_NONE;
|
|
usbdev_type devtype = USB_DEV_TYPE_NONE;
|
|
usb_device_c **device = (usb_device_c**)dev;
|
|
const char *raw_devname, *options;
|
|
const char *args, *new_opt1 = NULL;
|
|
char *devname, *ptr, new_options[BX_PATHNAME_LEN];
|
|
size_t len;
|
|
|
|
raw_devname = ((bx_param_string_c*)portconf->get_by_name("device"))->getptr();
|
|
options = ((bx_param_string_c*)portconf->get_by_name("options"))->getptr();
|
|
len = strlen(raw_devname);
|
|
devname = new char[len + 1];
|
|
strcpy(devname, raw_devname);
|
|
ptr = strtok(devname, ":");
|
|
ptr = strtok(NULL, "\n");
|
|
if (ptr == NULL) {
|
|
args = raw_devname+strlen(devname);
|
|
} else {
|
|
args = raw_devname+(ptr-devname);
|
|
}
|
|
new_options[0] = 0;
|
|
if (!strcmp(devname, "mouse")) {
|
|
modtype = USB_MOD_TYPE_HID;
|
|
devtype = USB_DEV_TYPE_MOUSE;
|
|
} else if (!strcmp(devname, "tablet")) {
|
|
modtype = USB_MOD_TYPE_HID;
|
|
devtype = USB_DEV_TYPE_TABLET;
|
|
} else if (!strcmp(devname, "keypad")) {
|
|
modtype = USB_MOD_TYPE_HID;
|
|
devtype = USB_DEV_TYPE_KEYPAD;
|
|
} else if (!strcmp(devname, "keyboard")) {
|
|
modtype = USB_MOD_TYPE_HID;
|
|
devtype = USB_DEV_TYPE_KEYBOARD;
|
|
} else if (!strcmp(devname, "disk")) {
|
|
modtype = USB_MOD_TYPE_MSD;
|
|
devtype = USB_DEV_TYPE_DISK;
|
|
new_opt1 = "path";
|
|
} else if (!strcmp(devname, "cdrom")) {
|
|
modtype = USB_MOD_TYPE_MSD;
|
|
devtype = USB_DEV_TYPE_CDROM;
|
|
new_opt1 = "path";
|
|
} else if (!strcmp(devname, "hub")) {
|
|
modtype = USB_MOD_TYPE_HUB;
|
|
devtype = USB_DEV_TYPE_HUB;
|
|
new_opt1 = "ports";
|
|
} else if (!strcmp(devname, "printer")) {
|
|
modtype = USB_MOD_TYPE_PRINTER;
|
|
devtype = USB_DEV_TYPE_PRINTER;
|
|
new_opt1 = "file";
|
|
} else if (!strncmp(devname, "floppy", 6)) {
|
|
modtype = USB_MOD_TYPE_CBI;
|
|
devtype = USB_DEV_TYPE_FLOPPY;
|
|
new_opt1 = "path";
|
|
} else {
|
|
hub->panic("unknown USB device: %s", devname);
|
|
delete [] devname;
|
|
return devtype;
|
|
}
|
|
if ((strlen(args) > 0) && (new_opt1 != NULL)) {
|
|
if (strlen(options) > 0) {
|
|
sprintf(new_options, "%s:%s, %s", new_opt1, args, options);
|
|
} else {
|
|
sprintf(new_options, "%s:%s", new_opt1, args);
|
|
}
|
|
((bx_param_string_c*)portconf->get_by_name("device"))->set(devname);
|
|
((bx_param_string_c*)portconf->get_by_name("options"))->set(new_options);
|
|
}
|
|
if (!usbdev_locator_c::module_present(usbmod_names[modtype])) {
|
|
#if BX_PLUGINS
|
|
PLUG_load_plugin_var(usbmod_names[modtype], PLUGTYPE_USB);
|
|
#else
|
|
BX_PANIC(("could not find USB device '%s'", usbmod_names[modtype]));
|
|
#endif
|
|
}
|
|
*device = usbdev_locator_c::create(usbmod_names[modtype], devtype);
|
|
if (*device != NULL) {
|
|
parse_port_options(*device, portconf);
|
|
}
|
|
delete [] devname;
|
|
return devtype;
|
|
}
|
|
|
|
void bx_usbdev_ctl_c::parse_port_options(usb_device_c *device, bx_list_c *portconf)
|
|
{
|
|
const char *raw_options;
|
|
int i, optc, speed = USB_SPEED_LOW; // assume LOW speed device if parameter not given.
|
|
char *opts[16];
|
|
|
|
memset(opts, 0, sizeof(opts));
|
|
raw_options = ((bx_param_string_c*)portconf->get_by_name("options"))->getptr();
|
|
optc = bx_split_option_list("USB port options", raw_options, opts, 16);
|
|
for (i = 0; i < optc; i++) {
|
|
if (!strncmp(opts[i], "speed:", 6)) {
|
|
if (!strcmp(opts[i]+6, "low")) {
|
|
speed = USB_SPEED_LOW;
|
|
} else if (!strcmp(opts[i]+6, "full")) {
|
|
speed = USB_SPEED_FULL;
|
|
} else if (!strcmp(opts[i]+6, "high")) {
|
|
speed = USB_SPEED_HIGH;
|
|
} else if (!strcmp(opts[i]+6, "super")) {
|
|
speed = USB_SPEED_SUPER;
|
|
} else {
|
|
BX_ERROR(("ignoring unknown USB device speed: '%s'", opts[i]+6));
|
|
}
|
|
if (!device->set_speed(speed)) {
|
|
BX_PANIC(("USB device '%s' doesn't support '%s' speed",
|
|
usb_device_names[device->get_type()], opts[i]+6));
|
|
}
|
|
} else if (!strcmp(opts[i], "debug")) {
|
|
device->set_debug_mode();
|
|
} else if (!device->set_option(opts[i])) {
|
|
BX_ERROR(("ignoring unknown USB device option: '%s'", opts[i]));
|
|
}
|
|
}
|
|
for (i = 0; i < optc; i++) {
|
|
if (opts[i] != NULL) {
|
|
free(opts[i]);
|
|
opts[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
usbdev_locator_c *usbdev_locator_c::all;
|
|
|
|
//
|
|
// Each USB device module has a static locator class that registers
|
|
// here
|
|
//
|
|
usbdev_locator_c::usbdev_locator_c(const char *type)
|
|
{
|
|
next = all;
|
|
all = this;
|
|
this->type = type;
|
|
}
|
|
|
|
usbdev_locator_c::~usbdev_locator_c()
|
|
{
|
|
usbdev_locator_c *ptr = 0;
|
|
|
|
if (this == all) {
|
|
all = all->next;
|
|
} else {
|
|
ptr = all;
|
|
while (ptr != NULL) {
|
|
if (ptr->next != this) {
|
|
ptr = ptr->next;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ptr) {
|
|
ptr->next = this->next;
|
|
}
|
|
}
|
|
|
|
bool usbdev_locator_c::module_present(const char *type)
|
|
{
|
|
usbdev_locator_c *ptr = 0;
|
|
|
|
for (ptr = all; ptr != NULL; ptr = ptr->next) {
|
|
if (strcmp(type, ptr->type) == 0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void usbdev_locator_c::cleanup()
|
|
{
|
|
#if BX_PLUGINS
|
|
while (all != NULL) {
|
|
PLUG_unload_plugin_type(all->type, PLUGTYPE_USB);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Called by USB HC emulations to locate and create a usb_device_c
|
|
// object
|
|
//
|
|
usb_device_c* usbdev_locator_c::create(const char *type, usbdev_type devtype)
|
|
{
|
|
usbdev_locator_c *ptr = 0;
|
|
|
|
for (ptr = all; ptr != NULL; ptr = ptr->next) {
|
|
if (strcmp(type, ptr->type) == 0)
|
|
return (ptr->allocate(devtype));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#undef LOG_THIS
|
|
#define LOG_THIS
|
|
|
|
// Base class for USB devices
|
|
|
|
usb_device_c::usb_device_c(void)
|
|
{
|
|
memset((void*)&d, 0, sizeof(d));
|
|
d.async_mode = 1;
|
|
}
|
|
|
|
// Find device with given address
|
|
usb_device_c* usb_device_c::find_device(Bit8u addr)
|
|
{
|
|
if (addr == d.addr) {
|
|
return this;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Generic USB packet handler
|
|
|
|
#define SETUP_STATE_IDLE 0
|
|
#define SETUP_STATE_DATA 1
|
|
#define SETUP_STATE_ACK 2
|
|
|
|
int usb_device_c::handle_packet(USBPacket *p)
|
|
{
|
|
int l, ret = 0;
|
|
int len = p->len;
|
|
Bit8u *data = p->data;
|
|
|
|
switch(p->pid) {
|
|
case USB_MSG_ATTACH:
|
|
d.state = USB_STATE_ATTACHED;
|
|
break;
|
|
case USB_MSG_DETACH:
|
|
d.state = USB_STATE_NOTATTACHED;
|
|
break;
|
|
case USB_MSG_RESET:
|
|
d.remote_wakeup = 0;
|
|
d.addr = 0;
|
|
d.state = USB_STATE_DEFAULT;
|
|
handle_reset();
|
|
break;
|
|
case USB_TOKEN_SETUP:
|
|
if (d.state < USB_STATE_DEFAULT || p->devaddr != d.addr)
|
|
return USB_RET_NODEV;
|
|
if (len != 8)
|
|
goto fail;
|
|
d.stall = 0;
|
|
memcpy(d.setup_buf, data, 8);
|
|
d.setup_len = (d.setup_buf[7] << 8) | d.setup_buf[6];
|
|
d.setup_index = 0;
|
|
if (d.setup_buf[0] & USB_DIR_IN) {
|
|
ret = handle_control((d.setup_buf[0] << 8) | d.setup_buf[1],
|
|
(d.setup_buf[3] << 8) | d.setup_buf[2],
|
|
(d.setup_buf[5] << 8) | d.setup_buf[4],
|
|
d.setup_len, d.data_buf);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (ret < d.setup_len)
|
|
d.setup_len = ret;
|
|
d.setup_state = SETUP_STATE_DATA;
|
|
} else {
|
|
if (d.setup_len == 0)
|
|
d.setup_state = SETUP_STATE_ACK;
|
|
else
|
|
d.setup_state = SETUP_STATE_DATA;
|
|
}
|
|
break;
|
|
case USB_TOKEN_IN:
|
|
if (d.state < USB_STATE_DEFAULT || p->devaddr != d.addr)
|
|
return USB_RET_NODEV;
|
|
if (d.stall) goto fail;
|
|
switch(p->devep) {
|
|
case 0:
|
|
switch(d.setup_state) {
|
|
case SETUP_STATE_ACK:
|
|
if (!(d.setup_buf[0] & USB_DIR_IN)) {
|
|
d.setup_state = SETUP_STATE_IDLE;
|
|
ret = handle_control((d.setup_buf[0] << 8) | d.setup_buf[1],
|
|
(d.setup_buf[3] << 8) | d.setup_buf[2],
|
|
(d.setup_buf[5] << 8) | d.setup_buf[4],
|
|
d.setup_len, d.data_buf);
|
|
if (ret > 0)
|
|
ret = 0;
|
|
} else {
|
|
// return 0 byte
|
|
}
|
|
break;
|
|
case SETUP_STATE_DATA:
|
|
if (d.setup_buf[0] & USB_DIR_IN) {
|
|
l = d.setup_len - d.setup_index;
|
|
if (l > len)
|
|
l = len;
|
|
memcpy(data, d.data_buf + d.setup_index, l);
|
|
d.setup_index += l;
|
|
if (d.setup_index >= d.setup_len)
|
|
d.setup_state = SETUP_STATE_ACK;
|
|
ret = l;
|
|
} else {
|
|
d.setup_state = SETUP_STATE_IDLE;
|
|
goto fail;
|
|
}
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
break;
|
|
default:
|
|
ret = handle_data(p);
|
|
break;
|
|
}
|
|
break;
|
|
case USB_TOKEN_OUT:
|
|
if (d.state < USB_STATE_DEFAULT || p->devaddr != d.addr)
|
|
return USB_RET_NODEV;
|
|
if (d.stall) goto fail;
|
|
switch(p->devep) {
|
|
case 0:
|
|
switch(d.setup_state) {
|
|
case SETUP_STATE_ACK:
|
|
if (d.setup_buf[0] & USB_DIR_IN) {
|
|
d.setup_state = SETUP_STATE_IDLE;
|
|
// transfer OK
|
|
} else {
|
|
// ignore additionnal output
|
|
}
|
|
break;
|
|
case SETUP_STATE_DATA:
|
|
if (!(d.setup_buf[0] & USB_DIR_IN)) {
|
|
l = d.setup_len - d.setup_index;
|
|
if (l > len)
|
|
l = len;
|
|
memcpy(d.data_buf + d.setup_index, data, l);
|
|
d.setup_index += l;
|
|
if (d.setup_index >= d.setup_len)
|
|
d.setup_state = SETUP_STATE_ACK;
|
|
ret = l;
|
|
} else {
|
|
// it is okay for a host to send an OUT before it reads
|
|
// all of the expected IN. It is telling the controller
|
|
// that it doesn't want any more from that particular call.
|
|
ret = 0;
|
|
d.setup_state = SETUP_STATE_IDLE;
|
|
}
|
|
break;
|
|
default:
|
|
goto fail;
|
|
}
|
|
break;
|
|
default:
|
|
ret = handle_data(p);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
fail:
|
|
d.stall = 1;
|
|
ret = USB_RET_STALL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int usb_device_c::handle_control_common(int request, int value, int index, int length, Bit8u *data)
|
|
{
|
|
int ret = -1;
|
|
|
|
switch (request) {
|
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
BX_DEBUG(("USB_REQ_SET_ADDRESS:"));
|
|
d.state = USB_STATE_ADDRESS;
|
|
d.addr = value;
|
|
ret = 0;
|
|
break;
|
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
switch (value >> 8) {
|
|
case USB_DT_DEVICE:
|
|
BX_DEBUG(("USB_REQ_GET_DESCRIPTOR: Device"));
|
|
memcpy(data, d.dev_descriptor, d.device_desc_size);
|
|
ret = d.device_desc_size;
|
|
break;
|
|
case USB_DT_CONFIG:
|
|
BX_DEBUG(("USB_REQ_GET_DESCRIPTOR: Config"));
|
|
memcpy(data, d.config_descriptor, d.config_desc_size);
|
|
ret = d.config_desc_size;
|
|
break;
|
|
case USB_DT_STRING:
|
|
BX_DEBUG(("USB_REQ_GET_DESCRIPTOR: String"));
|
|
switch(value & 0xff) {
|
|
case 0:
|
|
// language IDs
|
|
data[0] = 4;
|
|
data[1] = 3;
|
|
data[2] = 0x09;
|
|
data[3] = 0x04;
|
|
ret = 4;
|
|
break;
|
|
case 1:
|
|
// vendor description
|
|
ret = set_usb_string(data, d.vendor_desc);
|
|
break;
|
|
case 2:
|
|
// product description
|
|
ret = set_usb_string(data, d.product_desc);
|
|
break;
|
|
case 3:
|
|
// serial number
|
|
ret = set_usb_string(data, d.serial_num);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
BX_DEBUG(("USB_REQ_GET_STATUS:"));
|
|
data[0] = 0x00;
|
|
if (d.config_descriptor[7] & 0x40) {
|
|
data[0] |= (1 << USB_DEVICE_SELF_POWERED);
|
|
}
|
|
if (d.remote_wakeup) {
|
|
data[0] |= (1 << USB_DEVICE_REMOTE_WAKEUP);
|
|
}
|
|
data[1] = 0x00;
|
|
ret = 2;
|
|
break;
|
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
BX_DEBUG(("USB_REQ_GET_CONFIGURATION:"));
|
|
data[0] = d.config;
|
|
ret = 1;
|
|
break;
|
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
BX_DEBUG(("USB_REQ_SET_CONFIGURATION: value=%d", value));
|
|
d.config = value;
|
|
d.state = USB_STATE_CONFIGURED;
|
|
ret = 0;
|
|
break;
|
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
d.remote_wakeup = 0;
|
|
ret = 0;
|
|
}
|
|
break;
|
|
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
d.remote_wakeup = 1;
|
|
ret = 0;
|
|
}
|
|
break;
|
|
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
|
BX_DEBUG(("USB_REQ_GET_INTERFACE:"));
|
|
data[0] = d.interface;
|
|
ret = 1;
|
|
break;
|
|
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
|
BX_DEBUG(("USB_REQ_SET_INTERFACE: value=%d", value));
|
|
d.interface = value;
|
|
ret = 0;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void usb_device_c::register_state(bx_list_c *parent)
|
|
{
|
|
d.sr = parent;
|
|
bx_list_c *list = new bx_list_c(parent, "d", "Common USB Device State");
|
|
BXRS_DEC_PARAM_FIELD(list, addr, d.addr);
|
|
BXRS_DEC_PARAM_FIELD(list, config, d.config);
|
|
BXRS_DEC_PARAM_FIELD(list, interface, d.interface);
|
|
BXRS_DEC_PARAM_FIELD(list, state, d.state);
|
|
BXRS_DEC_PARAM_FIELD(list, remote_wakeup, d.remote_wakeup);
|
|
register_state_specific(parent);
|
|
}
|
|
|
|
// Turn on BX_DEBUG messages at connection time
|
|
void usb_device_c::set_debug_mode()
|
|
{
|
|
setonoff(LOGLEV_DEBUG, ACT_REPORT);
|
|
}
|
|
|
|
// Send an internal message to a USB device
|
|
void usb_device_c::usb_send_msg(int msg)
|
|
{
|
|
USBPacket p;
|
|
memset(&p, 0, sizeof(p));
|
|
p.pid = msg;
|
|
handle_packet(&p);
|
|
}
|
|
|
|
// Dumps the contents of a buffer to the log file
|
|
void usb_device_c::usb_dump_packet(Bit8u *data, unsigned size)
|
|
{
|
|
char buf_str[1025], temp_str[17];
|
|
|
|
if (getonoff(LOGLEV_DEBUG) == ACT_REPORT) {
|
|
BX_DEBUG(("packet hexdump (%i bytes)", size));
|
|
buf_str[0] = 0;
|
|
for (unsigned i = 0; i < size; i++) {
|
|
sprintf(temp_str, "%02X ", data[i]);
|
|
strcat(buf_str, temp_str);
|
|
if ((i % 16) == 15) {
|
|
BX_DEBUG(("%s", buf_str));
|
|
buf_str[0] = 0;
|
|
}
|
|
}
|
|
if (strlen(buf_str) > 0) BX_DEBUG(("%s", buf_str));
|
|
}
|
|
}
|
|
|
|
int usb_device_c::set_usb_string(Bit8u *buf, const char *str)
|
|
{
|
|
size_t len, i;
|
|
Bit8u *q;
|
|
|
|
q = buf;
|
|
len = strlen(str);
|
|
if (len > 32) {
|
|
*q = 0;
|
|
return 0;
|
|
}
|
|
*q++ = (Bit8u)(2 * len + 2);
|
|
*q++ = 3;
|
|
for(i = 0; i < len; i++) {
|
|
*q++ = str[i];
|
|
*q++ = 0;
|
|
}
|
|
return q - buf;
|
|
}
|
|
|
|
#endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
|