usb: -usbdevice cleanup and un-deprecation.
usb: split usb-storage. usb: misc fixes and cleanups. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmBPoTQACgkQTLbY7tPo cThrXxAAjAm9UOfcQNWhQZZlyHfZGCO+tx7KQE7XMocpcfsiaccs7t+EhHexzUel 0cjfTYYt0B3Lf9zpYH9kE9vH84cUvkrV7VP+4TtDNDUIMuJrbNCWZ7Wm8x4Q4IR6 YV9FisSZOqHoRU+kzh+jDkRNC+XzZGMFnoLN2cJRNvSOff25PVs8YsUVWeYKYcnU drVE8lYW+z8AB5M5aiHiJlzXVBwIS/bj/UZmF0ddIuWPyRLPMcnYxEVz2iUkSg6P HMjCexacCAJ2Ag48REu5cmHjyWBEOlcGq4Lvt1x84ZbWhb8rKQlajrr1vRDE1kgH OEMhaIzBxOLwdte4cENADpWxr+m2RiG1/OpRbHUmYJCDxvi0rO1uKWfLawd0tQ2j yYCo+cSuLMSwXjt18yu5NM4Cudc/UrOaoP+ZuCBgDbxIoQH/R46j+3ZThF5tayWx S462j1pwDl4Sc95zWPM7bJciiQognKE0aYHWXD950s3poYapLnGono3CU9QeTR8f pDCSDnBo2NZbFkDQxG9ZfodxxghWr3WwNEIw0JCP2AuUlU4Ndjt/fMiYyJUtF5bD zaVjQNd21NdTgwIipmTKaUUJRDMDU95SHHQm18AVv04oTP+qVEj/j2oebx1k0HtO CZrIZXU6ak6qe43ATZFHlOLryWllhbHHvSiis4357eRNEI8oL/o= =yqef -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/usb-20210315-pull-request' into staging usb: -usbdevice cleanup and un-deprecation. usb: split usb-storage. usb: misc fixes and cleanups. # gpg: Signature made Mon 15 Mar 2021 18:02:28 GMT # gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/usb-20210315-pull-request: usb/storage: clear csw on reset usb/storage: add kconfig symbols usb/storage move usb-storage device to separate source file usb/storage: move usb-bot device to separate source file usb/storage: move declarations to usb/msd.h header hw/usb: Extract VT82C686 UHCI PCI function into a new unit hw/usb/hcd-uhci: Expose generic prototypes to local header hw/southbridge: Add missing Kconfig dependency VT82C686 on USB_UHCI usb: Document the missing -usbdevice options usb: Un-deprecate -usbdevice (except for -usbdevice audio which gets removed) usb: remove '-usbdevice u2f-key' usb: remove support for -usbdevice parameters hw/usb/bus: Remove the "full-path" property Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5b7f5586d1
|
@ -1180,6 +1180,7 @@ S: Odd Fixes
|
||||||
F: hw/mips/fuloong2e.c
|
F: hw/mips/fuloong2e.c
|
||||||
F: hw/isa/vt82c686.c
|
F: hw/isa/vt82c686.c
|
||||||
F: hw/pci-host/bonito.c
|
F: hw/pci-host/bonito.c
|
||||||
|
F: hw/usb/vt82c686-uhci-pci.c
|
||||||
F: include/hw/isa/vt82c686.h
|
F: include/hw/isa/vt82c686.h
|
||||||
|
|
||||||
Loongson-3 virtual platforms
|
Loongson-3 virtual platforms
|
||||||
|
|
|
@ -21,15 +21,6 @@ deprecated.
|
||||||
System emulator command line arguments
|
System emulator command line arguments
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
``-usbdevice`` (since 2.10.0)
|
|
||||||
'''''''''''''''''''''''''''''
|
|
||||||
|
|
||||||
The ``-usbdevice DEV`` argument is now a synonym for setting
|
|
||||||
the ``-device usb-DEV`` argument instead. The deprecated syntax
|
|
||||||
would automatically enable USB support on the machine type.
|
|
||||||
If using the new syntax, USB support must be explicitly
|
|
||||||
enabled via the ``-machine usb=on`` argument.
|
|
||||||
|
|
||||||
``-drive file=json:{...{'driver':'file'}}`` (since 3.0)
|
``-drive file=json:{...{'driver':'file'}}`` (since 3.0)
|
||||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,14 @@ or ``-display default,show-cursor=on`` instead.
|
||||||
QEMU 5.0 introduced an alternative syntax to specify the size of the translation
|
QEMU 5.0 introduced an alternative syntax to specify the size of the translation
|
||||||
block cache, ``-accel tcg,tb-size=``.
|
block cache, ``-accel tcg,tb-size=``.
|
||||||
|
|
||||||
|
``-usbdevice audio`` (removed in 6.0)
|
||||||
|
'''''''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
This option lacked the possibility to specify an audio backend device.
|
||||||
|
Use ``-device usb-audio`` now instead (and specify a corresponding USB
|
||||||
|
host controller or ``-usb`` if necessary).
|
||||||
|
|
||||||
|
|
||||||
QEMU Machine Protocol (QMP) commands
|
QEMU Machine Protocol (QMP) commands
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ config VT82C686
|
||||||
select ACPI_SMBUS
|
select ACPI_SMBUS
|
||||||
select SERIAL_ISA
|
select SERIAL_ISA
|
||||||
select FDC
|
select FDC
|
||||||
|
select USB_UHCI
|
||||||
|
|
||||||
config SMC37C669
|
config SMC37C669
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -66,11 +66,22 @@ config USB_TABLET_WACOM
|
||||||
default y
|
default y
|
||||||
depends on USB
|
depends on USB
|
||||||
|
|
||||||
|
config USB_STORAGE_CORE
|
||||||
|
bool
|
||||||
|
depends on USB
|
||||||
|
select SCSI
|
||||||
|
|
||||||
|
config USB_STORAGE_CLASSIC
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
depends on USB
|
||||||
|
select USB_STORAGE_CORE
|
||||||
|
|
||||||
config USB_STORAGE_BOT
|
config USB_STORAGE_BOT
|
||||||
bool
|
bool
|
||||||
default y
|
default y
|
||||||
depends on USB
|
depends on USB
|
||||||
select SCSI
|
select USB_STORAGE_CORE
|
||||||
|
|
||||||
config USB_STORAGE_UAS
|
config USB_STORAGE_UAS
|
||||||
bool
|
bool
|
||||||
|
|
39
hw/usb/bus.c
39
hw/usb/bus.c
|
@ -19,8 +19,6 @@ static void usb_qdev_unrealize(DeviceState *qdev);
|
||||||
static Property usb_props[] = {
|
static Property usb_props[] = {
|
||||||
DEFINE_PROP_STRING("port", USBDevice, port_path),
|
DEFINE_PROP_STRING("port", USBDevice, port_path),
|
||||||
DEFINE_PROP_STRING("serial", USBDevice, serial),
|
DEFINE_PROP_STRING("serial", USBDevice, serial),
|
||||||
DEFINE_PROP_BIT("full-path", USBDevice, flags,
|
|
||||||
USB_DEV_FLAG_FULL_PATH, true),
|
|
||||||
DEFINE_PROP_BIT("msos-desc", USBDevice, flags,
|
DEFINE_PROP_BIT("msos-desc", USBDevice, flags,
|
||||||
USB_DEV_FLAG_MSOS_DESC_ENABLE, true),
|
USB_DEV_FLAG_MSOS_DESC_ENABLE, true),
|
||||||
DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename),
|
DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename),
|
||||||
|
@ -312,13 +310,13 @@ typedef struct LegacyUSBFactory
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *usbdevice_name;
|
const char *usbdevice_name;
|
||||||
USBDevice *(*usbdevice_init)(const char *params);
|
USBDevice *(*usbdevice_init)(void);
|
||||||
} LegacyUSBFactory;
|
} LegacyUSBFactory;
|
||||||
|
|
||||||
static GSList *legacy_usb_factory;
|
static GSList *legacy_usb_factory;
|
||||||
|
|
||||||
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
||||||
USBDevice *(*usbdevice_init)(const char *params))
|
USBDevice *(*usbdevice_init)(void))
|
||||||
{
|
{
|
||||||
if (usbdevice_name) {
|
if (usbdevice_name) {
|
||||||
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
|
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
|
||||||
|
@ -596,11 +594,8 @@ static char *usb_get_dev_path(DeviceState *qdev)
|
||||||
{
|
{
|
||||||
USBDevice *dev = USB_DEVICE(qdev);
|
USBDevice *dev = USB_DEVICE(qdev);
|
||||||
DeviceState *hcd = qdev->parent_bus->parent;
|
DeviceState *hcd = qdev->parent_bus->parent;
|
||||||
char *id = NULL;
|
char *id = qdev_get_dev_path(hcd);
|
||||||
|
|
||||||
if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) {
|
|
||||||
id = qdev_get_dev_path(hcd);
|
|
||||||
}
|
|
||||||
if (id) {
|
if (id) {
|
||||||
char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
|
char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
|
||||||
g_free(id);
|
g_free(id);
|
||||||
|
@ -663,27 +658,17 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handle legacy -usbdevice cmd line option */
|
/* handle legacy -usbdevice cmd line option */
|
||||||
USBDevice *usbdevice_create(const char *cmdline)
|
USBDevice *usbdevice_create(const char *driver)
|
||||||
{
|
{
|
||||||
USBBus *bus = usb_bus_find(-1 /* any */);
|
USBBus *bus = usb_bus_find(-1 /* any */);
|
||||||
LegacyUSBFactory *f = NULL;
|
LegacyUSBFactory *f = NULL;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
GSList *i;
|
GSList *i;
|
||||||
char driver[32];
|
|
||||||
const char *params;
|
|
||||||
int len;
|
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
|
|
||||||
params = strchr(cmdline,':');
|
if (strchr(driver, ':')) {
|
||||||
if (params) {
|
error_report("usbdevice parameters are not supported anymore");
|
||||||
params++;
|
return NULL;
|
||||||
len = params - cmdline;
|
|
||||||
if (len > sizeof(driver))
|
|
||||||
len = sizeof(driver);
|
|
||||||
pstrcpy(driver, len, cmdline);
|
|
||||||
} else {
|
|
||||||
params = "";
|
|
||||||
pstrcpy(driver, sizeof(driver), cmdline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = legacy_usb_factory; i; i = i->next) {
|
for (i = legacy_usb_factory; i; i = i->next) {
|
||||||
|
@ -707,15 +692,7 @@ USBDevice *usbdevice_create(const char *cmdline)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->usbdevice_init) {
|
dev = f->usbdevice_init ? f->usbdevice_init() : usb_new(f->name);
|
||||||
dev = f->usbdevice_init(params);
|
|
||||||
} else {
|
|
||||||
if (*params) {
|
|
||||||
error_report("usbdevice %s accepts no params", driver);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
dev = usb_new(f->name);
|
|
||||||
}
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
error_report("Failed to create USB device '%s'", f->name);
|
error_report("Failed to create USB device '%s'", f->name);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -1024,7 +1024,6 @@ static const TypeInfo usb_audio_info = {
|
||||||
static void usb_audio_register_types(void)
|
static void usb_audio_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&usb_audio_info);
|
type_register_static(&usb_audio_info);
|
||||||
usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(usb_audio_register_types)
|
type_init(usb_audio_register_types)
|
||||||
|
|
|
@ -614,7 +614,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
|
||||||
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static USBDevice *usb_braille_init(const char *unused)
|
static USBDevice *usb_braille_init(void)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
Chardev *cdrv;
|
Chardev *cdrv;
|
||||||
|
|
63
hw/usb/dev-storage-bot.c
Normal file
63
hw/usb/dev-storage-bot.c
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* USB Mass Storage Device emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 CodeSourcery.
|
||||||
|
* Written by Paul Brook
|
||||||
|
*
|
||||||
|
* This code is licensed under the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/typedefs.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "hw/usb.h"
|
||||||
|
#include "hw/usb/desc.h"
|
||||||
|
#include "hw/usb/msd.h"
|
||||||
|
|
||||||
|
static const struct SCSIBusInfo usb_msd_scsi_info_bot = {
|
||||||
|
.tcq = false,
|
||||||
|
.max_target = 0,
|
||||||
|
.max_lun = 15,
|
||||||
|
|
||||||
|
.transfer_data = usb_msd_transfer_data,
|
||||||
|
.complete = usb_msd_command_complete,
|
||||||
|
.cancel = usb_msd_request_cancelled,
|
||||||
|
.load_request = usb_msd_load_request,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void usb_msd_bot_realize(USBDevice *dev, Error **errp)
|
||||||
|
{
|
||||||
|
MSDState *s = USB_STORAGE_DEV(dev);
|
||||||
|
DeviceState *d = DEVICE(dev);
|
||||||
|
|
||||||
|
usb_desc_create_serial(dev);
|
||||||
|
usb_desc_init(dev);
|
||||||
|
if (d->hotplugged) {
|
||||||
|
s->dev.auto_attach = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
|
||||||
|
&usb_msd_scsi_info_bot, NULL);
|
||||||
|
usb_msd_handle_reset(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
uc->realize = usb_msd_bot_realize;
|
||||||
|
uc->attached_settable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo bot_info = {
|
||||||
|
.name = "usb-bot",
|
||||||
|
.parent = TYPE_USB_STORAGE,
|
||||||
|
.class_init = usb_msd_class_bot_initfn,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&bot_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(register_types)
|
156
hw/usb/dev-storage-classic.c
Normal file
156
hw/usb/dev-storage-classic.c
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* USB Mass Storage Device emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 CodeSourcery.
|
||||||
|
* Written by Paul Brook
|
||||||
|
*
|
||||||
|
* This code is licensed under the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/typedefs.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/visitor.h"
|
||||||
|
#include "hw/usb.h"
|
||||||
|
#include "hw/usb/desc.h"
|
||||||
|
#include "hw/usb/msd.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
|
|
||||||
|
static const struct SCSIBusInfo usb_msd_scsi_info_storage = {
|
||||||
|
.tcq = false,
|
||||||
|
.max_target = 0,
|
||||||
|
.max_lun = 0,
|
||||||
|
|
||||||
|
.transfer_data = usb_msd_transfer_data,
|
||||||
|
.complete = usb_msd_command_complete,
|
||||||
|
.cancel = usb_msd_request_cancelled,
|
||||||
|
.load_request = usb_msd_load_request,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
|
||||||
|
{
|
||||||
|
MSDState *s = USB_STORAGE_DEV(dev);
|
||||||
|
BlockBackend *blk = s->conf.blk;
|
||||||
|
SCSIDevice *scsi_dev;
|
||||||
|
|
||||||
|
if (!blk) {
|
||||||
|
error_setg(errp, "drive property not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blkconf_blocksizes(&s->conf, errp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk),
|
||||||
|
true, errp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hack alert: this pretends to be a block device, but it's really
|
||||||
|
* a SCSI bus that can serve only a single device, which it
|
||||||
|
* creates automatically. But first it needs to detach from its
|
||||||
|
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
|
||||||
|
* attaches again. We also need to take another reference so that
|
||||||
|
* blk_detach_dev() doesn't free blk while we still need it.
|
||||||
|
*
|
||||||
|
* The hack is probably a bad idea.
|
||||||
|
*/
|
||||||
|
blk_ref(blk);
|
||||||
|
blk_detach_dev(blk, DEVICE(s));
|
||||||
|
s->conf.blk = NULL;
|
||||||
|
|
||||||
|
usb_desc_create_serial(dev);
|
||||||
|
usb_desc_init(dev);
|
||||||
|
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
|
||||||
|
&usb_msd_scsi_info_storage, NULL);
|
||||||
|
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
|
||||||
|
s->conf.bootindex, s->conf.share_rw,
|
||||||
|
s->conf.rerror, s->conf.werror,
|
||||||
|
dev->serial,
|
||||||
|
errp);
|
||||||
|
blk_unref(blk);
|
||||||
|
if (!scsi_dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usb_msd_handle_reset(dev);
|
||||||
|
s->scsi_dev = scsi_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property msd_properties[] = {
|
||||||
|
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
|
||||||
|
DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf),
|
||||||
|
DEFINE_PROP_BOOL("removable", MSDState, removable, false),
|
||||||
|
DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
uc->realize = usb_msd_storage_realize;
|
||||||
|
device_class_set_props(dc, msd_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
|
||||||
|
void *opaque, Error **errp)
|
||||||
|
{
|
||||||
|
USBDevice *dev = USB_DEVICE(obj);
|
||||||
|
MSDState *s = USB_STORAGE_DEV(dev);
|
||||||
|
|
||||||
|
visit_type_int32(v, name, &s->conf.bootindex, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
|
||||||
|
void *opaque, Error **errp)
|
||||||
|
{
|
||||||
|
USBDevice *dev = USB_DEVICE(obj);
|
||||||
|
MSDState *s = USB_STORAGE_DEV(dev);
|
||||||
|
int32_t boot_index;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (!visit_type_int32(v, name, &boot_index, errp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* check whether bootindex is present in fw_boot_order list */
|
||||||
|
check_boot_index(boot_index, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* change bootindex to a new one */
|
||||||
|
s->conf.bootindex = boot_index;
|
||||||
|
|
||||||
|
if (s->scsi_dev) {
|
||||||
|
object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index,
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_msd_instance_init(Object *obj)
|
||||||
|
{
|
||||||
|
object_property_add(obj, "bootindex", "int32",
|
||||||
|
usb_msd_get_bootindex,
|
||||||
|
usb_msd_set_bootindex, NULL, NULL);
|
||||||
|
object_property_set_int(obj, "bootindex", -1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo msd_info = {
|
||||||
|
.name = "usb-storage",
|
||||||
|
.parent = TYPE_USB_STORAGE,
|
||||||
|
.class_init = usb_msd_class_storage_initfn,
|
||||||
|
.instance_init = usb_msd_instance_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&msd_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(register_types)
|
|
@ -14,13 +14,11 @@
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "hw/usb.h"
|
#include "hw/usb.h"
|
||||||
|
#include "hw/usb/msd.h"
|
||||||
#include "desc.h"
|
#include "desc.h"
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "hw/scsi/scsi.h"
|
#include "hw/scsi/scsi.h"
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
#include "sysemu/sysemu.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
|
||||||
#include "qapi/visitor.h"
|
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
@ -29,43 +27,6 @@
|
||||||
#define MassStorageReset 0xff
|
#define MassStorageReset 0xff
|
||||||
#define GetMaxLun 0xfe
|
#define GetMaxLun 0xfe
|
||||||
|
|
||||||
enum USBMSDMode {
|
|
||||||
USB_MSDM_CBW, /* Command Block. */
|
|
||||||
USB_MSDM_DATAOUT, /* Transfer data to device. */
|
|
||||||
USB_MSDM_DATAIN, /* Transfer data from device. */
|
|
||||||
USB_MSDM_CSW /* Command Status. */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct usb_msd_csw {
|
|
||||||
uint32_t sig;
|
|
||||||
uint32_t tag;
|
|
||||||
uint32_t residue;
|
|
||||||
uint8_t status;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MSDState {
|
|
||||||
USBDevice dev;
|
|
||||||
enum USBMSDMode mode;
|
|
||||||
uint32_t scsi_off;
|
|
||||||
uint32_t scsi_len;
|
|
||||||
uint32_t data_len;
|
|
||||||
struct usb_msd_csw csw;
|
|
||||||
SCSIRequest *req;
|
|
||||||
SCSIBus bus;
|
|
||||||
/* For async completion. */
|
|
||||||
USBPacket *packet;
|
|
||||||
/* usb-storage only */
|
|
||||||
BlockConf conf;
|
|
||||||
bool removable;
|
|
||||||
bool commandlog;
|
|
||||||
SCSIDevice *scsi_dev;
|
|
||||||
};
|
|
||||||
typedef struct MSDState MSDState;
|
|
||||||
|
|
||||||
#define TYPE_USB_STORAGE "usb-storage-dev"
|
|
||||||
DECLARE_INSTANCE_CHECKER(MSDState, USB_STORAGE_DEV,
|
|
||||||
TYPE_USB_STORAGE)
|
|
||||||
|
|
||||||
struct usb_msd_cbw {
|
struct usb_msd_cbw {
|
||||||
uint32_t sig;
|
uint32_t sig;
|
||||||
uint32_t tag;
|
uint32_t tag;
|
||||||
|
@ -259,7 +220,7 @@ static void usb_msd_packet_complete(MSDState *s)
|
||||||
usb_packet_complete(&s->dev, p);
|
usb_packet_complete(&s->dev, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||||
{
|
{
|
||||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||||
USBPacket *p = s->packet;
|
USBPacket *p = s->packet;
|
||||||
|
@ -277,7 +238,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_command_complete(SCSIRequest *req, size_t resid)
|
void usb_msd_command_complete(SCSIRequest *req, size_t resid)
|
||||||
{
|
{
|
||||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||||
USBPacket *p = s->packet;
|
USBPacket *p = s->packet;
|
||||||
|
@ -320,7 +281,7 @@ static void usb_msd_command_complete(SCSIRequest *req, size_t resid)
|
||||||
s->req = NULL;
|
s->req = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_request_cancelled(SCSIRequest *req)
|
void usb_msd_request_cancelled(SCSIRequest *req)
|
||||||
{
|
{
|
||||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||||
|
|
||||||
|
@ -337,7 +298,7 @@ static void usb_msd_request_cancelled(SCSIRequest *req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_handle_reset(USBDevice *dev)
|
void usb_msd_handle_reset(USBDevice *dev)
|
||||||
{
|
{
|
||||||
MSDState *s = (MSDState *)dev;
|
MSDState *s = (MSDState *)dev;
|
||||||
|
|
||||||
|
@ -352,6 +313,7 @@ static void usb_msd_handle_reset(USBDevice *dev)
|
||||||
usb_msd_packet_complete(s);
|
usb_msd_packet_complete(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&s->csw, 0, sizeof(s->csw));
|
||||||
s->mode = USB_MSDM_CBW;
|
s->mode = USB_MSDM_CBW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,7 +527,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
|
void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
|
||||||
{
|
{
|
||||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||||
|
|
||||||
|
@ -576,95 +538,6 @@ static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct SCSIBusInfo usb_msd_scsi_info_storage = {
|
|
||||||
.tcq = false,
|
|
||||||
.max_target = 0,
|
|
||||||
.max_lun = 0,
|
|
||||||
|
|
||||||
.transfer_data = usb_msd_transfer_data,
|
|
||||||
.complete = usb_msd_command_complete,
|
|
||||||
.cancel = usb_msd_request_cancelled,
|
|
||||||
.load_request = usb_msd_load_request,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct SCSIBusInfo usb_msd_scsi_info_bot = {
|
|
||||||
.tcq = false,
|
|
||||||
.max_target = 0,
|
|
||||||
.max_lun = 15,
|
|
||||||
|
|
||||||
.transfer_data = usb_msd_transfer_data,
|
|
||||||
.complete = usb_msd_command_complete,
|
|
||||||
.cancel = usb_msd_request_cancelled,
|
|
||||||
.load_request = usb_msd_load_request,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
|
|
||||||
{
|
|
||||||
MSDState *s = USB_STORAGE_DEV(dev);
|
|
||||||
BlockBackend *blk = s->conf.blk;
|
|
||||||
SCSIDevice *scsi_dev;
|
|
||||||
|
|
||||||
if (!blk) {
|
|
||||||
error_setg(errp, "drive property not set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blkconf_blocksizes(&s->conf, errp)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk),
|
|
||||||
true, errp)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hack alert: this pretends to be a block device, but it's really
|
|
||||||
* a SCSI bus that can serve only a single device, which it
|
|
||||||
* creates automatically. But first it needs to detach from its
|
|
||||||
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
|
|
||||||
* attaches again. We also need to take another reference so that
|
|
||||||
* blk_detach_dev() doesn't free blk while we still need it.
|
|
||||||
*
|
|
||||||
* The hack is probably a bad idea.
|
|
||||||
*/
|
|
||||||
blk_ref(blk);
|
|
||||||
blk_detach_dev(blk, DEVICE(s));
|
|
||||||
s->conf.blk = NULL;
|
|
||||||
|
|
||||||
usb_desc_create_serial(dev);
|
|
||||||
usb_desc_init(dev);
|
|
||||||
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
|
|
||||||
&usb_msd_scsi_info_storage, NULL);
|
|
||||||
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
|
|
||||||
s->conf.bootindex, s->conf.share_rw,
|
|
||||||
s->conf.rerror, s->conf.werror,
|
|
||||||
dev->serial,
|
|
||||||
errp);
|
|
||||||
blk_unref(blk);
|
|
||||||
if (!scsi_dev) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
usb_msd_handle_reset(dev);
|
|
||||||
s->scsi_dev = scsi_dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_msd_bot_realize(USBDevice *dev, Error **errp)
|
|
||||||
{
|
|
||||||
MSDState *s = USB_STORAGE_DEV(dev);
|
|
||||||
DeviceState *d = DEVICE(dev);
|
|
||||||
|
|
||||||
usb_desc_create_serial(dev);
|
|
||||||
usb_desc_init(dev);
|
|
||||||
if (d->hotplugged) {
|
|
||||||
s->dev.auto_attach = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
|
|
||||||
&usb_msd_scsi_info_bot, NULL);
|
|
||||||
usb_msd_handle_reset(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const VMStateDescription vmstate_usb_msd = {
|
static const VMStateDescription vmstate_usb_msd = {
|
||||||
.name = "usb-storage",
|
.name = "usb-storage",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
|
@ -683,14 +556,6 @@ static const VMStateDescription vmstate_usb_msd = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static Property msd_properties[] = {
|
|
||||||
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
|
|
||||||
DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf),
|
|
||||||
DEFINE_PROP_BOOL("removable", MSDState, removable, false),
|
|
||||||
DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
|
static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
@ -708,52 +573,6 @@ static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
|
||||||
dc->vmsd = &vmstate_usb_msd;
|
dc->vmsd = &vmstate_usb_msd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
||||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
uc->realize = usb_msd_storage_realize;
|
|
||||||
device_class_set_props(dc, msd_properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
|
|
||||||
void *opaque, Error **errp)
|
|
||||||
{
|
|
||||||
USBDevice *dev = USB_DEVICE(obj);
|
|
||||||
MSDState *s = USB_STORAGE_DEV(dev);
|
|
||||||
|
|
||||||
visit_type_int32(v, name, &s->conf.bootindex, errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
|
|
||||||
void *opaque, Error **errp)
|
|
||||||
{
|
|
||||||
USBDevice *dev = USB_DEVICE(obj);
|
|
||||||
MSDState *s = USB_STORAGE_DEV(dev);
|
|
||||||
int32_t boot_index;
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
if (!visit_type_int32(v, name, &boot_index, errp)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* check whether bootindex is present in fw_boot_order list */
|
|
||||||
check_boot_index(boot_index, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* change bootindex to a new one */
|
|
||||||
s->conf.bootindex = boot_index;
|
|
||||||
|
|
||||||
if (s->scsi_dev) {
|
|
||||||
object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index,
|
|
||||||
&error_abort);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo usb_storage_dev_type_info = {
|
static const TypeInfo usb_storage_dev_type_info = {
|
||||||
.name = TYPE_USB_STORAGE,
|
.name = TYPE_USB_STORAGE,
|
||||||
.parent = TYPE_USB_DEVICE,
|
.parent = TYPE_USB_DEVICE,
|
||||||
|
@ -762,40 +581,9 @@ static const TypeInfo usb_storage_dev_type_info = {
|
||||||
.class_init = usb_msd_class_initfn_common,
|
.class_init = usb_msd_class_initfn_common,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_msd_instance_init(Object *obj)
|
|
||||||
{
|
|
||||||
object_property_add(obj, "bootindex", "int32",
|
|
||||||
usb_msd_get_bootindex,
|
|
||||||
usb_msd_set_bootindex, NULL, NULL);
|
|
||||||
object_property_set_int(obj, "bootindex", -1, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data)
|
|
||||||
{
|
|
||||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
uc->realize = usb_msd_bot_realize;
|
|
||||||
uc->attached_settable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo msd_info = {
|
|
||||||
.name = "usb-storage",
|
|
||||||
.parent = TYPE_USB_STORAGE,
|
|
||||||
.class_init = usb_msd_class_storage_initfn,
|
|
||||||
.instance_init = usb_msd_instance_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const TypeInfo bot_info = {
|
|
||||||
.name = "usb-bot",
|
|
||||||
.parent = TYPE_USB_STORAGE,
|
|
||||||
.class_init = usb_msd_class_bot_initfn,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void usb_msd_register_types(void)
|
static void usb_msd_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&usb_storage_dev_type_info);
|
type_register_static(&usb_storage_dev_type_info);
|
||||||
type_register_static(&msd_info);
|
|
||||||
type_register_static(&bot_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(usb_msd_register_types)
|
type_init(usb_msd_register_types)
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
#include "hcd-uhci.h"
|
||||||
|
|
||||||
#define FRAME_TIMER_FREQ 1000
|
#define FRAME_TIMER_FREQ 1000
|
||||||
|
|
||||||
|
@ -50,8 +51,6 @@
|
||||||
|
|
||||||
#define MAX_FRAMES_PER_TICK (QH_VALID / 2)
|
#define MAX_FRAMES_PER_TICK (QH_VALID / 2)
|
||||||
|
|
||||||
#define NB_PORTS 2
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
TD_RESULT_STOP_FRAME = 10,
|
TD_RESULT_STOP_FRAME = 10,
|
||||||
TD_RESULT_COMPLETE,
|
TD_RESULT_COMPLETE,
|
||||||
|
@ -62,20 +61,8 @@ enum {
|
||||||
|
|
||||||
typedef struct UHCIState UHCIState;
|
typedef struct UHCIState UHCIState;
|
||||||
typedef struct UHCIAsync UHCIAsync;
|
typedef struct UHCIAsync UHCIAsync;
|
||||||
typedef struct UHCIQueue UHCIQueue;
|
|
||||||
typedef struct UHCIInfo UHCIInfo;
|
|
||||||
typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass;
|
typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass;
|
||||||
|
|
||||||
struct UHCIInfo {
|
|
||||||
const char *name;
|
|
||||||
uint16_t vendor_id;
|
|
||||||
uint16_t device_id;
|
|
||||||
uint8_t revision;
|
|
||||||
uint8_t irq_pin;
|
|
||||||
void (*realize)(PCIDevice *dev, Error **errp);
|
|
||||||
bool unplug;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UHCIPCIDeviceClass {
|
struct UHCIPCIDeviceClass {
|
||||||
PCIDeviceClass parent_class;
|
PCIDeviceClass parent_class;
|
||||||
UHCIInfo info;
|
UHCIInfo info;
|
||||||
|
@ -107,43 +94,6 @@ struct UHCIQueue {
|
||||||
int8_t valid;
|
int8_t valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct UHCIPort {
|
|
||||||
USBPort port;
|
|
||||||
uint16_t ctrl;
|
|
||||||
} UHCIPort;
|
|
||||||
|
|
||||||
struct UHCIState {
|
|
||||||
PCIDevice dev;
|
|
||||||
MemoryRegion io_bar;
|
|
||||||
USBBus bus; /* Note unused when we're a companion controller */
|
|
||||||
uint16_t cmd; /* cmd register */
|
|
||||||
uint16_t status;
|
|
||||||
uint16_t intr; /* interrupt enable register */
|
|
||||||
uint16_t frnum; /* frame number */
|
|
||||||
uint32_t fl_base_addr; /* frame list base address */
|
|
||||||
uint8_t sof_timing;
|
|
||||||
uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
|
|
||||||
int64_t expire_time;
|
|
||||||
QEMUTimer *frame_timer;
|
|
||||||
QEMUBH *bh;
|
|
||||||
uint32_t frame_bytes;
|
|
||||||
uint32_t frame_bandwidth;
|
|
||||||
bool completions_only;
|
|
||||||
UHCIPort ports[NB_PORTS];
|
|
||||||
|
|
||||||
/* Interrupts that should be raised at the end of the current frame. */
|
|
||||||
uint32_t pending_int_mask;
|
|
||||||
|
|
||||||
/* Active packets */
|
|
||||||
QTAILQ_HEAD(, UHCIQueue) queues;
|
|
||||||
uint8_t num_ports_vmstate;
|
|
||||||
|
|
||||||
/* Properties */
|
|
||||||
char *masterbus;
|
|
||||||
uint32_t firstport;
|
|
||||||
uint32_t maxframes;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct UHCI_TD {
|
typedef struct UHCI_TD {
|
||||||
uint32_t link;
|
uint32_t link;
|
||||||
uint32_t ctrl; /* see TD_CTRL_xxx */
|
uint32_t ctrl; /* see TD_CTRL_xxx */
|
||||||
|
@ -160,10 +110,6 @@ static void uhci_async_cancel(UHCIAsync *async);
|
||||||
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
|
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
|
||||||
static void uhci_resume(void *opaque);
|
static void uhci_resume(void *opaque);
|
||||||
|
|
||||||
#define TYPE_UHCI "pci-uhci-usb"
|
|
||||||
DECLARE_INSTANCE_CHECKER(UHCIState, UHCI,
|
|
||||||
TYPE_UHCI)
|
|
||||||
|
|
||||||
static inline int32_t uhci_queue_token(UHCI_TD *td)
|
static inline int32_t uhci_queue_token(UHCI_TD *td)
|
||||||
{
|
{
|
||||||
if ((td->token & (0xf << 15)) == 0) {
|
if ((td->token & (0xf << 15)) == 0) {
|
||||||
|
@ -1213,7 +1159,7 @@ static USBPortOps uhci_port_ops = {
|
||||||
static USBBusOps uhci_bus_ops = {
|
static USBBusOps uhci_bus_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
|
void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
||||||
|
@ -1261,21 +1207,6 @@ static void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
|
||||||
pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
|
pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
|
|
||||||
{
|
|
||||||
UHCIState *s = UHCI(dev);
|
|
||||||
uint8_t *pci_conf = s->dev.config;
|
|
||||||
|
|
||||||
/* USB misc control 1/2 */
|
|
||||||
pci_set_long(pci_conf + 0x40,0x00001000);
|
|
||||||
/* PM capability */
|
|
||||||
pci_set_long(pci_conf + 0x80,0x00020001);
|
|
||||||
/* USB legacy support */
|
|
||||||
pci_set_long(pci_conf + 0xc0,0x00002000);
|
|
||||||
|
|
||||||
usb_uhci_common_realize(dev, errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_uhci_exit(PCIDevice *dev)
|
static void usb_uhci_exit(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
UHCIState *s = UHCI(dev);
|
UHCIState *s = UHCI(dev);
|
||||||
|
@ -1335,7 +1266,7 @@ static const TypeInfo uhci_pci_type_info = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void uhci_data_class_init(ObjectClass *klass, void *data)
|
void uhci_data_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
@ -1372,14 +1303,6 @@ static UHCIInfo uhci_info[] = {
|
||||||
.revision = 0x01,
|
.revision = 0x01,
|
||||||
.irq_pin = 3,
|
.irq_pin = 3,
|
||||||
.unplug = true,
|
.unplug = true,
|
||||||
},{
|
|
||||||
.name = "vt82c686b-usb-uhci",
|
|
||||||
.vendor_id = PCI_VENDOR_ID_VIA,
|
|
||||||
.device_id = PCI_DEVICE_ID_VIA_UHCI,
|
|
||||||
.revision = 0x01,
|
|
||||||
.irq_pin = 3,
|
|
||||||
.realize = usb_uhci_vt82c686b_realize,
|
|
||||||
.unplug = true,
|
|
||||||
},{
|
},{
|
||||||
.name = "ich9-usb-uhci1", /* 00:1d.0 */
|
.name = "ich9-usb-uhci1", /* 00:1d.0 */
|
||||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||||
|
|
93
hw/usb/hcd-uhci.h
Normal file
93
hw/usb/hcd-uhci.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* USB UHCI controller emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Max Krasnyansky
|
||||||
|
* Magor rewrite of the UHCI data structures parser and frame processor
|
||||||
|
* Support for fully async operation and multiple outstanding transactions
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef HW_USB_HCD_UHCI_H
|
||||||
|
#define HW_USB_HCD_UHCI_H
|
||||||
|
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
#include "hw/usb.h"
|
||||||
|
|
||||||
|
typedef struct UHCIQueue UHCIQueue;
|
||||||
|
|
||||||
|
#define NB_PORTS 2
|
||||||
|
|
||||||
|
typedef struct UHCIPort {
|
||||||
|
USBPort port;
|
||||||
|
uint16_t ctrl;
|
||||||
|
} UHCIPort;
|
||||||
|
|
||||||
|
typedef struct UHCIState {
|
||||||
|
PCIDevice dev;
|
||||||
|
MemoryRegion io_bar;
|
||||||
|
USBBus bus; /* Note unused when we're a companion controller */
|
||||||
|
uint16_t cmd; /* cmd register */
|
||||||
|
uint16_t status;
|
||||||
|
uint16_t intr; /* interrupt enable register */
|
||||||
|
uint16_t frnum; /* frame number */
|
||||||
|
uint32_t fl_base_addr; /* frame list base address */
|
||||||
|
uint8_t sof_timing;
|
||||||
|
uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
|
||||||
|
int64_t expire_time;
|
||||||
|
QEMUTimer *frame_timer;
|
||||||
|
QEMUBH *bh;
|
||||||
|
uint32_t frame_bytes;
|
||||||
|
uint32_t frame_bandwidth;
|
||||||
|
bool completions_only;
|
||||||
|
UHCIPort ports[NB_PORTS];
|
||||||
|
|
||||||
|
/* Interrupts that should be raised at the end of the current frame. */
|
||||||
|
uint32_t pending_int_mask;
|
||||||
|
|
||||||
|
/* Active packets */
|
||||||
|
QTAILQ_HEAD(, UHCIQueue) queues;
|
||||||
|
uint8_t num_ports_vmstate;
|
||||||
|
|
||||||
|
/* Properties */
|
||||||
|
char *masterbus;
|
||||||
|
uint32_t firstport;
|
||||||
|
uint32_t maxframes;
|
||||||
|
} UHCIState;
|
||||||
|
|
||||||
|
#define TYPE_UHCI "pci-uhci-usb"
|
||||||
|
DECLARE_INSTANCE_CHECKER(UHCIState, UHCI, TYPE_UHCI)
|
||||||
|
|
||||||
|
typedef struct UHCIInfo {
|
||||||
|
const char *name;
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint16_t device_id;
|
||||||
|
uint8_t revision;
|
||||||
|
uint8_t irq_pin;
|
||||||
|
void (*realize)(PCIDevice *dev, Error **errp);
|
||||||
|
bool unplug;
|
||||||
|
} UHCIInfo;
|
||||||
|
|
||||||
|
void uhci_data_class_init(ObjectClass *klass, void *data);
|
||||||
|
void usb_uhci_common_realize(PCIDevice *dev, Error **errp);
|
||||||
|
|
||||||
|
#endif
|
|
@ -32,6 +32,7 @@ softmmu_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c'))
|
softmmu_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c'))
|
softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c'))
|
softmmu_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c'))
|
||||||
specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c'))
|
specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c'))
|
||||||
specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c'))
|
specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c'))
|
||||||
|
|
||||||
|
@ -39,7 +40,9 @@ specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsyst
|
||||||
softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c'))
|
softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c'))
|
softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c'))
|
softmmu_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage.c'))
|
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c'))
|
softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c'))
|
softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c'))
|
softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c'))
|
||||||
|
|
|
@ -346,7 +346,6 @@ static const TypeInfo u2f_key_info = {
|
||||||
static void u2f_key_register_types(void)
|
static void u2f_key_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&u2f_key_info);
|
type_register_static(&u2f_key_info);
|
||||||
usb_legacy_register(TYPE_U2F_KEY, "u2f-key", NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(u2f_key_register_types)
|
type_init(u2f_key_register_types)
|
||||||
|
|
43
hw/usb/vt82c686-uhci-pci.c
Normal file
43
hw/usb/vt82c686-uhci-pci.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hcd-uhci.h"
|
||||||
|
|
||||||
|
static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
|
||||||
|
{
|
||||||
|
UHCIState *s = UHCI(dev);
|
||||||
|
uint8_t *pci_conf = s->dev.config;
|
||||||
|
|
||||||
|
/* USB misc control 1/2 */
|
||||||
|
pci_set_long(pci_conf + 0x40, 0x00001000);
|
||||||
|
/* PM capability */
|
||||||
|
pci_set_long(pci_conf + 0x80, 0x00020001);
|
||||||
|
/* USB legacy support */
|
||||||
|
pci_set_long(pci_conf + 0xc0, 0x00002000);
|
||||||
|
|
||||||
|
usb_uhci_common_realize(dev, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UHCIInfo uhci_info[] = {
|
||||||
|
{
|
||||||
|
.name = "vt82c686b-usb-uhci",
|
||||||
|
.vendor_id = PCI_VENDOR_ID_VIA,
|
||||||
|
.device_id = PCI_DEVICE_ID_VIA_UHCI,
|
||||||
|
.revision = 0x01,
|
||||||
|
.irq_pin = 3,
|
||||||
|
.realize = usb_uhci_vt82c686b_realize,
|
||||||
|
.unplug = true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeInfo vt82c686b_usb_uhci_type_info = {
|
||||||
|
.parent = TYPE_UHCI,
|
||||||
|
.name = "vt82c686b-usb-uhci",
|
||||||
|
.class_init = uhci_data_class_init,
|
||||||
|
.class_data = uhci_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vt82c686b_usb_uhci_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&vt82c686b_usb_uhci_type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(vt82c686b_usb_uhci_register_types)
|
|
@ -216,7 +216,6 @@ struct USBEndpoint {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum USBDeviceFlags {
|
enum USBDeviceFlags {
|
||||||
USB_DEV_FLAG_FULL_PATH,
|
|
||||||
USB_DEV_FLAG_IS_HOST,
|
USB_DEV_FLAG_IS_HOST,
|
||||||
USB_DEV_FLAG_MSOS_DESC_ENABLE,
|
USB_DEV_FLAG_MSOS_DESC_ENABLE,
|
||||||
USB_DEV_FLAG_MSOS_DESC_IN_USE,
|
USB_DEV_FLAG_MSOS_DESC_IN_USE,
|
||||||
|
@ -500,7 +499,7 @@ void usb_bus_new(USBBus *bus, size_t bus_size,
|
||||||
void usb_bus_release(USBBus *bus);
|
void usb_bus_release(USBBus *bus);
|
||||||
USBBus *usb_bus_find(int busnr);
|
USBBus *usb_bus_find(int busnr);
|
||||||
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
void usb_legacy_register(const char *typename, const char *usbdevice_name,
|
||||||
USBDevice *(*usbdevice_init)(const char *params));
|
USBDevice *(*usbdevice_init)(void));
|
||||||
USBDevice *usb_new(const char *name);
|
USBDevice *usb_new(const char *name);
|
||||||
bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp);
|
bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp);
|
||||||
USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
||||||
|
|
54
include/hw/usb/msd.h
Normal file
54
include/hw/usb/msd.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* USB Mass Storage Device emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 CodeSourcery.
|
||||||
|
* Written by Paul Brook
|
||||||
|
*
|
||||||
|
* This code is licensed under the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw/usb.h"
|
||||||
|
#include "hw/scsi/scsi.h"
|
||||||
|
|
||||||
|
enum USBMSDMode {
|
||||||
|
USB_MSDM_CBW, /* Command Block. */
|
||||||
|
USB_MSDM_DATAOUT, /* Transfer data to device. */
|
||||||
|
USB_MSDM_DATAIN, /* Transfer data from device. */
|
||||||
|
USB_MSDM_CSW /* Command Status. */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct usb_msd_csw {
|
||||||
|
uint32_t sig;
|
||||||
|
uint32_t tag;
|
||||||
|
uint32_t residue;
|
||||||
|
uint8_t status;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MSDState {
|
||||||
|
USBDevice dev;
|
||||||
|
enum USBMSDMode mode;
|
||||||
|
uint32_t scsi_off;
|
||||||
|
uint32_t scsi_len;
|
||||||
|
uint32_t data_len;
|
||||||
|
struct usb_msd_csw csw;
|
||||||
|
SCSIRequest *req;
|
||||||
|
SCSIBus bus;
|
||||||
|
/* For async completion. */
|
||||||
|
USBPacket *packet;
|
||||||
|
/* usb-storage only */
|
||||||
|
BlockConf conf;
|
||||||
|
bool removable;
|
||||||
|
bool commandlog;
|
||||||
|
SCSIDevice *scsi_dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct MSDState MSDState;
|
||||||
|
#define TYPE_USB_STORAGE "usb-storage-dev"
|
||||||
|
DECLARE_INSTANCE_CHECKER(MSDState, USB_STORAGE_DEV,
|
||||||
|
TYPE_USB_STORAGE)
|
||||||
|
|
||||||
|
void usb_msd_transfer_data(SCSIRequest *req, uint32_t len);
|
||||||
|
void usb_msd_command_complete(SCSIRequest *req, size_t resid);
|
||||||
|
void usb_msd_request_cancelled(SCSIRequest *req);
|
||||||
|
void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req);
|
||||||
|
void usb_msd_handle_reset(USBDevice *dev);
|
|
@ -1705,7 +1705,7 @@ ERST
|
||||||
|
|
||||||
DEFHEADING()
|
DEFHEADING()
|
||||||
|
|
||||||
DEFHEADING(USB options:)
|
DEFHEADING(USB convenience options:)
|
||||||
|
|
||||||
DEF("usb", 0, QEMU_OPTION_usb,
|
DEF("usb", 0, QEMU_OPTION_usb,
|
||||||
"-usb enable on-board USB host controller (if not enabled by default)\n",
|
"-usb enable on-board USB host controller (if not enabled by default)\n",
|
||||||
|
@ -1723,9 +1723,31 @@ DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice,
|
||||||
QEMU_ARCH_ALL)
|
QEMU_ARCH_ALL)
|
||||||
SRST
|
SRST
|
||||||
``-usbdevice devname``
|
``-usbdevice devname``
|
||||||
Add the USB device devname. Note that this option is deprecated,
|
Add the USB device devname, and enable an on-board USB controller
|
||||||
please use ``-device usb-...`` instead. See the chapter about
|
if possible and necessary (just like it can be done via
|
||||||
|
``-machine usb=on``). Note that this option is mainly intended for
|
||||||
|
the user's convenience only. More fine-grained control can be
|
||||||
|
achieved by selecting a USB host controller (if necessary) and the
|
||||||
|
desired USB device via the ``-device`` option instead. For example,
|
||||||
|
instead of using ``-usbdevice mouse`` it is possible to use
|
||||||
|
``-device qemu-xhci -device usb-mouse`` to connect the USB mouse
|
||||||
|
to a USB 3.0 controller instead (at least on machines that support
|
||||||
|
PCI and do not have an USB controller enabled by default yet).
|
||||||
|
For more details, see the chapter about
|
||||||
:ref:`Connecting USB devices` in the System Emulation Users Guide.
|
:ref:`Connecting USB devices` in the System Emulation Users Guide.
|
||||||
|
Possible devices for devname are:
|
||||||
|
|
||||||
|
``braille``
|
||||||
|
Braille device. This will use BrlAPI to display the braille
|
||||||
|
output on a real or fake device (i.e. it also creates a
|
||||||
|
corresponding ``braille`` chardev automatically beside the
|
||||||
|
``usb-braille`` USB device).
|
||||||
|
|
||||||
|
``ccid``
|
||||||
|
Smartcard reader device
|
||||||
|
|
||||||
|
``keyboard``
|
||||||
|
Standard USB keyboard. Will override the PS/2 keyboard (if present).
|
||||||
|
|
||||||
``mouse``
|
``mouse``
|
||||||
Virtual Mouse. This will override the PS/2 mouse emulation when
|
Virtual Mouse. This will override the PS/2 mouse emulation when
|
||||||
|
@ -1737,9 +1759,10 @@ SRST
|
||||||
position without having to grab the mouse. Also overrides the
|
position without having to grab the mouse. Also overrides the
|
||||||
PS/2 mouse emulation when activated.
|
PS/2 mouse emulation when activated.
|
||||||
|
|
||||||
``braille``
|
``wacom-tablet``
|
||||||
Braille device. This will use BrlAPI to display the braille
|
Wacom PenPartner USB tablet.
|
||||||
output on a real or fake device.
|
|
||||||
|
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEFHEADING()
|
DEFHEADING()
|
||||||
|
|
|
@ -3180,8 +3180,6 @@ void qemu_init(int argc, char **argv, char **envp)
|
||||||
qemu_opts_parse_noisily(olist, "usb=on", false);
|
qemu_opts_parse_noisily(olist, "usb=on", false);
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_usbdevice:
|
case QEMU_OPTION_usbdevice:
|
||||||
error_report("'-usbdevice' is deprecated, please use "
|
|
||||||
"'-device usb-...' instead");
|
|
||||||
olist = qemu_find_opts("machine");
|
olist = qemu_find_opts("machine");
|
||||||
qemu_opts_parse_noisily(olist, "usb=on", false);
|
qemu_opts_parse_noisily(olist, "usb=on", false);
|
||||||
add_device_config(DEV_USB, optarg);
|
add_device_config(DEV_USB, optarg);
|
||||||
|
|
Loading…
Reference in a new issue