diff --git a/bochs/.bochsrc b/bochs/.bochsrc
index ed3ffd65c..027c514fc 100644
--- a/bochs/.bochsrc
+++ b/bochs/.bochsrc
@@ -1176,6 +1176,7 @@ speaker: enabled=1, mode=sound, volume=15
# speed choices depend on both HC and device. The option 'debug' turns on debug
# output for the device at connection time. The option 'pcap' turns on packet
# logging in PCAP format.
+#
# For the USB 'disk' device the optionsX parameter can be used to specify an
# alternative redolog file (journal) of some image modes. For 'vvfat' mode USB
# disks the optionsX parameter can be used to specify the disk size (range
@@ -1185,6 +1186,13 @@ speaker: enabled=1, mode=sound, volume=15
# supported (can fix hw detection in some guest OS). The USB floppy also
# accepts the parameter "write_protected" with valid values 0 and 1 to select
# the access mode (default is 0).
+#
+# For a high- or super-speed USB 'disk' device the optionsX parameter can include
+# the 'proto:bbb' or 'proto:uasp' parameter specifying to use either the bulk-only
+# Protocol (default) or the USB Attached SCSI Protocol. If no such parameter
+# is given, the 'bbb' protocol is used. A Guest that doesn't support UASP
+# should revert to bbb even if the 'uasp' attribute is given. See the usb_ehci:
+# or usb_xhci: section below for an example. (Only 1 LUN is available at this time)
#=======================================================================
#usb_uhci: enabled=1
#usb_uhci: port1=mouse, port2=disk, options2="path:usbstick.img"
@@ -1214,17 +1222,36 @@ speaker: enabled=1, mode=sound, volume=15
# also available on EHCI.
#=======================================================================
#usb_ehci: enabled=1
+#usb_ehci: port1=disk, options1="speed:high, path:hdd.img, proto:bbb"
+#usb_ehci: port1=disk, options1="speed:high, path:hdd.img, proto:uasp"
#=======================================================================
# USB_XHCI:
# This option controls the presence of the USB xHCI host controller with a
-# 4-port hub. The portX parameter accepts the same device types with the
-# same syntax as the UHCI controller (see above). The optionsX parameter is
-# also available on xHCI. NOTE: port 1 and 2 are USB3 and only support
-# super-speed devices, but port 3 and 4 are USB2 and support speed settings
-# low, full and high.
+# default 4-port hub. The portX parameter accepts the same device types
+# with the same syntax as the UHCI controller (see above). The optionsX
+# parameter is also available on xHCI.
+#
+# The xHCI emulation allows you to set the number of ports used with a range
+# of 2 to 10, requiring an even numbered count.
+#
+# NOTE: The first half of the ports, (ports 1 and 2 on a 4-port hub) are
+# USB3 only and support super-speed devices. The second half ports (ports
+# 3 and 4) are USB2 and support speed settings of low, full, or high.
+# The xHCI also allows for different host controllers using the model=
+# parameter. Currently, the two allowed options are "uPD720202" and
+# "uPD720201". The first defaults to 2 sockets (4 ports) and the later
+# defaults to 4 sockets (8 ports).
#=======================================================================
-#usb_xhci: enabled=1
+#usb_xhci: enabled=1 # defaults to the uPD720202 w/4 ports
+#usb_xhci: enabled=1, n_ports=6 # defaults to the uPD720202 w/6 ports
+#usb_xhci: enabled=1, model=uPD720202 # defaults to 4 ports
+#usb_xhci: enabled=1, model=uPD720202, n_ports=6 # change to 6 ports
+#usb_xhci: enabled=1, model=uPD720201 # defaults to 8 ports
+#usb_xhci: enabled=1, model=uPD720201, n_ports=10 # change to 10 ports
+#usb_xhci: port1=disk, options1="speed:super, path:hdd.img, proto:bbb"
+#usb_xhci: port1=disk, options1="speed:super, path:hdd.img, proto:uasp"
+#usb_xhci: port3=disk, options3="speed:high, path:hdd.img, proto:uasp"
#=======================================================================
# PCIDEV:
diff --git a/bochs/config.cc b/bochs/config.cc
index c3bb12155..5ae2ab85d 100644
--- a/bochs/config.cc
+++ b/bochs/config.cc
@@ -2,7 +2,7 @@
// $Id$
/////////////////////////////////////////////////////////////////////////
//
-// Copyright (C) 2002-2021 The Bochs Project
+// Copyright (C) 2002-2023 The Bochs Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
@@ -231,6 +231,22 @@ void bx_init_usb_options(const char *usb_name, const char *pname, int maxports)
sprintf(label, "Enable %s emulation", usb_name);
sprintf(descr, "Enables the %s emulation", usb_name);
bx_param_bool_c *enabled = new bx_param_bool_c(menu, "enabled", label, descr, 1);
+
+ // xhci host controller type and number of ports
+ static const char *xhci_model_names[] = { "uPD720202", "uPD720201", NULL };
+ bx_param_enum_c *model = new bx_param_enum_c(menu,
+ "model", "HC model",
+ "Select Host Controller to emulate",
+ xhci_model_names,
+ 0, 0
+ );
+ bx_param_num_c *n_ports = new bx_param_num_c(menu,
+ "n_ports", "Number of ports",
+ "Set the number of ports for this controller",
+ -1, 10,
+ -1, 0 // -1 as a default so that we can tell if this parameter was given
+ );
+
deplist = new bx_list_c(NULL);
for (Bit8u i = 0; i < maxports; i++) {
sprintf(name, "port%u", i+1);
@@ -2261,7 +2277,7 @@ int bx_parse_usb_port_params(const char *context, const char *param,
bool devopt = 0;
int idx, plen;
char tmpname[20], newopts[BX_PATHNAME_LEN];
- char *devstr, *arg;
+ char *devstr, *arg, *pEnd;
const char *opt = NULL, *origopts;
static bool compat_mode = false;
@@ -2272,12 +2288,13 @@ int bx_parse_usb_port_params(const char *context, const char *param,
devopt = 0;
plen = 7;
}
- idx = param[plen];
- if ((idx < '1') || (idx > '9') || (param[plen + 1] != '=')) {
+ // this gets the long int number value after the string: 'port'.
+ // Ex: 'port15=' will produce 15 for idx. We allow up to 30 for now.
+ idx = (int) strtol(¶m[plen], &pEnd, 10);
+ if ((idx < 1) || (idx > 30) || (pEnd[0] != '=')) {
PARSE_ERR(("%s: usb_%s: portX / optionsX parameter malformed.", context, base->get_name()));
return -1;
}
- idx -= '0';
if (idx > maxports) {
PARSE_ERR(("%s: usb_%s: port number out of range.", context, base->get_name()));
return -1;
diff --git a/bochs/doc/docbook/user/user.dbk b/bochs/doc/docbook/user/user.dbk
index 2ab38e87a..2bb746148 100644
--- a/bochs/doc/docbook/user/user.dbk
+++ b/bochs/doc/docbook/user/user.dbk
@@ -3,7 +3,7 @@
doc/docbook/user/user.dbk
$Id$
-This is the top level file for the Bochs Users Manual.
+This is the top level file for the bochs Users Manual.
================================================================
-->
@@ -208,7 +208,7 @@ environment that can better assist you in determining cause and effect relations
You can take snapshots that show you what is going on behind the scenes.
You can isolate the line that caused that crash. You can have multiple
images and compare them under a microscope.
-In these situation, Bochs could save you time and resources.
+In this situation, Bochs can save you time and resources.
@@ -549,8 +549,8 @@ currently work with.
PluginsYes
- Compiling devices, gui and drivers for network, sound and disk image
- formats as plugins is supported on Linux, MacOS X, Solaris, Cygwin, MinGW/MSYS,
+ Compiling devices, GUI and drivers for network, sound and disk image
+ formats as plugins are supported on Linux, MacOS X, Solaris, Cygwin, MinGW/MSYS,
MSVC nmake and the VS2019 IDE (workspace provided).
@@ -585,7 +585,7 @@ currently work with.
Take advantage of your SMP boxMinimal
- At present, Bochs only uses threads in some guis, in the lowlevel
+ At present, Bochs only uses threads in some GUIs, in the lowlevel
sound output code and in the Voodoo graphics adapter code. The simulation
core itself uses only one thread, so it will not run any faster on
multiprocessor hardware.
@@ -836,7 +836,7 @@ code that displays the Bochs VGA screen and handles keyboard and mouse events.
What applications are known to run inside of Bochs?
- Well, lot's of different OS's run inside of Bochs, so
+ Well, lots of different OS's run inside of Bochs, so
thousands. I'm assuming you are asking about Windows programs.
To give you a few, the following ones from the Winstone'98 tests
worked: Access 97, CorelDRAW! 7, Excel 97, Lotus 1-2-3 97, Word 97,
@@ -853,7 +853,7 @@ code that displays the Bochs VGA screen and handles keyboard and mouse events.
You should read first. Next, you can check
- if there specific instructions on how to install your (guest) OS inside of Bochs.
+ if they are specific instructions on how to install your (guest) OS inside of Bochs.
@@ -864,7 +864,7 @@ code that displays the Bochs VGA screen and handles keyboard and mouse events.
Bochs has now minimal support for the Android platform using a special
version of the SDL library. There are some external projects that use
- the offical sources with some modifications. They also offer a gui
+ the offical sources with some modifications. They also offer a GUI
for easy Bochs configuration. Other mobile platforms are not officially
supported yet.
@@ -976,7 +976,7 @@ made those changes) that have occurred to that source code. Use of SVN is
particularly common on projects with multiple developers, since SVN ensures
changes made by one developer are not accidentally removed when another
developer posts their changes to the source tree. The Bochs source code and
-documentation are available using SVN
+documentation is available using SVN
You can download SVN software and documentation from
subversion.apache.org.
@@ -1095,7 +1095,7 @@ below.
?unknown
- This file is in your bochs directory, but SVN does not know anything
+ This file is in your Bochs directory, but SVN does not know anything
about it. For example, when you compile Bochs, any files created
during the build process appear as ?.
@@ -1374,7 +1374,7 @@ The tagname tells which release you want, and it can
Entire books have been written on SVN, so there's no sense in duplicating
it all here in the Bochs documentation. Some sources of additional
-information are listed below.
+information is listed below.
The subversion.apache.org
@@ -1528,7 +1528,7 @@ download the source RPM and build a new binary RPM that is appropriate
for your platform. The command is rpmbuild --rebuild
NAME.src.rpm. As a last resort, you can run rpm with the
--nodeps option to ignore dependencies and install it
-anyway, but if it is missing important pieces it may not run properly.
+anyway, but if it is missing important pieces, it may not run properly.
@@ -1690,18 +1690,18 @@ you've copied it to the hard disk.
Once you've installed the binaries, it's probably a
good idea to drag the _dmg_top volume to trash to
unmount it, so you don't get confused and try to
-run bochs from there. Then open
-the bochs folder from wherever you installed it.
+run Bochs from there. Then open
+the Bochs folder from wherever you installed it.
-The MacOS X version of bochs requires a terminal
+The MacOS X version of Bochs requires a terminal
window to run. If you just double click on the Bochs
icon, you'll get an error message telling you to
double click on "bochs.scpt" to start
Bochs in a new terminal window. You'll need to
configure Bochs before you will get very far with
the bochs.scpt in the top folder,
-so to try out bochs open the dlxlinux folder and
+so to try out Bochs open the dlxlinux folder and
double click on the bochs.scpt icon inside.
@@ -1713,7 +1713,7 @@ do so. You will then get a new window containing
the VGA display of the simulated
machine. The new window will probably appear behind
the current terminal window, so either click on the
-bochs icon in the dock or the simulation window to
+Bochs icon in the dock or the simulation window to
bring it to the front. If you're quick
enough you'll then see the VGA BIOS screen, then
Linux uncompresses and boots, and you get a login
@@ -1884,7 +1884,7 @@ link the code, and more. After you have finished the configure step, just type
-The reason that make is so popular is that it is smart about when to compile
+The reason that Make is so popular is that it is smart about when to compile
and when not to compile. If you run make once, it compiles every file. But
when you run it again, it checks to see if any source files have been modified;
if not, there's nothing to do! For example, the &Makefile; says that
@@ -1926,8 +1926,8 @@ at least to erase any files that it created.
Once the program has been built, the next step is typically to run
make install to copy the executables, documentation, and
other required files into a public place so that all users can use it.
-By default the files are copied to some directories in /usr/local. The following
-tables shows the directories and their contents.
+By default, the files are copied to some directories in /usr/local. The following
+tables show the directories and their contents.
Installed files
@@ -2104,7 +2104,7 @@ In Bochs 1.3 and before, the X11 GUI was always the default.
--with-term
- Use text-only gui with curses library. Almost certainly
+ Use text-only GUI with curses library. Almost certainly
won't work right with the debugger or the textconfig interface.
@@ -2190,7 +2190,7 @@ SMP, x86_64 support) and the devices options (e.g. PCI, USB, Cirrus graphics).
--enable-debugger-guiyes if debugger is on
- Enable support for the gui frontend of the Bochs debugger. This feature
+ Enable support for the GUI frontend of the Bochs debugger. This feature
is supported on Windows hosts and on hosts with GTK2/GTK3 installed.
@@ -2225,9 +2225,9 @@ SMP, x86_64 support) and the devices options (e.g. PCI, USB, Cirrus graphics).
no
Compile in support for instrumentation. This allows you to collect
- instrumentation data from bochs as it executes code. You have to create
+ instrumentation data from Bochs as it executes code. You have to create
your own instrumentation library and define the instrumentation macros
- (hooks in bochs) to either call your library functions or not, depending
+ (hooks in Bochs) to either call your library functions or not, depending
upon whether you want to collect each piece of data.
@@ -2312,7 +2312,7 @@ SMP, x86_64 support) and the devices options (e.g. PCI, USB, Cirrus graphics).
no
Compile in support for SMP simulation. This allows you to boot Linux and
- maybe other OSes in SMP mode, and bochs will simulate all the different
+ maybe other OSes in SMP mode, and Bochs will simulate all the different
CPUs and communication between them. Do not expect this option to speed
up your simulation! On the contrary, it has to spend extra time simulating
the different CPUs (even if they're mostly idle) and the communication
@@ -2358,7 +2358,7 @@ SMP, x86_64 support) and the devices options (e.g. PCI, USB, Cirrus graphics).
--enable-x86-debuggerno
- X86 debugger support. If the software you run in bochs
+ X86 debugger support. If the software you run in Bochs
needs to use the x86 hardware debugging facilities such as
DR0..DR7, instruction and data breakpoints etc., then you
should use this option. Otherwise don't use it, as it
@@ -2973,9 +2973,9 @@ Compiling with MSVC nmake is not supported yet.
The SDL library version 2 is now also supported by Bochs. For some basic information
about SDL, see . The configure option to enable SDL2
support is and the configuration script is called
-sdl2-config. Note that the Bochs guis for SDL version 1.2.x
-and 2.x are mutually exclusive. The legacy SDL gui support will be removed someday.
-When using the gui library autodetection () the
+sdl2-config. Note that the Bochs GUIs for SDL version 1.2.x
+and 2.x are mutually exclusive. The legacy SDL GUI support will be removed someday.
+When using the GUI library autodetection () the
configure script probes for SDL version 2 first.
@@ -3166,7 +3166,7 @@ instructions given in .
-A collection of disk images of different operating systems can be found at
+A collection of disk images of different operating systems can be found at
. Some disk
images are the size of a floppy disk (1 meg compressed) and others are gigantic
(160 meg compressed). If you want to create a disk image yourself, please see
@@ -3333,7 +3333,7 @@ allows you to edit all the settings that control Bochs' behavior.
Depending on the platform there are up to 3 choices of configuration
interface: a text mode version called "textconfig" and two graphical versions
called "win32config" and "wx". The text mode version uses stdin/stdout or
-gui console (if available / runtime config) and is always compiled in, unless
+GUI console (if available / runtime config) and is always compiled in, unless
Bochs is compiled for wx only. The choice "win32config" is only available on
win32/win64 and it is the default on these platforms. The choice "wx" is only
available when Bochs is compiled with wxWidgets support, see .
@@ -3386,10 +3386,10 @@ behaviour. These options are supported by more than one display library:
"cmdmode" - call a headerbar button handler after pressing F7 (sdl, sdl2, win32, x)
"fullscreen" - startup in fullscreen mode (sdl, sdl2)
- "gui_debug" - use GTK debugger gui (sdl, x) / Win32 debugger gui (sdl, sdl2, win32)
+ "gui_debug" - use GTK debugger GUI (sdl, x) / Win32 debugger GUI (sdl, sdl2, win32)
"hideIPS" - disable IPS output in status bar (rfb, sdl, sdl2, term, vncsrv, win32, wx, x)
"nokeyrepeat" - turn off host keyboard repeat (sdl, sdl2, win32, x)
- "no_gui_console" - use system console instead of builtin gui console (rfb, sdl, sdl2, vncsrv, x)
+ "no_gui_console" - use system console instead of builtin GUI console (rfb, sdl, sdl2, vncsrv, x)
"timeout" - time (in seconds) to wait for client (rfb, vncsrv)
See the examples below for other currently supported options.
@@ -3503,14 +3503,14 @@ binary compiled with SMP support.
reset_on_triple_fault
-Reset the CPU when triple fault occur (highly recommended) rather than PANIC.
+Reset the CPU when a triple fault occurs (highly recommended) rather than PANIC.
Remember that if you are trying to continue after triple fault the simulation
will be completely bogus !
cpuid_limit_winnt
Determine whether to limit maximum CPUID function to 2. This mode is required
-to workaround WinNT installation and boot issues.
+to work around WinNT installation and boot issues.
mwait_is_nop
@@ -3534,7 +3534,7 @@ Emulated Instructions Per Second. This is the number of IPS that Bochs is
capable of running on your machine. You can recompile Bochs with
option enabled, to find your workstation's capability.
Measured IPS value will then be logged into your log file
-or in the status bar (if supported by the gui).
+or in the status bar (if supported by the GUI).
@@ -3628,7 +3628,7 @@ This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6.
Select SIMD instructions support.
Any of NONE/SSE/SSE2/SSE3/SSSE3/SSE4_1/SSE4_2/AVX/AVX2/AVX512 could be selected.
This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6.
-The AVX choises exists only if Bochs compiled with --enable-avx option.
+The AVX choices exists only if Bochs compiled with --enable-avx option.
sse4a
@@ -3766,7 +3766,7 @@ used all allocated host memory and wants more.
Due to limitations in the host OS, Bochs fails to allocate more than 1024MB on most 32-bit systems.
-In order to overcome this problem configure and build Bochs with
+In order to overcome this problem, configure and build Bochs with
option.
@@ -3884,7 +3884,7 @@ value is 1.
The parameter 'ddc' defines the behaviour of the DDC emulation that returns
-the monitor EDID data. By default the 'builtin' values for 'Bochs Screen'
+the monitor EDID data. By default, the 'builtin' values for 'Bochs Screen'
are used. Other choices are 'disabled' (no DDC emulation) and 'file'
(read monitor EDID from file / path name separated with a colon).
@@ -3902,7 +3902,7 @@ supported models are 'voodoo1', 'voodoo2', 'banshee' and 'voodoo3'.
The Voodoo2 support is not yet complete, but almost usable. The Banshee /
Voodoo3 support is under construction, but basically usable. The 2D/3D cards
require the vga extension option to be set to 'voodoo'. If the i440BX PCI
-chipset is selected, they can be assigned to AGP (slot #5). The gui screen
+chipset is selected, they can be assigned to AGP (slot #5). The GUI screen
update timing for all models is controlled by the related
'vga' options. See for more information.
@@ -3923,7 +3923,7 @@ This defines parameters related to the emulated keyboard.
Type of keyboard return by a "identify keyboard" command to the
keyboard controller. It must be one of "xt", "at" or "mf".
Defaults to "mf". It should be ok for almost everybody. A known
-exception is french macs, that do have a "at"-like keyboard.
+exception is French macs, that do have a "at"-like keyboard.
serial_delay
@@ -3995,7 +3995,7 @@ or 'usb_xhci' options (requires PCI and USB support).
enabled
-The Bochs gui creates mouse "events" unless the 'enabled' option is
+The Bochs GUI creates mouse "events" unless the 'enabled' option is
set to 0. The hardware emulation itself is not disabled by this.
Unless you have a particular reason for enabling the mouse by default,
it is recommended that you leave it off. You can also toggle the
@@ -4046,9 +4046,9 @@ to AGP.
advopts
-With the advanced PCI options it is possible to control the behaviour of the
+With the advanced PCI options, it is possible to control the behaviour of the
PCI chipset. These options can be specified as comma-separated values.
-By default the "Bochs i440FX" chipset enables the ACPI and HPET devices, but
+By default, the "Bochs i440FX" chipset enables the ACPI and HPET devices, but
original i440FX doesn't support them. The options 'noacpi' and 'nohpet' make
it possible to disable them. The option 'noagp' disables the incomplete AGP
subsystem of the i440BX chipset.
@@ -4112,7 +4112,7 @@ Example:
This defines a binary image file with size 128 bytes that can be loaded into
the CMOS RAM at startup. The rtc_init parameter controls whether initialize
-the RTC with values stored in the image. By default the time0 argument given
+the RTC with values stored in the image. By default, the time0 argument given
to the clock option is used. With
'rtc_init=image' the image is the source for the initial time.
@@ -4164,16 +4164,16 @@ one of the supported types.
You can set the initial status of the media to ejected
-or inserted. Usually you will want to use
+or inserted. Usually, you will want to use
inserted.
The parameter 'type' can be used to enable the floppy drive without media
-and status specified. Usually the drive type is set up based on the media type.
+and status specified. Usually, the drive type is set up based on the media type.
The optional parameter 'write_protected' can be used to control the media
-write protect switch. By default it is turned off.
+write protect switch. By default, it is turned off.
@@ -4187,7 +4187,7 @@ ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9
-These options enables up to 4 ata channels. For each channel
+These options enable up to 4 ata channels. For each channel
the two base io addresses and the irq must be specified.
ata0 and ata1 are enabled by default, with the values shown above.
@@ -4260,7 +4260,7 @@ but we don't recommend it for safety reasons. In Windows, t
Disk geometry autodetection works with images created by bximage if CHS is set
-to 0/0/0 (cylinders are calculated using heads=16 and spt=63). For other hard
+to 0/0/0 (cylinders are calculated using heads=16 and spt=63). For other hard
disk images and modes the cylinders, heads, and spt are mandatory. In all cases
the disk size reported from the image must be exactly C*H*S*512. Flat hard disk
images from other projects might store additional information at the end of the
@@ -4403,7 +4403,7 @@ Examples:
logprefix: %i%e%d
This handles the format of the string prepended to each log line.
-You may use those special tokens :
+You may use those special tokens:
%t : 11 decimal digits timer tick
%i : 8 hexadecimal digits of current cpu eip (ignored in SMP configuration)
@@ -4432,9 +4432,9 @@ info, error, and panic. Debug messages are usually only useful when writing
Bochs code or when trying to locate a problem. There may be thousands of debug
messages per second, so be careful before turning them on. Info messages tell
about interesting events that don't happen that frequently. Bochs produces an
-"error" message when it finds a condition that really shouldn't happen, but
-doesn't endanger the simulation. An example of an error might be if the
-emulated software produces an illegal disk command. Panic messages mean that
+"error" message when it finds a condition that really shouldn't happen, but
+doesn't endanger the simulation. An example of an error might be if the
+emulated software produces an illegal disk command. Panic messages mean that
Bochs cannot simulate correctly and should probably shut down.
A panic can be a configuration problem (like a misspelled bochsrc line) or an
emulation problem (like an unsupported video mode).
@@ -4443,7 +4443,7 @@ emulation problem (like an unsupported video mode).
The debug, info, error, and panic lines in the bochsrc control what Bochs will
do when it encounters each type of event. The allowed actions are: fatal
-(terminate bochs), ask (ask the user what to do), warn (show dialog with message
+(terminate Bochs), ask (ask the user what to do), warn (show dialog with message
and continue), report (print information to the console or log file), or ignore
(do nothing). The recommended settings are listed in the sample above.
@@ -4622,7 +4622,7 @@ lowlevel sound support. In this mode the 'volume' parameter can be used
to set the output volume (0 - 15). The 'system' mode is only available on
Linux and Windows. On Linux /dev/console is used for output and on Windows
the Beep() function. The 'gui' mode forwards the beep to the related
-gui methods (currently only used by the Carbon gui).
+GUI methods (currently only used by the Carbon GUI).
@@ -4777,7 +4777,7 @@ Niclist source code is in misc/niclist.c and it is included in Windows
binary releases.
SCRIPT: The script value is optional, and is the name of a script that
-is executed after bochs initialize the network interface. You can use
+is executed after Bochs initialize the network interface. You can use
this script to configure this network interface, or enable masquerading.
This is mainly useful for the tun/tap devices that only exist during
Bochs execution. The network interface name is supplied to the script
@@ -4976,14 +4976,20 @@ optionsX parameter also separated with a colon. An
option to insert/eject media is available in the runtime configuration.
-To emulate a USB floppy you can use the 'floppy' device and the path to a
+To emulate a USB floppy, you can use the 'floppy' device and the path to a
floppy image can be set with the 'path' option in the optionsX
parameter separated with a colon. To use the VVFAT image mode similar to the
legacy floppy the syntax 'path:vvfat:directory' must be used (see below).
An option to insert/eject media is available in the runtime configuration.
+A well-known, somewhat older, but still widely used Operating System must have
+the Vendor ID as an TEAC external drive. If the VendorID is not the TEAC id,
+this operating system doesn't know what to do with the drive. Therefore, use
+the optionsX="model=teac" option in the bochsrc.txt
+file to set this model. If you do not, a default model will be used.
+
-The device name 'hub' connects an external hub with max. 8 ports (default: 4)
+The device name 'hub' connects an external hub with a maximum of 8 ports (default: 4)
to the root hub. To specify the number of ports you have to use the 'ports'
option in the optionsX parameter with the value
separated with a colon. Connecting devices to the external hub ports is only
@@ -5012,9 +5018,41 @@ to specify an alternative device ID to be reported. Currently only the model
accepts the parameter "write_protected" with valid values 0 and 1 to select
the access mode (default is 0).
+
+For USB disk image emulation using the 'disk' or 'cdrom' type, an optionsX
+parameter of 'proto:bbb' or 'proto:uasp' may be used. The former tells Bochs to
+use the 'Bulk/Bulk/Bulk' protocol, aka the 'Bulk only' protocol. This is the
+standard protocol that most USB disks will use, and is used by default. The latter
+option tells Bochs to use the newer 'USB Attached SCSI' protocol, used on newer,
+more modern USB disks. If the 'proto:' parameter is not given, Bochs will default
+to the 'bbb' protocol. See the 'ehci' and 'xhci' examples below.
+
PCI support must be enabled to use USB UHCI.
+
+The BBB protocol must have a speed option of 'full', 'high', or 'super'. The UASP
+protocol can only be used on high- and super-speed devices, therefore must only be
+used with the 'usb_ehci' and 'usb_xhci' configuration options. Full-speed devices
+will default to the BBB protocol.
+
+
+Specifying the UASP option does not guarantee the Guest will use this interface.
+The Bochs emulation still gives both options. It is up to the Guest to choose which
+protocol to use. A modern, widely used OS will default to BBB for various EHCI
+controllers, and controllers that are known to have issues with this type of protocol.
+
+
+The USB emulation now checks many items for accuracy. For example, it will monitor the
+toggle bit in a Transfer Descriptor. If it is not correct, the TD will not be executed.
+If your code use to work in a Bochs emulation, but now it does not, check your toggle
+bit implementation. (This particular check can be disabled using the HANDLE_TOGGLE_CONTROL
+define in 'usb_common.h') Other checks for accuracy are checks for the 'value' and 'index'
+fields of a SETUP packet. For example, a particular request may specify that the 'value'
+field must be zero. Bochs now checks for this. Other checks include the Command Length
+field in a SCSI command request, valid values in an xHCI 'slot' and 'endpoint' context,
+as well as other checks.
+usb_ohci
@@ -5034,6 +5072,10 @@ The optionsX parameter is also available on OHCI.
Example:
usb_ehci: enabled=1, port1=tablet, options1="speed:high"
+ usb_ehci: enabled=1, port1=disk, options1="speed:high, path:hdd.img, proto:bbb"
+ usb_ehci: enabled=1, port1=disk, options1="speed:high, path:hdd.img, proto:uasp"
+ usb_ehci: enabled=1, port1=cdrom, options1="speed:high, path:bootcd.iso, proto:bbb"
+ usb_ehci: enabled=1, port1=cdrom, options1="speed:high, path:bootcd.iso, proto:uasp"
This option controls the presence of the USB EHCI host controller with a
6-port hub. The portX parameter accepts the same device types with the same
@@ -5045,14 +5087,41 @@ The optionsX parameter is also available on EHCI.
Example:
- usb_xhci: enabled=1, port1="disk:usbdisk.img"
+ usb_xhci: enabled=1, model="uPD720202", n_ports=4
+ usb_xhci: enabled=1, port1="disk:usbdisk.img" # defaults to the BBB protocol
+ usb_xhci: port1=disk, options1="speed:super, path:usbdisk.img, proto:uasp"
+ usb_xhci: port1=disk, options1="speed:super, path:usbdisk.img, proto:bbb"
+ usb_xhci: port1=cdrom, options1="speed:super, path:bootcd.iso, proto:uasp"
+ usb_xhci: port3=disk, options3="speed:high, path:usbdisk.img, proto:uasp"
+ usb_xhci: port3=disk, options3="speed:high, path:usbdisk.img, proto:bbb"
+ usb_xhci: port3=floppy, options3="speed:full, path:floppy.img, model:teac"
+ usb_xhci: port3=tablet, options3="speed:low"
-This option controls the presence of the USB xHCI host controller with a 4-port
-hub. The portX parameter accepts the same device types with the same syntax as
+This option controls the presence of the USB xHCI host controller with a default 4-port
+hub. The portX parameter accepts similar device types with a similar syntax as
the UHCI controller (see the usb_uhci option).
-The optionsX parameter is also available on xHCI. NOTE: port 1 and 2 are USB3 and
-only support super-speed devices, but port 3 and 4 are USB2 and support speed
-settings low, full and high.
+The optionsX parameter is also available on xHCI.
+
+
+The xHCI supports up to two models, the NEC uPD720202 and the NEC uPD720201. The former
+defaults to a 4-port hub (two physical sockets), while the latter defaults to an 8-port hub
+(four physical sockets). This was added for future expansion. The 'n_ports=n'
+parameter can be given to override the count of ports used. This count must be at least two
+and not more than USB_XHCI_PORTS_MAX (currently 10), and must be
+an even numbered count.
+
+
+No matter the count of ports, the first half (ports 1, 2, 3, and 4 on an 8-port hub) will
+be the USB3 ports (register sets), while the second half (ports 5, 6, 7, and 8) will be the
+USB2 ports (register sets). When the 'optionsX="speed:" option is
+used, it must contain 'super' for the first half ports (1, 2, 3, and 4) and the second half
+(5, 6, 7, and 8) must use a speed of 'low', 'full', or 'high'.
+
+
+On an xHCI, the number of ports used is the number of port register sets, one set for the USB3
+protocol and a paired set for the USB2 protocol. An 'n_ports=' specification of 4 defines
+two physical sockets.
+pcidev
@@ -5380,7 +5449,7 @@ steps are done before the simulation is running.
-During simulation, Bochs usually generates more or less log output. By default it
+During simulation, Bochs usually generates more or less log output. By default, it
is sent to the console, otherwise to the specified log file.
The amount of output can be controlled with the log options
in bochsrc, the start menu and the runtime configuration.
@@ -5524,7 +5593,7 @@ The configuration interface 'textconfig' is the text mode version of the Bochs
configuration system. It is a series of menus (using stdin/stdout) that allows
you to edit all the settings that control Bochs' behavior. If you do not write
a config_interface line, Bochs will choose it as the default for you (unless Bochs
-is compiled for Win32 or wxWidgets only). If the gui console is available at
+is compiled for Win32 or wxWidgets only). If the GUI console is available at
runtime, textconfig switches the display to text mode 80 x 25 and restores the
simulation screen afterwards.
@@ -5538,7 +5607,7 @@ It consists of these three parts:
On Win32 (without wxWidgets) the default configuration interface 'win32config' is
-very similar, but it presents gui dialogs instead of text menus.
+very similar, but it presents GUI dialogs instead of text menus.
The start menu
@@ -5555,7 +5624,7 @@ configuration file (typically called bochsrc.txt) and loaded it if it
could be found. When you are satisfied with the configuration, go
ahead and start the simulation.
-You can also start bochs with the -q option to skip these menus.
+You can also start Bochs with the -q option to skip these menus.
1. Restore factory default configuration
2. Read options from...
@@ -5606,7 +5675,7 @@ behavoiur of Bochs at runtime if you click on one of these buttons:
Here you can enable the creation of mouse events by the host. Once mouse
events are captured, you cannot reach the button anymore, in order to disable
- capturing again. By default you can enable and disable the mouse capture pressing
+ capturing again. By default, you can enable and disable the mouse capture pressing
the CTRL key and the third (middle) mouse button. See the mouse option
parameter 'toggle' for other methods to toggle the mouse capture.
@@ -5642,10 +5711,10 @@ feature work.snapshot buttonPress this button if you want to save a snapshot of the Bochs screen. All
-text and graphics modes are now supported. If gui dialogs are supported (e.g. on
+text and graphics modes are now supported. If GUI dialogs are supported (e.g. on
win32) Bochs presents you a "Save as..." dialog box to specify the filename. In
text modes the formats BMP and TXT are supported depending on file extension.
-Platforms without gui dialogs are using the fixed filenames "snapshot.txt" or
+Platforms without GUI dialogs are using the fixed filenames "snapshot.txt" or
"snapshot.bmp".
@@ -5665,7 +5734,7 @@ could be restored back using bochs -r command. For more details read the
power button
-This button stops the simulation and quits bochs.
+This button stops the simulation and quits Bochs.
@@ -5756,7 +5825,7 @@ hardcoded file name and the user button sends the configured shortcut.
If you want to change certain settings at runtime, you have to press the "config" button in
the headerbar. The simulation stops and the runtime menu appears on the console window / xterm
-or the Bochs gui console (if available).
+or the Bochs GUI console (if available).
---------------------
Bochs Runtime Options
@@ -5791,7 +5860,7 @@ more information (e.g. report debug messages).
Pre-defined CPU models
If Bochs is compiled with cpu level 5 or higher the CPUID opcode is supported
-and it can return some information about the cpu model and it's features. When
+and it can return some information about the cpu model and its features. When
using a pre-defined CPU model in Bochs the features reported by CPUID are set up
according to the model's specification. The following table shows all available
CPU models with a short description. The amount of choices depends on the CPU
@@ -5962,8 +6031,8 @@ copy the whole image or the file containing changes (journal). This may take som
so be patient when using this feature.
can be saved. When running Bochs there will be a button in the
-header bar called "Suspend". Depending on config interface and gui there will be a
-prompt where you can enter a path to an existing directory or a gui folder selection
+header bar called "Suspend". Depending on config interface and GUI there will be a
+prompt where you can enter a path to an existing directory or a GUI folder selection
dialog box. It is possible to save the state at any time, but we recommend to do it
when the simulation is idle. After pressing OK/Enter, Bochs will save a set of files
into the selected folder. It is possible to continue after saving the state, but when
@@ -5971,7 +6040,7 @@ using the restore function in a new Bochs session, all changes after this checkp
will be lost.
-To restore the saved simulation state you can select the restore function in the
+To restore the saved simulation state, you can select the restore function in the
text mode start menu or specify the restore path at the command line:
bochs -r /path/to/save-restore-data
@@ -6055,7 +6124,7 @@ one.
At runtime the lowlevel sound module will be loaded automatically if one of the
-sound devices is enabled in the bochsrc. The drivers and
+sound devices are enabled in the bochsrc file. The drivers and
devices for wave input / output and MIDI output must be set up with the
sound option.
@@ -6076,8 +6145,8 @@ parameter of the speaker option. Three c
generator which is a part of the lowlevel sound support.
: only available on Linux and Windows.
On Linux /dev/console is used for output and on Windows the Beep() function.
- : forwards the beep to the related gui methods
-(currently only used by the Carbon gui).
+ : forwards the beep to the related GUI methods
+(currently only used by the Carbon GUI).
@@ -6090,7 +6159,7 @@ the simulation.
Runtime options
-Unlike other devices, the SB16 emulation has it's own logfile and a loglevel parameter
+Unlike other devices, the SB16 emulation has its own logfile and a loglevel parameter
to control what should be printed there. Both the file and
parameters can be changed at runtime. See the
sb16 bochsrc option for details.
@@ -6102,7 +6171,7 @@ runtime.
The parameter controls the DMA timing for wave (PCM)
-input and output. When you get non-continuous sound this value can be ajusted
+input and output. When you get non-continuous sound, this value can be ajusted
to fix this. This needs a reasonably correct setting for the
cpu: ips option.
@@ -6301,7 +6370,7 @@ Project news page (used for release announcements)
SourceForge tickets section (bug and patch trackers)
-There are some requirements when submitting bug reports, patches and feature
+There are some requirements when submitting bug reports, patches, and feature
requests for Bochs to make it easier to reproduce bugs and test patches.
Reporting errors from Bochs compilation
@@ -6513,7 +6582,7 @@ it if possible.
This section describes how to enable and use the Bochs command line debugger.
-For it's builtin graphical front-end please see the debugger gui
+For its builtin graphical front-end please see the debugger gui
section how to enable it.
@@ -6669,7 +6738,7 @@ From here, you may use the following commands:
writemem filename addr len dump a number of bytes of virtual memory starting from
the specified linear address into a file
- loadmem filename addr iniialize virtual memory starting from the specified linear
+ loadmem filename addr initialize virtual memory starting from the specified linear
address from a file
deref addr deep pointer dereference. For example: get value of [[[rax]]] or ***rax: deref rax 3
@@ -6728,7 +6797,7 @@ From here, you may use the following commands:
special ':' operator which computes the linear address of a
segment:offset (in real and v86 mode) or of a selector:offset
(in protected mode) pair. Use $ operator for dereference,
- for example get value of [[[rax]]] or ***rax: rax$3.
+ for example, get value of [[[rax]]] or ***rax: rax$3.
@@ -6785,7 +6854,7 @@ From here, you may use the following commands:
Instrumentation
-To use instrumentation features in bochs, you must compile in support for it.
+To use instrumentation features in Bochs, you must compile in support for it.
You should build a custom instrumentation library in a separate directory in
the "instrument/" directory. To tell configure which instrumentation library
you want to use, use the option.
@@ -6799,7 +6868,7 @@ equivalent:
You could make a separate directory with your custom library,
-for example "instrument/myinstrument", copy the contents of
+for example, "instrument/myinstrument", copy the contents of
the "instrument/stubs" directory to it, then customize it. Use:
@@ -6935,14 +7004,14 @@ Related links
-The Bochs debugger gui
+The Bochs debugger GUI
The graphical front-end for the Bochs command line debugger
is available for Windows and GTK2/GTK3 hosts.
-To use the gui debugger, you must configure Bochs with the
+To use the GUI debugger, you must configure Bochs with the
default debugger switch and the flag.
For example:
@@ -6952,19 +7021,19 @@ For example:
At runtime you need to add the value to the
display_library options parameter
-in order to use the gui instead of the command line debugger. This example shows
-how to use it with the 'x' gui:
+in order to use the GUI instead of the command line debugger. This example shows
+how to use it with the 'x' GUI:
display_library: x, options="gui_debug"
-The wxWidgets port of Bochs always uses this debugger gui. Passing
+The wxWidgets port of Bochs always uses this debugger GUI. Passing
the option to the display library is not necessary, since the command line
interface is not available then.
Overview
-The gui debugger consists of a gui window with a menu bar, a button bar and some
+The GUI debugger consists of a GUI window with a menu bar, a button bar and some
child windows for different purposes. Not all windows are visible at the same time.
@@ -7002,7 +7071,7 @@ Having a big list will reduce the number of autoloads, and allows you to see
more. The list can contain up to 2048 lines. However, if you load more than
1000 lines, you are more likely to see performance problems.
-There are two kinds of emulated memory in bochs: Linear and Physical.
+There are two kinds of emulated memory in Bochs: Linear and Physical.
Emulated Linear memory is mapped onto Physical memory by x86 virtual memory
methods (paging and segmentation). If paging and segmenataion are "off", or
"identity mapped", then both "types" of memory mean the same thing. But they
@@ -7010,7 +7079,7 @@ still work a little differently. With the Internal Debugger, you can set
breakpoints to either kind of memory, separately. Normally, you would use
the "b" command to set breakpoints in physical mem, and "lb" to set breakpoints
in linear mem. This frontend ONLY displays linear breakpoints. It does not
-bother trying to figure out the linear->phsical reverse mapping to show
+bother trying to figure out the linear->physical reverse mapping to show
physical breakpoints. (There are also "virtual" breakpoints that are also
not shown.) All the types of breakpoints still WORK, it is just that you
will not see them marked on the screen.
@@ -7030,7 +7099,7 @@ steps) to set or clear a linear breakpoint.
As of this version, the MemDump window isn't much more than a display of the
contents of memory. In later versions, hopefully it will be expanded into a
-fairly fully-featured hexeditor. You can dump either phyical mem, or linear
+fairly fully-featured hexeditor. You can dump either physical mem, or linear
mem. There are breakpoint-like things (that work with physical memory only,
currently), called "watchpoints". A physical memory address can cause a break
in the simulation if it is read, or written.
@@ -7043,7 +7112,7 @@ in any attempt to display the physical watchpoints while viewing linear mem.
You must click a hex byte (on a physical mem dump that shows bytes), in order to
set or clear a read and/or write watchpoint on that byte. Read watchpoints are
green (on black), write watchpoints are red, watchpoints that are both write
-and read are blue. There is a hardcoded limit in bochs of 16 of each type of
+and read are blue. There is a hardcoded limit in Bochs of 16 of each type of
watchpoint.
@@ -7087,7 +7156,7 @@ Hitting Enter on a blank line will cause a Singlestep.
The param tree
-The bochs param_tree shows the internal state of most of bochs. It will be
+The Bochs param_tree shows the internal state of most of Bochs. It will be
expanded in the future to show even more. You can see the detailed state of
all cpu registers -- including the "hidden" parts (look in the "bochs" branch).
Or see the current state of most of the emulated hardware.
@@ -7144,7 +7213,7 @@ read, especially on a proportional font. If you change to a fixed font, then you
may want to switch the display to lowercase.
-Most of the gui debugger settings are now saved to an INI file on exit and
+Most of the GUI debugger settings are now saved to an INI file on exit and
restored at the next run.
@@ -7311,7 +7380,7 @@ simulation is starting.
MACGUIMGUI
- MacOS9 / MacOSX gui
+ MacOS9 / MacOSX GUINE2K
@@ -7381,7 +7450,7 @@ simulation is starting.
RFBRFB
- RFB gui
+ RFB GUISB16
@@ -7401,12 +7470,12 @@ simulation is starting.
SDLSDL
- SDL 1.2.x gui
+ SDL 1.2.x GUISDL2SDL2
- SDL 2.x gui
+ SDL 2.x GUIserial
@@ -7436,7 +7505,7 @@ simulation is starting.
TERMTERM
- Term gui
+ Term GUIunmappped
@@ -7506,17 +7575,17 @@ simulation is starting.
WINGUIWINGUI
- WIN32 gui
+ WIN32 GUIWXWX
- wxWidgets gui
+ wxWidgets GUIXGUIXGUI
- X11 gui
+ X11 GUI
@@ -7577,8 +7646,8 @@ previous step).
-When you'll update your configuration file, please
-fill in the same cylinders, heads and sector per
+When you update your configuration file, please
+fill in the same cylinders, heads, and sector per
track values.
@@ -7832,57 +7901,32 @@ http://hp.vector.co.jp/authors/VA013937/editdisk/index_e.html
-Ben Lunt's MTOOLs for Bochs and Win32 and/or DOS
+Ben Lunt's 'Ultimate' for Bochs and Win32/64
-Ben Lunt wrote a set of utilities for Dos/Win32 to manipulate flat disk images.
+Ben Lunt wrote a utility for Windows to manipulate flat disk images and their
+included file systems.
-You can find it at
-
-http://www.fysnet.net/mtools.htm
+You can find it at
+https://www.fysnet.net/ultimate/index.htm
-These utilities includes :
+This is a Windows Dialog based utility, with source, and includes the following items:
- BOCHSRC.EXE "Bochs Resource"
- A utility to create/modify a Bochs resource file.
+ Partitioning: MBR, PMBR, eMBR, EFI GPT
- MKDOSFS.EXE "Make DOS FS"
- A utility to create a FAT disk image of specified size.
+ File Systems: FAT, FYSFS, LeanFS, Ext2/3/4, SimpleFS, ExFAT
- MCOPYF.EXE "Copy From"
- A utility to copy an existing file from a FAT disk image to the current
- directory.
+ Inserting, extracting, and deleting files from said file systems.
+ (some file system support is currently only read-only)
- MDEL.EXE "Delete file"
- A utility to delete an existing file from a FAT disk image.
-
-
- MDIREX.EXE "Directory Extended"
- A utility to view a FAT disk images directory and FAT contents.
-
-
- MGETIMG.EXE "Get Disk Image"
- A utility to create a disk image from a floppy (multiple formats).
-
-
- MBOOTCD.EXE "Create a CDROM Image with boot options"
- Create a CDROM image capable of booting with only a ROOT and a single file.
-
-
- MGETCD.EXE "Get Disk Image of Physical CD"
- A utility to create a disk image from a CD.
-
-
- MCDINFO.EXE "Get CD Info"
- A utility to the info from a CD. Not much yet, but a little.
+ Checking the integrity of said file systems and other items.
-
@@ -7951,7 +7995,7 @@ the files is to mount the disk image using the loop device.
This section was contributed by Volker Ruppert.
Today I have made some tests with the loop device, because I want to exchange
-files with the bochs disk images. This is what I found out:
+files with the Bochs disk images. This is what I found out:
1. Using Floppy images is easy, because there is no partition table:
@@ -7963,7 +8007,7 @@ files with the bochs disk images. This is what I found out:
- filesystem check : fsck.minix /dev/loop0
- mount : mount /dev/loop0 -o loop /mnt/floppy
- Before you want to restart bochs you must do this:
+ Before you want to restart Bochs you must do this:
losetup -d /dev/loop0
@@ -7983,7 +8027,7 @@ files with the bochs disk images. This is what I found out:
3. The hard disk image access doesn't work if the image contains more than
one partition.
-4. I have made this tests with Linux and I don't know how
+4. I have made this test with Linux and I don't know how
this could be done with other operating systems.
@@ -8051,7 +8095,7 @@ Starting with Bochs 2.2.6 you can set up the number of processors in the
set up the number of processors.
-It is important to understand that configuring bochs for 4 processors will NOT
+It is important to understand that configuring Bochs for 4 processors will NOT
make your single-threaded applications run faster in general! On the contrary,
it has to spend time simulating idle processors as well as the ones doing your
task. The point is to simulate an SMP system, not to speed up a uniprocessor
@@ -8075,7 +8119,7 @@ processors, their IDs, interrupt sources, etc. Starting with Bochs 2.2.5 these
structures are dynamically created by Bochs.
ACPI support is required to boot SMP system in most of modern
-operating systems. For example WinXP 64 bit require ACPI support even for
+operating systems. For example, WinXP 64 bit require ACPI support even for
single processor configuration.
@@ -8134,7 +8178,7 @@ edit the values if necessary.
# sample for Windows
ne2k: ioaddr=0x300, irq=9, mac=00:c4:3B:00:C3:00, ethmod=win32, ethdev=NE2000
-You see the pattern. Usually you won't need to change the I/O address, IRQ
+You see the pattern. Usually, you won't need to change the I/O address, IRQ
number, or MAC address. The ethmod value depends on your
host operating system, and it must be either null,
fbsd (for FreeBSD or OpenBSD), linux,
@@ -8150,13 +8194,13 @@ suggest an ne2k line which is a very good first try.
Next, if you are on a UNIX machine you will need to become the root user.
-Since bochs is sending and receiving raw network packets, you need to be root
+Since Bochs is sending and receiving raw network packets, you need to be root
to use the network device. To allow normal users to do this would be a
security problem.
-Now run Bochs to boot DLX Linux. Press enter a few times to accept the default
+Now run Bochs to boot DLX Linux. Press 'enter' a few times to accept the default
configuration choices. This tells Bochs read the configuration file and then
begin. DLX Linux should boot in the Bochs window, and you should see
that Linux detects the NE2000 card. Eventually it gets to a login prompt.
@@ -8185,7 +8229,7 @@ network.
-The bochs IP address must be an unused IP address on your
+The Bochs IP address must be an unused IP address on your
network. If you duplicate someone else's IP address, your network will
become very confused.
@@ -8196,7 +8240,7 @@ Finally, the network is ready and you can test it out with ping, telnet, or ftp
to various machines by their numerical IP address. Keep in mind that for all
UNIX host platforms, Bochs networking cannot talk to the host machine. That
means the host machine can't be the gateway either. You need another physical
-machine on the network that bochs can talk to. On Win32 this restriction does
+machine on the network that Bochs can talk to. On Win32 this restriction does
not apply.
@@ -8338,7 +8382,7 @@ Some of the other options in this group is probably also needed,
#!/bin/bash
/sbin/ifconfig ${1##/*/} 192.168.1.1
- The script get the interface name as the first parameter. Linux
+ The script gets the interface name as the first parameter. Linux
will forward incoming packets between interfaces.
@@ -8546,7 +8590,7 @@ extensions:
-The 'socket' networking module uses two UDP ports per Bochs session. By default
+The 'socket' networking module uses two UDP ports per Bochs session. By default,
the first session receives packets from port 40000 and sends packets to port
40001. The second session uses then the ports 40002 and 40003. For further
sessions the port numbers are incremented accordingly. The port number for
@@ -8594,12 +8638,12 @@ Supported options:
The vnet FTP service
The 'vnet' server now provides passive FTP support also using the TFTP directory
-as root. The FTP server name is vnet-ftp. For read only access
-the user name must be set to anonymous with any password.
-This mode supports browing the directory subtree and downloading files. For
+as root. The FTP server name is vnet-ftp. For read only access,
+the username must be set to anonymous with any password.
+This mode supports browsing the directory subtree and downloading files. For
read/write access, the user must be set to bochs with
password bochs. This enables support for uploading,
-renaming and deleting files, creating and removing directories.
+renaming and deleting files, creating, and removing directories.
@@ -8775,7 +8819,7 @@ T0:1234:respawn:/bin/agetty 38400 cua0
com1: enabled=1, mode=term, dev=/dev/ptyp0
- and lauch dlxlinux. After you log in as root, enter the command:
+ and launch dlxlinux. After you log in as root, enter the command:
dlx:~# /sbin/agetty 38400 cua0
@@ -8867,16 +8911,16 @@ Here is a summary of what can happen when booting from the CD.
0x01 no atapi device found 0x02 no atapi cdrom found
- 0x03 can not read cd - BRVD
+ 0x03 cannot read cd - BRVD 0x04 cd is not eltorito (BRVD) 0x05 cd is not eltorito (ISO TAG) 0x06 cd is not eltorito (ELTORITO TAG)
- 0x07 can not read cd - boot catalog
+ 0x07 cannot read cd - boot catalog 0x08 boot catalog : bad header 0x09 boot catalog : bad platform 0x0A boot catalog : bad signature 0x0B boot catalog : bootable flag not set
- 0x0C can not read cd - boot image
+ 0x0C cannot read cd - boot image
@@ -8894,7 +8938,7 @@ been defined in Bochs conf file.
-0x03 can not read cd - BRVD
+0x03 cannot read cd - BRVD
For this error, the cdrom support has not been compiled in Bochs,
@@ -8929,8 +8973,8 @@ for a x86 bootable cd.
-0x07 can not read cd - boot catalog
-0x0C can not read cd - boot image
+0x07 cannot read cd - boot catalog
+0x0C cannot read cd - boot image
here, specific part of the cd could not be read. This should
definitely not happen.
@@ -9038,7 +9082,7 @@ in the "ataX-xxxx: ..., translation='algorithm'" section.
PCHS:16320/16/63
- a LBA-assisted algorithm
+ An LBA-assisted algorithm
is used to translate the CHS between
the int13h interface
and the ATA interface. The translation is achieved by
@@ -9073,7 +9117,7 @@ was used. You must not set the translation to 'auto'.
rechs translation should only be useful for Compaq users who wants to
use a disk as a block device. Please report if you know any other
-system that use such translation.
+system that uses such translation.
@@ -9088,10 +9132,10 @@ Upward compatibility will be maintained.
-This translation applies only to int13h BIOS disk accesses. Older OSes (e.g. MS-DOS)
+This translation applies only to int13h BIOS disk accesses. Older OSes (e.g., MS-DOS)
tend to use them a lot. On modern OSes, disk accesses through BIOS int13h are
limited to boot loaders.
-The usual rules and tricks of the installed OS still apply (ie 1024 cylinders boot limit).
+The usual rules and tricks of the installed OS still apply (i.e., 1024 cylinders boot limit).
@@ -9235,7 +9279,7 @@ Bochs VBE Display Drivers for Windows NT/2000
Bochs supports the emulation of 4 different 3dfx Voodoo Graphics adapter models.
The Voodoo1 and Voodoo2 models are 3D-only add-on cards and require a VGA
- compatible primary device. The Voodoo Banshee and it's successor Voodoo3 have
+ compatible primary device. The Voodoo Banshee and its successor Voodoo3 have
both a VGA core and a 2D/3D core, so no other VGA card is required. The 2D/3D
cards can be assigned to AGP if the i440BX chipset is selected (slot #5).
@@ -9247,7 +9291,7 @@ Bochs VBE Display Drivers for Windows NT/2000
For emulating the Voodoo Banshee or Voodoo3 adapter, the vga extension option
must be set to "voodoo". Using a different primary display adapter and a
- Banshee compatible device with it's VGA interface disabled is not
+ Banshee compatible device with its VGA interface disabled is not
implemented yet. For both Banshee and Voodoo3 there is now a specific version
of the LGPL'd VGABIOS with Voodoo Banshee specific code called
VGABIOS-lgpl-latest-banshee. The binary file is compiled
@@ -9535,7 +9579,7 @@ resize support for "sparse" mode images.
Create a sparse disk image called "c.img.0". Point .bochsrc at "c.img.0".
- In bochs, install your favorite OS. Switch off bochs.
+ In Bochs, install your favorite OS. Switch off Bochs.
@@ -9543,7 +9587,7 @@ resize support for "sparse" mode images.
Create a sparse disk image (of the same size)
and name it "c.img.1". Point .bochsrc at "c.img.1"
"c.img.0" is visible, but all writes go to "c.img.1".
- After using bochs, you can simply delete
+ After using Bochs, you can simply delete
"c.img.1" to undo changes and go back to a clean OS install.
@@ -9558,7 +9602,7 @@ resize support for "sparse" mode images.
Create a sparse disk image called "c.img.0". Point .bochsrc at "c.img.0".
- In bochs, install your favorite OS. Switch off bochs.
+ In Bochs, install your favorite OS. Switch off Bochs.
@@ -9566,14 +9610,14 @@ resize support for "sparse" mode images.
Create a sparse disk image (of the same size) and name it "c.img.1".
Point .bochsrc at "c.img.1"
"c.img.0" is visible, but all writes go to "c.img.1".
- After using bochs, if you want to keep the
+ After using Bochs, if you want to keep the
changes, use the (currently non-existent) merge utility
to make a single unified drive image.
- Alternatively simply create a new partition on top called "c.img.2".
+ Alternatively, simply create a new partition on top called "c.img.2".
@@ -9586,14 +9630,14 @@ resize support for "sparse" mode images.
Create a sparse disk image called "base.img". Point .bochsrc at "base.img".
- In bochs, install your favorite OS. Switch off bochs.
+ In Bochs, install your favorite OS. Switch off Bochs.
Create a sparse disk image (of the same size) and name it "www.img.1".
Make "wwww.img.0" a symlink to
- "base.img". Point .bochsrc at "www.img.1". Using bochs, install a webserver.
+ "base.img". Point .bochsrc at "www.img.1". Using Bochs, install a webserver.
@@ -9601,7 +9645,7 @@ resize support for "sparse" mode images.
Create a symlink to "base.img" called "db.img.0".
Create a sparse disk image (of the same size)
and name it "db.img.1". Point .bochsrc at "db.img.1".
- Using bochs, install a database server.
+ Using Bochs, install a database server.
@@ -9770,7 +9814,7 @@ Only vmware versions 3 and 4 disk image files are supported.
description
- Growing disk images start as a small files, and
+ Growing disk images start as small files, and
grow whenever new data is written to them.
@@ -9826,7 +9870,7 @@ Be sure to enter "growing" when selecting the image type.
description
Volatile disks are always-rollbacked disk images.
- An volatile disk is based on a read-only image, associated with
+ A volatile disk is based on a read-only image, associated with
a growing redolog, that contains all changes (writes)
made to the base image content. Currently, base images of
types 'flat', 'sparse', 'growing', 'vmware3', 'vmware4', 'vpc' and
@@ -9995,7 +10039,7 @@ Be sure to enter "growing" when selecting the image type.
when Bochs is starting. All writes to this virtual disk go to a volatile
redolog and when closing Bochs, the user can decide whether or not to commit
the changes. If "No" is seclected, all changes will be lost when Bochs quits
- and the files of the local directory are not modified. Otherwise the changes
+ and the files of the local directory are not modified. Otherwise, the changes
of files and directories are committed. WARNING: Don't use important data without
backup in the "vvfat" directory when using this "optional commit" feature.
@@ -10104,7 +10148,7 @@ Bximage is an easy to use console based tool for creating, converting and
resizing disk images, particularly for use with Bochs. It also supports
committing redolog files to their base images. It is completely interactive
if no command line arguments are used. It can be switched to a non-interactive
-mode if all required parameters are given in the command line.
+mode if all required parameters are given in the command line.
When you run bximage without one of the following options, it will appear in
@@ -10208,7 +10252,7 @@ to another. The type of the source image is auto-detected. For the target
format it supports the same disk image modes as the create function. If the
name of the new image file is not specified or identical to the source one
and you have enabled the backup switch, a backup of the source file will be
-created with it's original name plus the suffix ".orig".
+created with its original name plus the suffix ".orig".
Resize image
@@ -10218,7 +10262,7 @@ It supports the same disk image modes as the create function.
Making a disk image smaller is not supported, since it may damage the
disk and data will be lost. If the name of the new image file is not
specified or identical to the source one and you have enabled the backup
-switch, a backup of the source file will be created with it's original name
+switch, a backup of the source file will be created with its original name
plus the suffix ".orig".
@@ -10249,14 +10293,14 @@ and 63 sectors per track.
Guest operating systems
In the past several tweaks were necessary to install a guest OS inside of Bochs.
-Nowadays it's almost the same as installing it on a real machine. There are only
+Nowadays, it is almost the same as installing it on a real machine. There are only
a few Bochs specific issues you should know about. Note that we cannot give you
a full installation guide if you don't know how to install an OS in the real
world. The following remarks apply to all guest OS installations. Some specific
issues are reported in the next sections.
-First of all you need the installation media or image (floppy/CD/DVD).
+First of all, you need the installation media or image (floppy/CD/DVD).
For platforms that don't support raw device access it might be necessary to
create an image from the media. You must read the message regarding software
licenses in before you install or use a commercial
@@ -10385,7 +10429,7 @@ Known problems
This is just a very short step-by-step installation guide for FreeBSD in Bochs. It
doesn't explain what you do nor why you do it, it just tells you how to do it.
- For in-deepth information refer to the FreeBSD handbook:
+ For in-depth information refer to the FreeBSD handbook:
Installing FreeBSD.
@@ -10441,7 +10485,7 @@ Known problems
All files are installed on your (virtual) hard disk now, and FreeBSD is ready for getting set
up. As this is a very basic FreeBSD installation, you just answer 'no' to nearly all questions,
but the one about your mouse: Answer 'yes' for PS/2 mouse, and choose 'Exit' at mouse configuration.
- The miniinst FreeBSD ISO image contains nearly no binary packages, so don't browse the package
+ The minimalist FreeBSD ISO image contains nearly no binary packages, so don't browse the package
collection. Then, when asked to create a new user account, answer 'yes' and create a new user
called 'bochs' (or whatever you like). You might want to use /bin/csh
or /bin/tcsh as shell rather than /bin/sh. Next,
@@ -10534,7 +10578,7 @@ string SDR-31STD-1-US (Revision 1).
Bootdisks of early DOS versions
-On the Web there are bootdisks available for most of the DOS versions ever released,
+On the Web, there are bootdisks available for most of the DOS versions ever released,
but some of them have been reported to fail in Bochs. The bootdisk for MS-DOS 1.25
contains a boot sector of a newer DOS version, so it would fail on real hardware,
too. The floppy image for MS-DOS 2.11 has a boot sector that tries to boot from
@@ -10565,7 +10609,7 @@ first reboot.
to log in you must press ctrl-alt-del, and it is likely that the window manager
will trap this key combination. You can either use the trick described in
or define a user short-cut
-(callable through the user short-cut gui button)
+(callable through the user short-cut GUI button)
in you configuration file, for example:
keyboard: user_shortcut=ctrl-alt-del
@@ -10792,7 +10836,7 @@ drivers have been linked out of the kernel.
-Before you configure the network card, I'd strongly suggest getting the latest "nat" driver from SCO.
+Before you configure the network card, I'd strongly suggest getting the latest "nat" driver from SCO.
Version 5.0.5b of this driver, according to the SCO web site, "correct[s] possible system lockup
under high load due to internal buffer overflow." The driver can be found
here.
@@ -10821,7 +10865,7 @@ that the printing is going through an extra layer of operating system!
-Obviously, dont forget to apply the release supplements and other patches that are considered
+Obviously, don't forget to apply the release supplements and other patches that are considered
"must haves" for OSR5: rs505a, oss600a, oss497b (others?).
diff --git a/bochs/iodev/usb/Makefile.in b/bochs/iodev/usb/Makefile.in
index 1891cd1ef..2a421ff61 100644
--- a/bochs/iodev/usb/Makefile.in
+++ b/bochs/iodev/usb/Makefile.in
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2021 The Bochs Project
+# Copyright (C) 2012-2023 The Bochs Project
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -50,7 +50,7 @@ top_builddir = ../..
LIBTOOL=@LIBTOOL@
WIN32_DLL_IMPORT_LIBRARY=../../@WIN32_DLL_IMPORT_LIB@
-USBDEV_OBJS = usb_hid.o usb_hub.o usb_msd.o usb_printer.o usb_floppy.o
+USBDEV_OBJS = usb_hid.o usb_hub.o usb_msd.o usb_uasp.o usb_printer.o usb_floppy.o
UHCICORE_OBJ = uhci_core.o
SCSI_OBJS = scsi_device.o
@@ -72,7 +72,7 @@ OBJS_THAT_SUPPORT_OTHER_PLUGINS = \
NONPLUGIN_OBJS = @IODEV_EXT_NON_PLUGIN_OBJS@
PLUGIN_OBJS = @IODEV_EXT_PLUGIN_OBJS@
USBHC_DLL_TARGETS = @USBHC_DLL_TARGETS@
-USBDEV_DLL_TARGETS = bx_usb_hid.dll bx_usb_hub.dll bx_usb_msd.dll bx_usb_printer.dll bx_usb_floppy.dll
+USBDEV_DLL_TARGETS = bx_usb_hid.dll bx_usb_hub.dll bx_usb_msd.dll bx_usb_uasp.dll bx_usb_printer.dll bx_usb_floppy.dll
all: libusb.a
@@ -99,8 +99,8 @@ libbx_%.la: %.lo
$(LIBTOOL) --mode=link --tag CXX $(CXX) $(LDFLAGS) -module $< -o $@ -rpath $(PLUGIN_PATH)
# special link rules for plugins that require more than one object file
-libbx_usb_msd.la: usb_msd.lo scsi_device.lo
- $(LIBTOOL) --mode=link --tag CXX $(CXX) $(LDFLAGS) -module usb_msd.lo scsi_device.lo -o libbx_usb_msd.la -rpath $(PLUGIN_PATH)
+libbx_usb_msd.la: usb_msd.lo scsi_device.lo usb_uasp.lo
+ $(LIBTOOL) --mode=link --tag CXX $(CXX) $(LDFLAGS) -module usb_msd.lo scsi_device.lo usb_uasp.lo -o libbx_usb_msd.la -rpath $(PLUGIN_PATH)
libbx_usb_uhci.la: usb_uhci.lo uhci_core.lo
$(LIBTOOL) --mode=link --tag CXX $(CXX) $(LDFLAGS) -module usb_uhci.lo uhci_core.lo -o libbx_usb_uhci.la -rpath $(PLUGIN_PATH)
@@ -134,8 +134,8 @@ bx_usb_hid.dll: usb_hid.o
bx_usb_hub.dll: usb_hub.o
@LINK_DLL@ usb_hub.o $(WIN32_DLL_IMPORT_LIBRARY)
-bx_usb_msd.dll: usb_msd.o scsi_device.o
- @LINK_DLL@ usb_msd.o scsi_device.o $(WIN32_DLL_IMPORT_LIBRARY)
+bx_usb_msd.dll: usb_msd.o usb_uasp.o scsi_device.o
+ @LINK_DLL@ usb_msd.o usb_uasp.o scsi_device.o $(WIN32_DLL_IMPORT_LIBRARY)
bx_usb_printer.dll: usb_printer.o
@LINK_DLL@ usb_printer.o $(WIN32_DLL_IMPORT_LIBRARY)
@@ -216,6 +216,14 @@ usb_msd.o: usb_msd.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \
../../memory/memory-bochs.h ../../gui/siminterface.h ../../gui/gui.h \
usb_common.h usb_pcap.h ../hdimage/cdrom.h ../hdimage/hdimage.h \
scsi_device.h usb_msd.h
+usb_uasp.o: usb_uasp.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \
+ ../../osdep.h ../../gui/paramtree.h ../../logio.h \
+ ../../instrument/stubs/instrument.h ../../misc/bswap.h ../../plugin.h \
+ ../../extplugin.h ../../param_names.h ../../pc_system.h \
+ ../../bx_debug/debug.h ../../config.h ../../osdep.h \
+ ../../memory/memory-bochs.h ../../gui/siminterface.h ../../gui/gui.h \
+ usb_common.h usb_pcap.h ../hdimage/cdrom.h ../hdimage/hdimage.h \
+ scsi_device.h usb_msd.h
usb_ohci.o: usb_ohci.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \
../../osdep.h ../../gui/paramtree.h ../../logio.h \
../../instrument/stubs/instrument.h ../../misc/bswap.h ../../plugin.h \
@@ -309,6 +317,14 @@ usb_msd.lo: usb_msd.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \
../../memory/memory-bochs.h ../../gui/siminterface.h ../../gui/gui.h \
usb_common.h usb_pcap.h ../hdimage/cdrom.h ../hdimage/hdimage.h \
scsi_device.h usb_msd.h
+usb_uasp.lo: usb_uasp.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \
+ ../../osdep.h ../../gui/paramtree.h ../../logio.h \
+ ../../instrument/stubs/instrument.h ../../misc/bswap.h ../../plugin.h \
+ ../../extplugin.h ../../param_names.h ../../pc_system.h \
+ ../../bx_debug/debug.h ../../config.h ../../osdep.h \
+ ../../memory/memory-bochs.h ../../gui/siminterface.h ../../gui/gui.h \
+ usb_common.h usb_pcap.h ../hdimage/cdrom.h ../hdimage/hdimage.h \
+ scsi_device.h usb_msd.h
usb_ohci.lo: usb_ohci.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \
../../osdep.h ../../gui/paramtree.h ../../logio.h \
../../instrument/stubs/instrument.h ../../misc/bswap.h ../../plugin.h \
diff --git a/bochs/iodev/usb/scsi_device.cc b/bochs/iodev/usb/scsi_device.cc
index 90134d30b..bb59b79a6 100644
--- a/bochs/iodev/usb/scsi_device.cc
+++ b/bochs/iodev/usb/scsi_device.cc
@@ -546,7 +546,7 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
goto fail;
}
switch (command) {
- case 0x0:
+ case 0x00:
BX_DEBUG(("Test Unit Ready"));
if (!inserted)
goto notready;
@@ -974,11 +974,11 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
outbuf[0] = (Bit8u)((nb_sectors >> 24) & 0xff);
outbuf[1] = (Bit8u)((nb_sectors >> 16) & 0xff);
outbuf[2] = (Bit8u)((nb_sectors >> 8) & 0xff);
- outbuf[3] = (Bit8u)(nb_sectors & 0xff);
- outbuf[4] = 0;
- outbuf[5] = 0;
- outbuf[6] = (block_size >> 8);
- outbuf[7] = 0;
+ outbuf[3] = (Bit8u) (nb_sectors & 0xff);
+ outbuf[4] = (Bit8u) ((block_size >> 24) & 0xff);
+ outbuf[5] = (Bit8u) ((block_size >> 16) & 0xff);
+ outbuf[6] = (Bit8u) ((block_size >> 8) & 0xff);
+ outbuf[7] = (Bit8u) (block_size & 0xff);
r->buf_len = 8;
} else {
notready:
@@ -1016,6 +1016,7 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
r->async_mode = async;
break;
case 0x35:
+ case 0x91:
BX_DEBUG(("Synchronise cache (sector " FMT_LL "d, count %d)", lba, len));
// TODO: flush cache
break;
@@ -1112,6 +1113,52 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
}
break;
+ // The 0x9E command uses a service action code (ex: 0x9E/0x10)
+ case 0x9E:
+ switch (buf[1] & 0x1F) { // service action code
+ case 0x10: // Read Capacity(16)
+ BX_DEBUG(("Read Capacity 16"));
+ memset(outbuf, 0, 32);
+ if (type == SCSIDEV_TYPE_CDROM) {
+ nb_sectors = max_lba;
+ } else {
+ nb_sectors = hdimage->hd_size / block_size;
+ nb_sectors--;
+ }
+ /* Returned value is the address of the last sector. */
+ if (nb_sectors) {
+ // 64-bit lba
+ outbuf[ 0] = (Bit8u)((nb_sectors >> 56) & 0xff);
+ outbuf[ 1] = (Bit8u)((nb_sectors >> 48) & 0xff);
+ outbuf[ 2] = (Bit8u)((nb_sectors >> 40) & 0xff);
+ outbuf[ 3] = (Bit8u)((nb_sectors >> 32) & 0xff);
+ outbuf[ 4] = (Bit8u)((nb_sectors >> 24) & 0xff);
+ outbuf[ 5] = (Bit8u)((nb_sectors >> 16) & 0xff);
+ outbuf[ 6] = (Bit8u)((nb_sectors >> 8) & 0xff);
+ outbuf[ 7] = (Bit8u) (nb_sectors & 0xff);
+ // 32-bit block size
+ outbuf[ 8] = (Bit8u) ((block_size >> 24) & 0xff);
+ outbuf[ 9] = (Bit8u) ((block_size >> 16) & 0xff);
+ outbuf[10] = (Bit8u) ((block_size >> 8) & 0xff);
+ outbuf[11] = (Bit8u) (block_size & 0xff);
+ // protection
+ outbuf[12] = 0;
+ // exponent/one or more physical blocks per logical block
+ outbuf[13] = 0;
+ // lowest aligned logical block address
+ outbuf[14] = 0;
+ outbuf[15] = 0;
+ // bytes 16 through 31 are reserved (zero'd above)
+ r->buf_len = (len < 32) ? len : 32;
+ } else {
+ scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
+ return 0;
+ }
+ break;
+ default:
+ BX_ERROR(("Unknown SCSI command (0x9E/%02X)", buf[1] & 0x1F));
+ }
+ break;
default:
BX_ERROR(("Unknown SCSI command (0x%02X)", buf[0]));
fail:
diff --git a/bochs/iodev/usb/usb_common.cc b/bochs/iodev/usb/usb_common.cc
index ce190136d..39d3817f6 100644
--- a/bochs/iodev/usb/usb_common.cc
+++ b/bochs/iodev/usb/usb_common.cc
@@ -448,7 +448,7 @@ int usb_device_c::handle_packet(USBPacket *p)
#if HANDLE_TOGGLE_CONTROL
// manage our toggle bit
if ((p->toggle > -1) && (p->toggle != get_toggle(p->devep))) {
- BX_ERROR(("DATA IN: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->toggle, get_toggle(p->devep)));
+ BX_ERROR(("DATA IN EP%d: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->devep, p->toggle, get_toggle(p->devep)));
goto fail;
}
set_toggle(p->devep, get_toggle(p->devep) ^ 1); // toggle the bit
@@ -525,7 +525,7 @@ int usb_device_c::handle_packet(USBPacket *p)
#if HANDLE_TOGGLE_CONTROL
// manage our toggle bit
if ((p->toggle > -1) && (p->toggle != get_toggle(p->devep))) {
- BX_ERROR(("DATA OUT: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->toggle, get_toggle(p->devep)));
+ BX_ERROR(("DATA OUT EP%d: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->devep, p->toggle, get_toggle(p->devep)));
goto fail;
}
set_toggle(p->devep, get_toggle(p->devep) ^ 1); // toggle the bit
@@ -687,22 +687,33 @@ int usb_device_c::handle_control_common(int request, int value, int index, int l
}
break;
case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ // Ben TODO: If the device is not in the configured state, this request should stall
BX_DEBUG(("USB_REQ_GET_INTERFACE:"));
// with InterfaceRequest, the wValue field must be zero and wLength field must be 1
if ((value != 0) || (length != 1)) {
BX_ERROR(("USB_REQ_GET_INTERFACE: This type of request requires the wValue field to be zero and wLength field to be one."));
}
- data[0] = d.iface;
- ret = 1;
+ // all our devices only have one interface, and that value must be zero
+ // if we ever add a device that has more than one interface (a video cam ?), we will need to modify this
+ if (index == 0) {
+ data[0] = d.alt_iface;
+ ret = 1;
+ }
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ // Ben TODO: If the device is not in the configured state, this request should stall
BX_DEBUG(("USB_REQ_SET_INTERFACE: value=%d", value));
// with InterfaceRequest, the wIndex and wLength fields must be zero
if ((index != 0) || (length != 0)) {
BX_ERROR(("USB_REQ_SET_INTERFACE: This type of request requires the wIndex and wLength fields to be zero."));
}
- d.iface = value;
- ret = 0;
+ // all our devices only have one interface, and that value must be zero
+ // if we ever add a device that has more than one interface (a video cam ?), we will need to modify this
+ if ((index == 0) && (value <= d.alt_iface_max)) {
+ d.alt_iface = value; // alternate interface
+ handle_iface_change(value); // let the device know we changed the interface number
+ ret = 0;
+ }
break;
// should not have a default: here, so allowing the device's handle_control() to try to execute the request
}
@@ -717,7 +728,7 @@ void usb_device_c::register_state(bx_list_c *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.iface);
+ BXRS_DEC_PARAM_FIELD(list, interface, d.alt_iface);
BXRS_DEC_PARAM_FIELD(list, state, d.state);
BXRS_DEC_PARAM_FIELD(list, remote_wakeup, d.remote_wakeup);
register_state_specific(parent);
@@ -755,13 +766,13 @@ void usb_device_c::usb_dump_packet(Bit8u *data, int size, int bus, int dev_addr,
}
// safety catch
- if (size > 4096) {
- BX_DEBUG(("packet hexdump with irregular size: %u (truncating to 64 bytes)", size));
+ if (size > 8192) {
+ BX_DEBUG(("packet hexdump with irregular size: %u (truncating to 8192 bytes)", size));
}
- // safety catch (only dump up to 512 bytes per packet so to not fill the log file)
- if (size > 512)
- size = 512;
+ // safety catch (only dump up to 8192 bytes per packet so to not fill the log file)
+ if (size > 8192)
+ size = 8192;
if (getonoff(LOGLEV_DEBUG) == ACT_REPORT) {
BX_DEBUG(("packet hexdump (%d bytes)", size));
diff --git a/bochs/iodev/usb/usb_common.h b/bochs/iodev/usb/usb_common.h
index 3d0c45da4..677378d14 100644
--- a/bochs/iodev/usb/usb_common.h
+++ b/bochs/iodev/usb/usb_common.h
@@ -115,6 +115,7 @@
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
#define USB_REQ_SET_SEL 0x30
+#define USB_REQ_SET_ISO_DELAY 0x31
#define USB_DEVICE_SELF_POWERED 0
#define USB_DEVICE_REMOTE_WAKEUP 1
@@ -162,6 +163,7 @@ struct USBPacket {
USBCallback *complete_cb;
void *complete_dev;
usb_device_c *dev;
+ int strm_pid; // stream primary id
};
typedef struct USBAsync {
@@ -211,6 +213,7 @@ public:
virtual void handle_reset() {}
virtual int handle_control(int request, int value, int index, int length, Bit8u *data) { return -1; }
virtual int handle_data(USBPacket *p) { return 0; }
+ virtual void handle_iface_change(int iface) {}
void register_state(bx_list_c *parent);
virtual void register_state_specific(bx_list_c *parent) {}
virtual void after_restore_state() {}
@@ -231,7 +234,7 @@ public:
}
// return information for the specified ep of the current device
-#define USB_MAX_ENDPOINTS 4 // we currently don't use more than 4 endpoints (ep0, ep1, ep2, and ep3)
+#define USB_MAX_ENDPOINTS 5 // we currently don't use more than 5 endpoints (ep0, ep1, ep2, ep3, and ep4)
int get_mps(const int ep) {
return (ep < USB_MAX_ENDPOINTS) ? d.endpoint_info[ep].max_packet_size : 0;
}
@@ -252,6 +255,9 @@ public:
Bit8u get_type() {
return d.type;
}
+ Bit8u get_aIface() {
+ return d.alt_iface;
+ }
Bit8u get_address() {return d.addr;}
void set_async_mode(bool async) { d.async_mode = async; }
@@ -275,7 +281,8 @@ protected:
int speed;
Bit8u addr;
Bit8u config;
- Bit8u iface;
+ Bit8u alt_iface;
+ Bit8u alt_iface_max;
char devname[32];
USBEndPoint endpoint_info[USB_MAX_ENDPOINTS];
diff --git a/bochs/iodev/usb/usb_ehci.cc b/bochs/iodev/usb/usb_ehci.cc
index ccd9cf156..5a89bf790 100644
--- a/bochs/iodev/usb/usb_ehci.cc
+++ b/bochs/iodev/usb/usb_ehci.cc
@@ -1432,12 +1432,13 @@ int bx_usb_ehci_c::execute(EHCIPacket *p)
#endif
p->packet.complete_cb = ehci_event_handler;
p->packet.complete_dev = BX_EHCI_THIS_PTR;
+ p->packet.strm_pid = 0; // if UASP is used, this must be 0
p->async = EHCI_ASYNC_INITIALIZED;
}
ret = p->queue->dev->handle_packet(&p->packet);
- BX_DEBUG(("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+ BX_DEBUG(("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d",
p->queue->qhaddr, p->queue->qh.next, p->queue->qtdaddr, p->pid,
p->packet.len, p->tbytes, endp, ret));
@@ -2079,7 +2080,10 @@ void bx_usb_ehci_c::advance_async_state(void)
default:
/* this should only be due to a developer mistake */
- BX_PANIC(("Bad asynchronous state %d. Resetting to active", BX_EHCI_THIS hub.astate));
+ // Ben: I commented this line due to the fact that after a USB_RET_ASYNC return,
+ // then a usb_packet_complete(p), the event handler is setting the astate to
+ // EST_EXECUTE instead of EST_ACTIVE ???
+ //BX_PANIC(("Bad asynchronous state %d. Resetting to active", BX_EHCI_THIS hub.astate));
BX_EHCI_THIS set_state(async, EST_ACTIVE);
}
}
diff --git a/bochs/iodev/usb/usb_floppy.cc b/bochs/iodev/usb/usb_floppy.cc
index eebdf073f..c02553e4e 100644
--- a/bochs/iodev/usb/usb_floppy.cc
+++ b/bochs/iodev/usb/usb_floppy.cc
@@ -470,6 +470,7 @@ bool usb_floppy_device_c::init()
sprintf(s.info_txt, "USB floppy: media not present");
}
d.connected = 1;
+ d.alt_iface_max = 0;
s.did_inquiry_fail = 0;
s.fail_count = 0;
diff --git a/bochs/iodev/usb/usb_hid.cc b/bochs/iodev/usb/usb_hid.cc
index d58448344..4141352b1 100644
--- a/bochs/iodev/usb/usb_hid.cc
+++ b/bochs/iodev/usb/usb_hid.cc
@@ -792,7 +792,7 @@ bool usb_hid_device_c::init()
* in the configuration, simply uncomment this line. I use
* it when I am working on this emulation.
*/
- LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
+ //LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
if ((d.type == USB_HID_TYPE_MOUSE) ||
(d.type == USB_HID_TYPE_TABLET)) {
@@ -850,6 +850,7 @@ bool usb_hid_device_c::init()
}
}
d.connected = 1;
+ d.alt_iface_max = 0;
return 1;
}
diff --git a/bochs/iodev/usb/usb_hub.cc b/bochs/iodev/usb/usb_hub.cc
index ee958c8c7..2e75489f9 100644
--- a/bochs/iodev/usb/usb_hub.cc
+++ b/bochs/iodev/usb/usb_hub.cc
@@ -283,6 +283,7 @@ bool usb_hub_device_c::init()
}
sprintf(hub.info_txt, "%d-port USB hub", hub.n_ports);
d.connected = 1;
+ d.alt_iface_max = 0;
return 1;
}
@@ -740,8 +741,10 @@ Bit64s usb_hub_device_c::hub_param_handler(bx_param_c *param, bool set, Bit64s v
} else {
BX_PANIC(("usb_param_handler called with unexpected parameter '%s'", param->get_name()));
}
- } else {
- BX_PANIC(("hub_param_handler: external hub not found"));
+ // hub == NULL. We shouldn't call BX_PANIC with a NULL pointer.
+ // #define BX_PANIC(x) (LOG_THIS /* = hub-> */ panic) x
+ //} else {
+ // BX_PANIC(("hub_param_handler: external hub not found"));
}
}
return val;
diff --git a/bochs/iodev/usb/usb_msd.cc b/bochs/iodev/usb/usb_msd.cc
index d2e169d98..282919bf1 100644
--- a/bochs/iodev/usb/usb_msd.cc
+++ b/bochs/iodev/usb/usb_msd.cc
@@ -140,7 +140,7 @@ static const Bit8u bx_msd_config_descriptor[] = {
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
- 0x00, /* u8 if_bAlternateSetting; */
+ MSD_PROTO_BBB, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
@@ -150,7 +150,7 @@ static const Bit8u bx_msd_config_descriptor[] = {
/* Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x80 | MSD_BBB_DATAIN_EP, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; 64 */
0x00, /* u8 ep_bInterval; */
@@ -158,7 +158,7 @@ static const Bit8u bx_msd_config_descriptor[] = {
/* Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x00 | MSD_BBB_DATAOUT_EP, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; 64 */
0x00 /* u8 ep_bInterval; */
@@ -179,7 +179,7 @@ static const Bit8u bx_msd_dev_descriptor2[] = {
0x00, 0x00, /* u16 idVendor; */
0x00, 0x00, /* u16 idProduct; */
0x00, 0x01, /* u16 bcdDevice */
-
+
0x01, /* u8 iManufacturer; */
0x02, /* u8 iProduct; */
0x03, /* u8 iSerialNumber; */
@@ -187,7 +187,7 @@ static const Bit8u bx_msd_dev_descriptor2[] = {
};
// High-speed
-static const Bit8u bx_msd_config_descriptor2[] = {
+static Bit8u bx_msd_config_descriptor2[] = {
/* one configuration */
0x09, /* u8 bLength; */
@@ -196,18 +196,18 @@ static const Bit8u bx_msd_config_descriptor2[] = {
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
- 0xc0, /* u8 bmAttributes;
+ 0x80, /* u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* u8 MaxPower; */
-
+
/* one interface */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
- 0x00, /* u8 if_bAlternateSetting; */
+ MSD_PROTO_BBB, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
@@ -217,7 +217,7 @@ static const Bit8u bx_msd_config_descriptor2[] = {
/* Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x80 | MSD_BBB_DATAIN_EP, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00, /* u8 ep_bInterval; */
@@ -225,10 +225,83 @@ static const Bit8u bx_msd_config_descriptor2[] = {
/* Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x00 | MSD_BBB_DATAOUT_EP, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
- 0x00 /* u8 ep_bInterval; */
+ 0x00, /* u8 ep_bInterval; */
+
+ /***** If UASP is requested, the rest of this descriptor
+ * will be returned, else, the descriptor stops here *****/
+ /* alt interface 1 */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ MSD_PROTO_UASP, /* u8 if_bAlternateSetting; */
+ 0x04, /* u8 if_bNumEndpoints; */
+ 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
+ 0x06, /* u8 if_bInterfaceSubClass; SCSI */
+ 0x62, /* u8 if_bInterfaceProtocol; UASP */
+ 0x00, /* u8 if_iInterface; */
+
+ /***** Command Out Pipe *****/
+ /* Alt Int 1: Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x00 | MSD_UASP_COMMAND, /* u8 ep_bEndpointAddress; Out Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-In Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_COMMAND, /* u8 bPipeUsage; Command Out pipe */
+ 0x00, /* u8 breserved; */
+
+ /***** Status In Pipe *****/
+ /* Alt Int 1: Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x80 | MSD_UASP_STATUS, /* u8 ep_bEndpointAddress; IN Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-In Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_STATUS, /* u8 bPipeUsage; Status In pipe */
+ 0x00, /* u8 breserved; */
+
+ /***** Data In Pipe *****/
+ /* Alt Int 1: Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x80 | MSD_UASP_DATAIN, /* u8 ep_bEndpointAddress; IN Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-In Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_DATAIN, /* u8 bPipeUsage; Data In pipe */
+ 0x00, /* u8 breserved; */
+
+ /***** Data Out Pipe *****/
+ /* Alt Int 1: Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x00 | MSD_UASP_DATAOUT, /* u8 ep_bEndpointAddress; OUT Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-Out Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_DATAOUT, /* u8 bPipeUsage; Data Out pipe */
+ 0x00, /* u8 breserved; */
};
// Super-speed
@@ -253,11 +326,14 @@ static const Bit8u bx_msd_dev_descriptor3[] = {
0x01 /* u8 bNumConfigurations; */
};
-static const Bit8u bx_msd_config_descriptor3[] = {
+static Bit8u bx_msd_config_descriptor3[] = {
/* one configuration */
0x09, /* u8 bLength; */
0x02, /* u8 bDescriptorType; Configuration */
- 0x2C, 0x00, /* u16 wTotalLength; */
+
+ /* this length will be modified depending if UASP is requested (0x002C or 0x0079) */
+ 0x2C, 0x00, /* u16 wTotalLength; */
+
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
@@ -266,48 +342,149 @@ static const Bit8u bx_msd_config_descriptor3[] = {
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
- 0x3F, /* u8 MaxPower; */
+ 0x12, /* u8 MaxPower; */
- /* one interface */
+ /* alt interface 0 */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
- 0x00, /* u8 if_bAlternateSetting; */
+ MSD_PROTO_BBB, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
0x00, /* u8 if_iInterface; */
- /* Bulk-In endpoint */
+ /* Alt Int 0: Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x80 | MSD_BBB_DATAIN_EP, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
- /* Bulk-In companion descriptor */
+ /* Alt Int 0: Bulk-In companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
0x00, /* u8 epc_bmAttributes; */
0x00, 0x00, /* u16 epc_reserved; */
- /* Bulk-Out endpoint */
+ /* Alt Int 0: Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
- 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x00 | MSD_BBB_DATAOUT_EP, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
- /* Bulk-Out companion descriptor */
+ /* Alt Int 0: Bulk-Out companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
0x00, /* u8 epc_bmAttributes; */
- 0x00, 0x00 /* u16 epc_reserved; */
+ 0x00, 0x00, /* u16 epc_reserved; */
+
+ /***** If UASP is requested, the rest of this descriptor
+ * will be returned, else, the descriptor stops here *****/
+ /* alt interface 1 */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ MSD_PROTO_UASP, /* u8 if_bAlternateSetting; */
+ 0x04, /* u8 if_bNumEndpoints; */
+ 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
+ 0x06, /* u8 if_bInterfaceSubClass; SCSI */
+ 0x62, /* u8 if_bInterfaceProtocol; UASP */
+ 0x00, /* u8 if_iInterface; */
+
+ /***** Command Out Pipe *****/
+ /* Alt Int 1: Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x00 | MSD_UASP_COMMAND, /* u8 ep_bEndpointAddress; Out Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-Out companion descriptor */
+ 0x06, /* u8 epc_bLength; */
+ 0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
+ 0x00, /* u8 epc_bMaxPerBurst; */
+ 0x00, /* u8 epc_bmAttributes; no streams */
+ 0x00, 0x00, /* u16 epc_reserved; */
+
+ /* Alt Int 1: Bulk-In Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_COMMAND, /* u8 bPipeUsage; Command Out pipe */
+ 0x00, /* u8 breserved; */
+
+ /***** Status In Pipe *****/
+ /* Alt Int 1: Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x80 | MSD_UASP_STATUS, /* u8 ep_bEndpointAddress; IN Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-In companion descriptor */
+ 0x06, /* u8 epc_bLength; */
+ 0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
+ 0x0F, /* u8 epc_bMaxPerBurst; */
+ UASP_MAX_STREAMS, /* u8 epc_bmAttributes; 2^x max streams */
+ 0x00, 0x00, /* u16 epc_reserved; */
+
+ /* Alt Int 1: Bulk-In Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_STATUS, /* u8 bPipeUsage; Status In pipe */
+ 0x00, /* u8 breserved; */
+
+ /***** Data In Pipe *****/
+ /* Alt Int 1: Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x80 | MSD_UASP_DATAIN, /* u8 ep_bEndpointAddress; IN Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-In companion descriptor */
+ 0x06, /* u8 epc_bLength; */
+ 0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
+ 0x0F, /* u8 epc_bMaxPerBurst; */
+ UASP_MAX_STREAMS, /* u8 epc_bmAttributes; 2^x max streams */
+ 0x00, 0x00, /* u16 epc_reserved; */
+
+ /* Alt Int 1: Bulk-In Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_DATAIN, /* u8 bPipeUsage; Data In pipe */
+ 0x00, /* u8 breserved; */
+
+ /***** Data Out Pipe *****/
+ /* Alt Int 1: Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x00 | MSD_UASP_DATAOUT, /* u8 ep_bEndpointAddress; OUT Endpoint */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Alt Int 1: Bulk-Out companion descriptor */
+ 0x06, /* u8 epc_bLength; */
+ 0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
+ 0x0F, /* u8 epc_bMaxPerBurst; */
+ UASP_MAX_STREAMS, /* u8 epc_bmAttributes; 2^x max streams */
+ 0x00, 0x00, /* u16 epc_reserved; */
+
+ /* Alt Int 1: Bulk-Out Pipe usage */
+ 0x04, /* u8 bLength; */
+ 0x24, /* u8 bDescriptorType; Function */
+ MSD_UASP_DATAOUT, /* u8 bPipeUsage; Data Out pipe */
+ 0x00, /* u8 breserved; */
};
// BOS Descriptor
@@ -479,18 +656,36 @@ bool usb_msd_device_c::set_option(const char *option)
} else {
BX_ERROR(("Option 'sect_size' is only valid for USB disks"));
}
+ } else if (!strncmp(option, "proto:", 6)) {
+ if (!strcmp(option+6, "uasp")) {
+ s.proto = MSD_PROTO_UASP;
+ } else if (!strcmp(option+6, "bbb")) {
+ s.proto = MSD_PROTO_BBB;
+ } else {
+ BX_ERROR(("Unknown option '%s' for proto:", option+6));
+ }
+ return 1;
}
return 0;
}
bool usb_msd_device_c::init()
{
+ unsigned i;
+
/* If you wish to set DEBUG=report in the code, instead of
* in the configuration, simply uncomment this line. I use
* it when I am working on this emulation.
*/
//LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
+
+ // check to make sure correct speed is used if the proto is uasp
+ if ((s.proto == MSD_PROTO_UASP) && (d.speed < USB_SPEED_HIGH)) {
+ BX_ERROR(("UASP selected on a non-uasp speed device."));
+ s.proto = MSD_PROTO_BBB;
+ }
+ d.alt_iface_max = 0;
if (d.type == USB_MSD_TYPE_DISK) {
if (strlen(s.fname) > 0) {
s.hdimage = DEV_hdimage_init_image(s.image_mode, 0, s.journal);
@@ -504,7 +699,7 @@ bool usb_msd_device_c::init()
s.hdimage->sect_size = s.sect_size;
}
if (s.hdimage->open(s.fname) < 0) {
- BX_ERROR(("could not open hard drive image file '%s'", s.fname));
+ BX_PANIC(("could not open hard drive image file '%s'", s.fname));
return 0;
} else {
s.scsi_dev = new scsi_device_t(s.hdimage, 0, usb_msd_command_complete, (void*)this);
@@ -512,7 +707,7 @@ bool usb_msd_device_c::init()
sprintf(s.info_txt, "USB HD: path='%s', mode='%s', sect_size=%d", s.fname,
s.image_mode, s.hdimage->sect_size);
} else {
- BX_ERROR(("USB HD: disk image not specified"));
+ BX_PANIC(("USB HD: disk image not specified"));
return 0;
}
} else if (d.type == USB_MSD_TYPE_CDROM) {
@@ -528,28 +723,36 @@ bool usb_msd_device_c::init()
if (getonoff(LOGLEV_DEBUG) == ACT_REPORT) {
s.scsi_dev->set_debug_mode();
}
- if (get_speed() == USB_SPEED_SUPER) {
+ if (d.speed == USB_SPEED_SUPER) {
d.dev_descriptor = bx_msd_dev_descriptor3;
d.config_descriptor = bx_msd_config_descriptor3;
d.device_desc_size = sizeof(bx_msd_dev_descriptor3);
- d.config_desc_size = sizeof(bx_msd_config_descriptor3);
- d.endpoint_info[USB_CONTROL_EP].max_packet_size = 512; // Control ep0
- d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
- d.endpoint_info[1].max_packet_size = 1024; // In ep1
- d.endpoint_info[1].max_burst_size = 15;
- d.endpoint_info[2].max_packet_size = 1024; // Out ep2
- d.endpoint_info[2].max_burst_size = 15;
+ // we need to set the length of the descriptor per the protocol used
+ if (s.proto == MSD_PROTO_UASP) {
+ * (Bit16u *) &bx_msd_config_descriptor3[2] =
+ d.config_desc_size = sizeof(bx_msd_config_descriptor3);
+ d.alt_iface_max = 1; // allow alt interface 0 through 1
+ } else {
+ * (Bit16u *) &bx_msd_config_descriptor3[2] =
+ d.config_desc_size = 0x002C;
+ }
+ // initialize the bbb's endpoint data
+ handle_iface_change(MSD_PROTO_BBB);
} else if (get_speed() == USB_SPEED_HIGH) {
d.dev_descriptor = bx_msd_dev_descriptor2;
d.config_descriptor = bx_msd_config_descriptor2;
d.device_desc_size = sizeof(bx_msd_dev_descriptor2);
- d.config_desc_size = sizeof(bx_msd_config_descriptor2);
- d.endpoint_info[USB_CONTROL_EP].max_packet_size = 64; // Control ep0
- d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
- d.endpoint_info[1].max_packet_size = 512; // In ep1
- d.endpoint_info[1].max_burst_size = 0;
- d.endpoint_info[2].max_packet_size = 512; // Out ep2
- d.endpoint_info[2].max_burst_size = 0;
+ // we need to set the length of the descriptor per the protocol used
+ if (s.proto == MSD_PROTO_UASP) {
+ * (Bit16u *) &bx_msd_config_descriptor2[2] =
+ d.config_desc_size = sizeof(bx_msd_config_descriptor2);
+ d.alt_iface_max = 1; // allow alt interface 0 through 1
+ } else {
+ * (Bit16u *) &bx_msd_config_descriptor2[2] =
+ d.config_desc_size = 0x0020;
+ }
+ // initialize the bbb's endpoint data
+ handle_iface_change(MSD_PROTO_BBB);
} else { // USB_SPEED_FULL
d.dev_descriptor = bx_msd_dev_descriptor;
d.config_descriptor = bx_msd_config_descriptor;
@@ -557,15 +760,21 @@ bool usb_msd_device_c::init()
d.config_desc_size = sizeof(bx_msd_config_descriptor);
d.endpoint_info[USB_CONTROL_EP].max_packet_size = 64; // Control ep0
d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
- d.endpoint_info[1].max_packet_size = 64; // In ep1
- d.endpoint_info[1].max_burst_size = 0;
- d.endpoint_info[2].max_packet_size = 64; // Out ep2
- d.endpoint_info[2].max_burst_size = 0;
+ d.endpoint_info[MSD_BBB_DATAIN_EP].max_packet_size = 64; // In ep
+ d.endpoint_info[MSD_BBB_DATAIN_EP].max_burst_size = 0;
+ d.endpoint_info[MSD_BBB_DATAOUT_EP].max_packet_size = 64; // Out ep
+ d.endpoint_info[MSD_BBB_DATAOUT_EP].max_burst_size = 0;
}
d.serial_num = s.scsi_dev->get_serial_number();
s.mode = USB_MSDM_CBW;
d.connected = 1;
s.status_changed = 0;
+
+ // initialize the uasp stream data
+ for (i=0; i> 8) {
case USB_DT_STRING:
BX_DEBUG(("USB_REQ_GET_DESCRIPTOR: String"));
- switch(value & 0xFF) {
+ switch (value & 0xFF) {
case 0xEE:
// Microsoft OS Descriptor check
// We don't support this check, so fail
@@ -705,14 +914,25 @@ int usb_msd_device_c::handle_control(int request, int value, int index, int leng
set_toggle(index, 0);
#endif
ret = 0;
- } else
+ } else {
+ BX_ERROR(("Bad value for clear feature: %d", value));
goto fail;
+ }
break;
case DeviceOutRequest | USB_REQ_SET_SEL:
// Set U1 and U2 System Exit Latency
BX_DEBUG(("SET_SEL (U1 and U2):"));
ret = 0;
break;
+ case DeviceOutRequest | USB_REQ_SET_ISO_DELAY:
+ // Set Isochronous Delay (USB 3.0+)
+ BX_DEBUG(("USB_REQ_SET_ISO_DELAY: %d", value));
+ if ((index == 0) && (length == 0)) { // index and length must be zero
+ // value = setting (ignored)
+ ret = 0;
+ } else
+ goto fail;
+ break;
// Class specific requests
case InterfaceOutClassRequest | MassStorageReset:
case MassStorageReset:
@@ -740,12 +960,61 @@ int usb_msd_device_c::handle_control(int request, int value, int index, int leng
return ret;
}
+void usb_msd_device_c::handle_iface_change(int iface)
+{
+ if (d.speed == USB_SPEED_SUPER) {
+ d.endpoint_info[USB_CONTROL_EP].max_packet_size = 512; // Control ep0
+ d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
+ if (iface == MSD_PROTO_BBB) {
+ // initialize the bbb's endpoint data
+ d.endpoint_info[MSD_BBB_DATAIN_EP].max_packet_size = 1024; // In ep
+ d.endpoint_info[MSD_BBB_DATAIN_EP].max_burst_size = 15;
+ d.endpoint_info[MSD_BBB_DATAOUT_EP].max_packet_size = 1024; // Out ep
+ d.endpoint_info[MSD_BBB_DATAOUT_EP].max_burst_size = 15;
+ } else if (iface == MSD_PROTO_UASP) {
+ d.endpoint_info[MSD_UASP_COMMAND].max_packet_size = 1024;
+ d.endpoint_info[MSD_UASP_COMMAND].max_burst_size = 0;
+ d.endpoint_info[MSD_UASP_STATUS].max_packet_size = 1024;
+ d.endpoint_info[MSD_UASP_STATUS].max_burst_size = 15;
+ d.endpoint_info[MSD_UASP_DATAIN].max_packet_size = 1024;
+ d.endpoint_info[MSD_UASP_DATAIN].max_burst_size = 15;
+ d.endpoint_info[MSD_UASP_DATAOUT].max_packet_size = 1024;
+ d.endpoint_info[MSD_UASP_DATAOUT].max_burst_size = 15;
+ } else {
+ BX_ERROR(("Unknown interface number: %d", iface));
+ }
+ // ben
+ } else if (d.speed == USB_SPEED_HIGH) {
+ d.endpoint_info[USB_CONTROL_EP].max_packet_size = 64; // Control ep0
+ d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
+ if (iface == MSD_PROTO_BBB) {
+ // initialize the bbb's endpoint data
+ d.endpoint_info[MSD_BBB_DATAIN_EP].max_packet_size = 512; // In ep
+ d.endpoint_info[MSD_BBB_DATAIN_EP].max_burst_size = 0;
+ d.endpoint_info[MSD_BBB_DATAOUT_EP].max_packet_size = 512; // Out ep
+ d.endpoint_info[MSD_BBB_DATAOUT_EP].max_burst_size = 0;
+ } else if (iface == MSD_PROTO_UASP) {
+ d.endpoint_info[MSD_UASP_COMMAND].max_packet_size = 512;
+ d.endpoint_info[MSD_UASP_COMMAND].max_burst_size = 0;
+ d.endpoint_info[MSD_UASP_STATUS].max_packet_size = 512;
+ d.endpoint_info[MSD_UASP_STATUS].max_burst_size = 0;
+ d.endpoint_info[MSD_UASP_DATAIN].max_packet_size = 512;
+ d.endpoint_info[MSD_UASP_DATAIN].max_burst_size = 0;
+ d.endpoint_info[MSD_UASP_DATAOUT].max_packet_size = 512;
+ d.endpoint_info[MSD_UASP_DATAOUT].max_burst_size = 0;
+ } else {
+ BX_ERROR(("Unknown interface number: %d", iface));
+ }
+ }
+}
+
int usb_msd_device_c::handle_data(USBPacket *p)
{
struct usb_msd_cbw cbw;
int ret = 0;
Bit8u devep = p->devep;
Bit8u *data = p->data;
+ Bit8u aIface = get_aIface();
int len = p->len;
bool was_status = false; // so don't dump the status packet twice
@@ -754,145 +1023,157 @@ int usb_msd_device_c::handle_data(USBPacket *p)
BX_DEBUG(("EP%d transfer length (%d) is greater than Max Packet Size (%d).", p->devep, p->len, get_mps(p->devep)));
}
- switch (p->pid) {
- case USB_TOKEN_OUT:
- usb_dump_packet(data, len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
- if (devep != 2)
- goto fail;
+ // are we doing bbb interface?
+ if (aIface == MSD_PROTO_BBB) {
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ usb_dump_packet(data, len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
+ if (devep != MSD_BBB_DATAOUT_EP)
+ goto fail;
- switch (s.mode) {
- case USB_MSDM_CBW:
- if (len != 31) {
- BX_ERROR(("bad CBW len (%d)", len));
- goto fail;
- }
- memcpy(&cbw, data, 31);
- if (dtoh32(cbw.sig) != 0x43425355) {
- BX_ERROR(("bad signature %08X", dtoh32(cbw.sig)));
- goto fail;
- }
- BX_DEBUG(("command on LUN %d", cbw.lun));
- s.tag = dtoh32(cbw.tag);
- s.data_len = dtoh32(cbw.data_len);
- if (s.data_len == 0) {
- s.mode = USB_MSDM_CSW;
- } else if (cbw.flags & 0x80) {
- s.mode = USB_MSDM_DATAIN;
- } else {
- s.mode = USB_MSDM_DATAOUT;
- }
- BX_DEBUG(("command tag 0x%X flags %02X cmd_len %d data_len %d",
- s.tag, cbw.flags, cbw.cmd_len, s.data_len));
- s.residue = 0;
- s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.cmd_len, cbw.lun, d.async_mode);
- if (s.residue == 0) {
- if (s.mode == USB_MSDM_DATAIN) {
- s.scsi_dev->scsi_read_data(s.tag);
- } else if (s.mode == USB_MSDM_DATAOUT) {
- s.scsi_dev->scsi_write_data(s.tag);
+ switch (s.mode) {
+ case USB_MSDM_CBW:
+ if (len != 31) {
+ BX_ERROR(("bad CBW len (%d)", len));
+ goto fail;
}
- }
- ret = len;
- break;
-
- case USB_MSDM_DATAOUT:
- BX_DEBUG(("data out %d/%d", len, s.data_len));
- if (len > (int)s.data_len)
- goto fail;
-
- s.usb_buf = data;
- s.usb_len = len;
- while (s.usb_len && s.scsi_len) {
- copy_data();
- }
- if (s.residue && s.usb_len) {
- s.data_len -= s.usb_len;
- if (s.data_len == 0)
+ memcpy(&cbw, data, 31);
+ if (dtoh32(cbw.sig) != 0x43425355) {
+ BX_ERROR(("bad signature %08X", dtoh32(cbw.sig)));
+ goto fail;
+ }
+ BX_DEBUG(("command on LUN %d", cbw.lun));
+ s.tag = dtoh32(cbw.tag);
+ s.data_len = dtoh32(cbw.data_len);
+ if (s.data_len == 0) {
s.mode = USB_MSDM_CSW;
- s.usb_len = 0;
- }
- if (s.usb_len) {
+ } else if (cbw.flags & 0x80) {
+ s.mode = USB_MSDM_DATAIN;
+ } else {
+ s.mode = USB_MSDM_DATAOUT;
+ }
+ BX_DEBUG(("command tag 0x%X flags %02X cmd_len %d data_len %d",
+ s.tag, cbw.flags, cbw.cmd_len, s.data_len));
+ s.residue = 0;
+ s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.cmd_len, cbw.lun, d.async_mode);
+ if (s.residue == 0) {
+ if (s.mode == USB_MSDM_DATAIN) {
+ s.scsi_dev->scsi_read_data(s.tag);
+ } else if (s.mode == USB_MSDM_DATAOUT) {
+ s.scsi_dev->scsi_write_data(s.tag);
+ }
+ }
+ ret = len;
+ break;
+
+ case USB_MSDM_DATAOUT:
+ BX_DEBUG(("data out %d/%d", len, s.data_len));
+ if (len > (int)s.data_len)
+ goto fail;
+
+ s.usb_buf = data;
+ s.usb_len = len;
+ while (s.usb_len && s.scsi_len) {
+ copy_data();
+ }
+ 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.usb_len) {
+ BX_DEBUG(("deferring packet %p", p));
+ usb_defer_packet(p, this);
+ s.packet = p;
+ ret = USB_RET_ASYNC;
+ } else {
+ ret = len;
+ }
+ break;
+
+ default:
+ BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_OUT: (0x%02X)", s.mode));
+ goto fail;
+ }
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != MSD_BBB_DATAIN_EP)
+ goto fail;
+
+ switch (s.mode) {
+ case USB_MSDM_DATAOUT:
+ if (s.data_len != 0 || len < 13)
+ goto fail;
BX_DEBUG(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
- } else {
- ret = len;
- }
- break;
+ break;
- default:
- BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_OUT: (0x%02X)", s.mode));
- goto fail;
- }
- break;
+ case USB_MSDM_CSW:
+ BX_DEBUG(("command status %d tag 0x%x, len %d",
+ s.result, s.tag, len));
+ if (len < 13)
+ return ret;
- case USB_TOKEN_IN:
- if (devep != 1)
- goto fail;
+ send_status(p);
+ s.mode = USB_MSDM_CBW;
+ was_status = true;
+ ret = 13;
+ break;
- switch (s.mode) {
- case USB_MSDM_DATAOUT:
- if (s.data_len != 0 || len < 13)
+ case USB_MSDM_DATAIN:
+ BX_DEBUG(("data in %d/%d", len, s.data_len));
+ if (len > (int)s.data_len)
+ len = s.data_len;
+ s.usb_buf = data;
+ s.usb_len = len;
+ while (s.usb_len && s.scsi_len) {
+ copy_data();
+ }
+ 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.usb_len) {
+ BX_DEBUG(("deferring packet %p", p));
+ usb_defer_packet(p, this);
+ s.packet = p;
+ ret = USB_RET_ASYNC;
+ } else {
+ ret = len;
+ }
+ break;
+
+ default:
+ BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN: (0x%02X)", s.mode));
goto fail;
- BX_DEBUG(("deferring packet %p", p));
- usb_defer_packet(p, this);
- s.packet = p;
- ret = USB_RET_ASYNC;
- break;
+ }
+ if (!was_status && (ret > 0)) usb_dump_packet(data, ret, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
+ break;
- case USB_MSDM_CSW:
- BX_DEBUG(("command status %d tag 0x%x, len %d",
- s.result, s.tag, len));
- if (len < 13)
- return ret;
-
- send_status(p);
- s.mode = USB_MSDM_CBW;
- was_status = true;
- ret = 13;
- break;
-
- case USB_MSDM_DATAIN:
- BX_DEBUG(("data in %d/%d", len, s.data_len));
- if (len > (int)s.data_len)
- len = s.data_len;
- s.usb_buf = data;
- s.usb_len = len;
- while (s.usb_len && s.scsi_len) {
- copy_data();
- }
- 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.usb_len) {
- BX_DEBUG(("deferring packet %p", p));
- usb_defer_packet(p, this);
- s.packet = p;
- ret = USB_RET_ASYNC;
- } else {
- ret = len;
- }
- break;
-
- default:
- BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN: (0x%02X)", s.mode));
- goto fail;
- }
- if (!was_status && (ret > 0)) usb_dump_packet(data, ret, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
- break;
-
- default:
- BX_ERROR(("USB MSD handle_data: bad token"));
-fail:
- d.stall = 1;
- ret = USB_RET_STALL;
- break;
- }
+ default:
+ BX_ERROR(("USB MSD handle_data: bad token"));
+ fail:
+ d.stall = 1;
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ // must be uasp interface
+ } else if (aIface == MSD_PROTO_UASP) {
+ ret = uasp_handle_data(p);
+
+ } else {
+ BX_ERROR(("Unknown interface number: %d", aIface));
+ d.stall = 1;
+ ret = USB_RET_STALL;
+ }
return ret;
}
@@ -943,52 +1224,60 @@ void usb_msd_device_c::usb_msd_command_complete(void *this_ptr, int reason, Bit3
void usb_msd_device_c::command_complete(int reason, Bit32u tag, Bit32u arg)
{
USBPacket *p = s.packet;
+ Bit8u aIface = get_aIface();
- if (tag != s.tag) {
- BX_ERROR(("usb-msd_command_complete: unexpected SCSI tag 0x%x", tag));
- }
- if (reason == SCSI_REASON_DONE) {
- BX_DEBUG(("command complete %d", arg));
- s.residue = s.data_len;
- s.result = arg != 0;
- if (s.packet) {
- if ((s.data_len == 0) && (s.mode == USB_MSDM_DATAOUT)) {
- send_status(p);
- s.mode = USB_MSDM_CBW;
- } else if (s.mode == USB_MSDM_CSW) {
- send_status(p);
- 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;
+ // if bulk/bulk/bulk
+ if (aIface == MSD_PROTO_BBB) {
+ if (tag != s.tag) {
+ BX_ERROR(("usb-msd_command_complete: unexpected SCSI tag 0x%x", tag));
+ }
+ if (reason == SCSI_REASON_DONE) {
+ BX_DEBUG(("command complete %d", arg));
+ s.residue = s.data_len;
+ s.result = arg != 0;
+ if (s.packet) {
+ if ((s.data_len == 0) && (s.mode == USB_MSDM_DATAOUT)) {
+ send_status(p);
+ s.mode = USB_MSDM_CBW;
+ } else if (s.mode == USB_MSDM_CSW) {
+ send_status(p);
+ 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;
+ }
+ if (s.data_len == 0)
+ s.mode = USB_MSDM_CSW;
}
- if (s.data_len == 0)
- s.mode = USB_MSDM_CSW;
- }
- s.packet = NULL;
- usb_packet_complete(p);
- } else if (s.data_len == 0) {
- s.mode = USB_MSDM_CSW;
- }
- return;
- }
- s.scsi_len = arg;
- s.scsi_buf = s.scsi_dev->scsi_get_buf(tag);
- if (p) {
- if ((s.scsi_len > 0) && (s.mode == USB_MSDM_DATAIN)) {
- usb_dump_packet(s.scsi_buf, p->len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
- }
- copy_data();
- if (s.usb_len == 0) {
- BX_DEBUG(("packet complete %p", p));
- if (s.packet != NULL) {
s.packet = NULL;
usb_packet_complete(p);
+ } else if (s.data_len == 0) {
+ s.mode = USB_MSDM_CSW;
+ }
+ return;
+ }
+ s.scsi_len = arg;
+ s.scsi_buf = s.scsi_dev->scsi_get_buf(tag);
+ if (p) {
+ if ((s.scsi_len > 0) && (s.mode == USB_MSDM_DATAIN)) {
+ usb_dump_packet(s.scsi_buf, p->len, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
+ }
+ copy_data();
+ if (s.usb_len == 0) {
+ BX_DEBUG(("packet complete %p", p));
+ if (s.packet != NULL) {
+ s.packet = NULL;
+ usb_packet_complete(p);
+ }
}
}
+
+ // else if uasp
+ } else if (aIface == MSD_PROTO_UASP) {
+ uasp_command_complete(reason, tag, arg);
}
}
diff --git a/bochs/iodev/usb/usb_msd.h b/bochs/iodev/usb/usb_msd.h
index c04bbf707..65f1bf3d0 100644
--- a/bochs/iodev/usb/usb_msd.h
+++ b/bochs/iodev/usb/usb_msd.h
@@ -30,6 +30,36 @@ class device_image_t;
class cdrom_base_c;
class scsi_device_t;
+#define UASP_MAX_STREAMS 5
+#define UASP_MAX_STREAMS_N (1 << (UASP_MAX_STREAMS + 1))
+
+// protocol
+#define MSD_PROTO_BBB 0 // bulk only
+#define MSD_PROTO_UASP 1 // UASP protocol (uses streams)
+
+// BBB endpoint numbers (must remain consecutive)
+#define MSD_BBB_DATAIN_EP 1
+#define MSD_BBB_DATAOUT_EP 2
+// UASP endpoint numbers (must remain consecutive)
+#define MSD_UASP_COMMAND 1
+#define MSD_UASP_STATUS 2
+#define MSD_UASP_DATAIN 3
+#define MSD_UASP_DATAOUT 4
+
+typedef struct UASPRequest {
+ Bit32u mode; // current direction and other flags
+ Bit32u data_len; // count of bytes requested (from command requested)
+ Bit32u residue; // = data_len - actual_len
+ Bit32u scsi_len; //
+ Bit8u *scsi_buf; //
+ Bit32u usb_len; //
+ Bit8u *usb_buf; //
+ int result; //
+ Bit32u tag; // tag number from command ui
+ USBPacket *p; //
+ USBPacket *status; //
+} UASPRequest;
+
class usb_msd_device_c : public usb_device_c {
public:
usb_msd_device_c(const char *devname);
@@ -44,6 +74,7 @@ public:
virtual void handle_reset();
virtual int handle_control(int request, int value, int index, int length, Bit8u *data);
virtual int handle_data(USBPacket *p);
+ virtual void handle_iface_change(int iface);
virtual void register_state_specific(bx_list_c *parent);
virtual void cancel_packet(USBPacket *p);
bool set_inserted(bool value);
@@ -60,6 +91,7 @@ private:
struct {
// members set in constructor / init
char *image_mode;
+ int proto; // MSD_PROTO_BBB (default), MSD_PROTO_UASP (uses streams)
device_image_t *hdimage;
cdrom_base_c *cdrom;
scsi_device_t *scsi_dev;
@@ -84,11 +116,28 @@ private:
Bit8u *scsi_buf;
Bit8u *usb_buf;
USBPacket *packet;
+ // UASP (w/streams)
+ UASPRequest uasp_request[UASP_MAX_STREAMS_N];
} s;
static const char *cdrom_path_handler(bx_param_string_c *param, bool set,
const char *oldval, const char *val, int maxlen);
static Bit64s cdrom_status_handler(bx_param_c *param, bool set, Bit64s val);
+
+public:
+ int uasp_handle_data(USBPacket *p);
+ void uasp_initialize_request(int tag);
+ UASPRequest *uasp_find_request(Bit32u tag);
+ Bit32u get_data_len(const struct S_UASP_INPUT *input, Bit8u *buf);
+ const struct S_UASP_INPUT *uasp_get_info(Bit8u command, Bit8u serv_action);
+ int uasp_do_stall(UASPRequest *req);
+ int uasp_do_command(USBPacket *p);
+ int uasp_process_request(USBPacket *p, int index);
+ int uasp_do_data(UASPRequest *req, USBPacket *p);
+ int uasp_do_ready(UASPRequest *req, USBPacket *p);
+ int uasp_do_status(UASPRequest *req, USBPacket *p);
+ void uasp_copy_data(UASPRequest *req);
+ void uasp_command_complete(int reason, Bit32u tag, Bit32u arg);
};
#endif
diff --git a/bochs/iodev/usb/usb_printer.cc b/bochs/iodev/usb/usb_printer.cc
index b458864b7..a42c2a3e4 100644
--- a/bochs/iodev/usb/usb_printer.cc
+++ b/bochs/iodev/usb/usb_printer.cc
@@ -216,6 +216,7 @@ bool usb_printer_device_c::init()
} else {
BX_ERROR(("USB printer: missing output file"));
}
+ d.alt_iface_max = 0;
return 0;
}
diff --git a/bochs/iodev/usb/usb_uasp.cc b/bochs/iodev/usb/usb_uasp.cc
new file mode 100644
index 000000000..ab05ee3a2
--- /dev/null
+++ b/bochs/iodev/usb/usb_uasp.cc
@@ -0,0 +1,509 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id$
+/////////////////////////////////////////////////////////////////////////
+//
+// Experimental USB Attached SCSI Protocol support (UASP)
+//
+// Copyright (C) 2023 Benjamin D Lunt (fys [at] fysnet [dot] net)
+// Copyright (C) 2023 The Bochs Project
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/////////////////////////////////////////////////////////////////////////
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "iodev.h"
+
+#if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
+#include "usb_common.h"
+#include "hdimage/cdrom.h"
+#include "hdimage/hdimage.h"
+#include "scsi_device.h"
+#include "usb_msd.h"
+
+#define LOG_THIS
+
+// Information Unit Types
+// byte[0] in uasp command and status????
+#define IU_CMD 1
+#define IU_SENSE 3
+#define IU_RESP 4
+#define IU_TMF 5
+#define IU_RRDY 6
+#define IU_WRDY 7
+
+#define IU_CMD_LEN 32
+#define IU_SENSE_LEN 16
+#define IU_RESP_LEN 8
+#define IU_TMF_LEN 16
+#define IU_RRDY_LEN 4
+#define IU_WRDY_LEN 4
+
+// Task Attribute
+#define UASP_TASK_SIMPLE 0
+#define UASP_TASK_HOQ 1
+#define UASP_TASK_ORDERED 2
+#define UASP_TASK_ACA 4
+
+#define UASP_GET_ACTIVE(m) (((m) & 1) > 0) // is the request active?
+#define UASP_SET_ACTIVE(m) ((m) > 0) // clearing/setting active needs to clear everything else
+#define UASP_GET_STATUS(m) (((m) & 2) > 0) // have we sent the RRIU or WRIU?
+#define UASP_SET_STATUS(m) ((m) | 2) // set that we have sent the RRIU or WRIU
+#define UASP_GET_CMND(m) (((m) & 4) > 0) // have we received and processed the command yet?
+#define UASP_SET_CMND(m) ((m) | 4) // set that we have received and processed the command
+#define UASP_GET_COMPLETE(m) (((m) & 8) > 0) // is the datain/dataout transfer complete?
+#define UASP_SET_COMPLETE(m) ((m) | 8) // set that the datain/dataout transfer is complete
+#define UASP_GET_DIR(m) (((m) & 0x0000FF00) >> 8)
+#define UASP_SET_DIR(m,d) (((m) & ~0x0000FF00) | ((d) << 8))
+
+struct S_UASP_COMMAND {
+ Bit8u id; // Information Unit Type
+ Bit8u rsvd0; //
+ Bit16u tag; // big endian
+ Bit8u prio_attr; // Task Attribute
+ Bit8u rsvd1; //
+ Bit8u len; // len (if more than 16 bytes) (i.e.: if command len is 20 bytes, this field is 4) (bottom 2 bits are reserved)
+ Bit8u rsvd2; //
+ Bit64u lun; // eight byte lun
+ Bit8u com_block[16]; // command block
+};
+
+struct S_UASP_STATUS {
+ Bit8u id; // usually IU_SENSE ?
+ Bit8u rsvd0;
+ Bit16u tag; // big endian
+ Bit16u stat_qual; // big endian
+ Bit8u status;
+ Bit8u resv1[7];
+ Bit16u len; // big endian
+ Bit8u sense[18];
+};
+
+#define UASP_FROM_COMMAND ((Bit32u) -1)
+#define U_NONE 0
+#define U_SRV_ACT (1<<0)
+#define U_IS_LBA (1<<1)
+struct S_UASP_INPUT {
+ Bit8u command; // command byte
+ Bit8u serv_action; // command/service action
+ Bit8u cmd_len; // length of the command buf
+ Bit8u direction; // in or out
+ Bit8u flags; // see S_UASP_FLAGS_*
+ Bit32u data_len; // if the command is fixed at a count of bytes, this holds that count (next two members are ignored)
+ int offset; // byte offset of data length requested
+ int size; // size in bytes of this field
+};
+
+int usb_msd_device_c::uasp_handle_data(USBPacket *p)
+{
+ int ret = 0;
+ Bit8u *data = p->data;
+ int len = p->len;
+ int index = p->strm_pid; // high-speed device will be zero. super-speed device will have the stream id.
+
+ BX_INFO(("uasp_handle_data(): %X ep=%d index=%d len=%d", p->pid, p->devep, index, len));
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (p->devep == MSD_UASP_DATAOUT) { // Bulk out pipe
+ if ((index < 0) || (index > UASP_MAX_STREAMS_N))
+ goto fail;
+ ret = uasp_process_request(p, index);
+ if (ret == USB_RET_ASYNC)
+ usb_defer_packet(p, this);
+ } else if (p->devep == MSD_UASP_COMMAND) { // Command pipe
+ ret = uasp_do_command(p);
+ }
+ break;
+
+ case USB_TOKEN_IN:
+ // TODO: these are the same
+ if ((p->devep == MSD_UASP_DATAIN) || // Bulk in pipe
+ (p->devep == MSD_UASP_STATUS)) { // Status pipe
+ if ((index < 0) || (index > UASP_MAX_STREAMS_N))
+ goto fail;
+ ret = uasp_process_request(p, index);
+ if (ret == USB_RET_ASYNC)
+ usb_defer_packet(p, this);
+ }
+ break;
+
+ default:
+ BX_ERROR(("USB MSD (UASP) handle_data: bad token"));
+fail:
+ d.stall = 1;
+ ret = USB_RET_STALL;
+ }
+
+ return ret;
+}
+
+void usb_msd_device_c::uasp_initialize_request(int index)
+{
+ UASPRequest *req = &s.uasp_request[index];
+
+ req->mode = UASP_SET_ACTIVE(1); // setting to 1 clears other flags
+ req->data_len = 0;
+ req->result = 0;
+ req->tag = 0;
+ req->scsi_len = 0;
+ req->status = NULL;
+ req->p = NULL;
+
+ d.stall = 0;
+}
+
+UASPRequest *usb_msd_device_c::uasp_find_request(Bit32u tag)
+{
+ for (int i=0; isize) {
+ case 1: // byte
+ ret = * (Bit8u *) &buf[input->offset];
+ break;
+ case 2: // word
+ ret = * (Bit16u *) &buf[input->offset];
+ ret = bx_bswap16(ret);
+ break;
+ case 4: // dword
+ ret = * (Bit32u *) &buf[input->offset];
+ ret = bx_bswap32(ret);
+ break;
+ }
+
+ // is lba instead of bytes?
+ if (input->flags & U_IS_LBA)
+ ret *= 512; // TODO: get size of a sector/block
+
+ return ret;
+}
+
+// get information about the command requested
+const struct S_UASP_INPUT *usb_msd_device_c::uasp_get_info(Bit8u command, Bit8u serv_action)
+{
+ int i = 0;
+
+ do {
+ if (bx_uasp_info_array[i].command == command) {
+ if (bx_uasp_info_array[i].flags & U_SRV_ACT) {
+ if (bx_uasp_info_array[i].serv_action == serv_action)
+ return &bx_uasp_info_array[i];
+ } else
+ return &bx_uasp_info_array[i];
+ }
+ i++;
+ } while (bx_uasp_info_array[i].command != 0xFF);
+
+ BX_ERROR(("uasp_get_info: Unknown command found: %02X/%02X", command, serv_action));
+ return NULL;
+}
+
+int usb_msd_device_c::uasp_do_stall(UASPRequest *req)
+{
+ USBPacket *p = req->p;
+
+ if (p) {
+ req->p = NULL;
+ p->len = USB_RET_STALL;
+ usb_packet_complete(p);
+ }
+
+ p = req->status;
+ if (p) {
+ req->status = NULL;
+ p->len = USB_RET_STALL;
+ usb_packet_complete(p);
+ }
+
+ req->mode = UASP_SET_ACTIVE(0);
+ d.stall = 1;
+ return USB_RET_STALL;
+}
+
+int usb_msd_device_c::uasp_do_command(USBPacket *p)
+{
+ struct S_UASP_COMMAND *ui = (struct S_UASP_COMMAND *) p->data;
+ const struct S_UASP_INPUT *input;
+ Bit8u lun = (Bit8u) (ui->lun >> 56);
+ int index = (get_speed() == USB_SPEED_HIGH) ? 0 : bx_bswap16(ui->tag);
+ UASPRequest *req = &s.uasp_request[index];
+
+ usb_dump_packet(p->data, p->len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
+ if (ui->id == IU_CMD) {
+ if ((ui->prio_attr & 0x7) == UASP_TASK_SIMPLE) {
+ if (!UASP_GET_ACTIVE(req->mode))
+ uasp_initialize_request(index);
+ // get information about the command requested
+ input = uasp_get_info(ui->com_block[0], ui->com_block[1] & 0x1F);
+ // if unknown command, stall
+ if (input == NULL)
+ return uasp_do_stall(req);
+
+ // get the count of bytes requested, command length, etc.
+ req->tag = bx_bswap16(ui->tag);
+ req->mode = UASP_SET_DIR(req->mode, input->direction);
+ req->data_len = (input->data_len == UASP_FROM_COMMAND) ? get_data_len(input, ui->com_block) : input->data_len;
+ BX_INFO(("uasp command id %d, tag 0x%04X, command 0x%X, len = %d, data_len = %d", ui->id, req->tag, ui->com_block[0], p->len, req->data_len));
+
+ s.scsi_dev->scsi_send_command(req->tag, ui->com_block, input->cmd_len, lun, d.async_mode);
+ if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
+ s.scsi_dev->scsi_read_data(req->tag);
+ } else if (UASP_GET_DIR(req->mode) == USB_TOKEN_OUT) {
+ s.scsi_dev->scsi_write_data(req->tag);
+ }
+
+ // if a high-speed device, we need to send the status ready ui
+ if ((get_speed() == USB_SPEED_HIGH) && req->status) {
+ USBPacket *s = req->status;
+ s->len = uasp_do_ready(req, s);
+ req->status = NULL;
+ usb_packet_complete(s);
+ }
+
+ req->mode = UASP_SET_CMND(req->mode); // mark that we have sent the command
+ return p->len;
+ } else
+ BX_ERROR(("uasp: unknown/unsupported task attribute. %d", (ui->prio_attr & 0x7)));
+ } else
+ BX_ERROR(("uasp: unknown id on command pipe. %d", ui->id));
+
+ return 0;
+}
+
+// if we have already done the command and there is data
+// waiting, we can process the packet request.
+// else ret USB_RET_ASYNC
+int usb_msd_device_c::uasp_process_request(USBPacket *p, int index)
+{
+ UASPRequest *req = &s.uasp_request[index];
+ int len = p->len;
+
+ if (!UASP_GET_ACTIVE(req->mode))
+ uasp_initialize_request(index);
+
+ // if it is the status pipe
+ if (p->devep == MSD_UASP_STATUS) {
+ // TODO: check to see if the direction is in
+ if (UASP_GET_COMPLETE(req->mode)) {
+ return uasp_do_status(req, p);
+ } else {
+ if ((get_speed() == USB_SPEED_HIGH) && UASP_GET_CMND(req->mode) && !UASP_GET_STATUS(req->mode)) {
+ return uasp_do_ready(req, p);
+ } else {
+ req->status = p;
+ return USB_RET_ASYNC;
+ }
+ }
+ }
+
+ // have we received the Command yet?
+ if (!UASP_GET_CMND(req->mode)) {
+ req->p = p;
+ return USB_RET_ASYNC;
+ }
+
+ // check to make sure the direction is correct
+ if (p->pid != UASP_GET_DIR(req->mode)) {
+ BX_INFO(("Found packet with wrong direction."));
+ uasp_do_stall(req);
+ }
+
+ // do the transfer for this packet
+ len = uasp_do_data(req, p);
+ BX_INFO(("uasp: (0) data: transferred %d bytes", len));
+
+ return len;
+}
+
+int usb_msd_device_c::uasp_do_data(UASPRequest *req, USBPacket *p)
+{
+ int len = p->len;
+
+ // TODO: if (dir != req->dir) error
+ if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
+ BX_INFO(("data in %d/%d/%d", len, req->data_len, req->scsi_len));
+ } else if (UASP_GET_DIR(req->mode) == USB_TOKEN_OUT) {
+ BX_INFO(("data out %d/%d/%d", len, req->data_len, req->scsi_len));
+ }
+
+ if (len > (int) req->data_len)
+ len = req->data_len;
+ req->usb_buf = p->data;
+ req->usb_len = len;
+ while (req->usb_len && req->scsi_len) {
+ uasp_copy_data(req);
+ }
+
+ if (req->residue && req->usb_len) {
+ req->data_len -= req->usb_len;
+ memset(req->usb_buf, 0, req->usb_len);
+ req->usb_len = 0;
+ }
+
+ usb_dump_packet(p->data, len, 0, p->devaddr, ((UASP_GET_DIR(req->mode) == USB_TOKEN_IN) ? USB_DIR_IN : USB_DIR_OUT) | p->devep, USB_TRANS_TYPE_BULK, false, true);
+
+ return len;
+}
+
+int usb_msd_device_c::uasp_do_ready(UASPRequest *req, USBPacket *p)
+{
+ struct S_UASP_STATUS *status;
+
+ // do the RRIU or WRIU
+ status = (struct S_UASP_STATUS *) p->data;
+ status->id = (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) ? IU_RRDY : IU_WRDY;
+ status->rsvd0 = 0;
+ status->tag = bx_bswap16((Bit16u) req->tag);
+ usb_dump_packet(p->data, IU_RRDY_LEN, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
+
+ req->mode = UASP_SET_STATUS(req->mode);
+
+ return IU_RRDY_LEN;
+}
+
+int usb_msd_device_c::uasp_do_status(UASPRequest *req, USBPacket *p)
+{
+ struct S_UASP_STATUS *status;
+
+ // do the status
+ BX_INFO(("uasp: Sending Status:"));
+ status = (struct S_UASP_STATUS *) p->data;
+ memset(status, 0, 16);
+ status->id = IU_SENSE;
+ status->tag = bx_bswap16((Bit16u) req->tag);
+ status->status = 0; // good return
+ status->len = bx_bswap16(0); // no sense data
+ usb_dump_packet(p->data, IU_SENSE_LEN, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
+
+ req->mode = UASP_SET_ACTIVE(0);
+
+ // return the size of the status block
+ return IU_SENSE_LEN;
+}
+
+void usb_msd_device_c::uasp_copy_data(UASPRequest *req)
+{
+ Bit32u len = req->usb_len;
+ if (len > req->scsi_len)
+ len = req->scsi_len;
+ if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
+ memcpy(req->usb_buf, req->scsi_buf, len);
+ } else {
+ memcpy(req->scsi_buf, req->usb_buf, len);
+ }
+ req->usb_len -= len;
+ req->scsi_len -= len;
+ req->usb_buf += len;
+ req->scsi_buf += len;
+ req->data_len -= len;
+ if (req->scsi_len == 0) {
+ if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
+ s.scsi_dev->scsi_read_data(req->tag);
+ } else {
+ s.scsi_dev->scsi_write_data(req->tag);
+ }
+ }
+}
+
+void usb_msd_device_c::uasp_command_complete(int reason, Bit32u tag, Bit32u arg)
+{
+ USBPacket *p;
+ UASPRequest *req = uasp_find_request(tag);
+
+ BX_INFO(("uasp_command_complete: reason %d, arg %d, tag 0x%04X", reason, arg, tag));
+
+ if (req == NULL)
+ return;
+
+ if (reason == SCSI_REASON_DONE) {
+ req->residue = req->data_len;
+ req->result = arg != 0;
+ req->mode = UASP_SET_COMPLETE(req->mode); // mark that are transfer is complete
+ // do the status
+ p = req->status;
+ if (p) {
+ p->len = uasp_do_status(req, p);
+ BX_INFO(("uasp: status: transferred %d bytes (residue = %d)", p->len, req->residue));
+ req->status = NULL;
+ usb_packet_complete(p);
+ }
+ return;
+ }
+
+ // reason == SCSI_REASON_DATA
+ req->scsi_len = arg;
+ req->scsi_buf = s.scsi_dev->scsi_get_buf(tag);
+ p = req->p;
+ if (p) {
+ p->len = uasp_do_data(req, p);
+ BX_INFO(("uasp: (1) data: transferred %d bytes", p->len));
+ //if (req->usb_len == 0) {
+ BX_DEBUG(("packet complete 0x%p", p));
+ req->p = NULL;
+ usb_packet_complete(p);
+ //}
+ }
+}
+
+#endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
diff --git a/bochs/iodev/usb/usb_xhci.cc b/bochs/iodev/usb/usb_xhci.cc
index 5d5c658ea..ac46b81fc 100644
--- a/bochs/iodev/usb/usb_xhci.cc
+++ b/bochs/iodev/usb/usb_xhci.cc
@@ -34,24 +34,28 @@
*
* I have tested in with my own tests and WinXP Home Edition SP3 and Windows 7 Ultimate.
*
- * Use port1 and port2 to emulate a Super-speed device, and use port3 and port4
- * to emulate Low-, Full-, or High-speed devices.
+ * Assuming a two-socket, four-port root hub:
+ * Use port1 and port2 to emulate a Super-speed device, and use port3 and port4
+ * to emulate Low-, Full-, or High-speed devices.
+ * (Other settings, use the first half as Super-speed, and the last half as the other speeds)
*
* Examples:
- * usb_xhci: enabled=1, port3=disk:"usbdisk.img", options3=speed:low
- * usb_xhci: enabled=1, port3=disk:"usbdisk.img", options3=speed:full
- * usb_xhci: enabled=1, port3=disk:"usbdisk.img", options3=speed:high
- * usb_xhci: enabled=1, port1=disk:"usbdisk.img", options1=speed:super
+ * usb_xhci: enabled=1, port3=mouse, options3="speed:low"
+ * usb_xhci: enabled=1, port3=disk, options3="speed:full, path:usbdisk.img"
+ * usb_xhci: enabled=1, port3=disk, options3="speed:high, path:usbdisk.img"
+ * usb_xhci: enabled=1, port1=disk, options1="speed:super, path:usbdisk.img"
+ * usb_xhci: enabled=1, port1=cdrom, options1="speed:super, path:cdrom.iso"
*
- * Currently, only a USB MSD Thumb Drive device is emulated as a Super-speed device.
- * All other emulated devices are Low- or Full-speed devices.
+ * Currently, only a USB MSD Thumb Drive device ("=disk") and a USB MSD CDROM ("=cdrom")
+ * can be emulated as a Super-speed device.
+ * All other emulated devices must be Low-, Full-, or High-speed devices.
*
* This code is 32- and 64-bit addressing aware (uses: bx_phy_address & ADDR_CAP_64)
*
* This emulation is coded with the new registers of xHCI version 1.10. However,
- * nothing more has been done to emulate 1.10 features.
- * This emulation is set with VERSION_MAJOR and VERSION_MINOR as 0x01 and 0x10
- * respectively (version 1.10).
+ * at this time, nothing more has been done to emulate 1.10 features.
+ * This emulation is set with VERSION_MAJOR and VERSION_MINOR as 0x01 and 0x00
+ * respectively (version 1.00).
*
* This emulation seems to work as expected. It may not be completly accorate, though
* it is my intention to make it as close as possible. If you find an error or would
@@ -69,10 +73,11 @@
#define LOG_THIS theUSB_XHCI->
-bx_usb_xhci_c* theUSB_XHCI = NULL;
+bx_usb_xhci_c *theUSB_XHCI = NULL;
-Bit8u port_speed_allowed[USB_XHCI_PORTS] = { USB3, USB3, USB2, USB2 };
-Bit8u ext_caps[EXT_CAPS_SIZE] = {
+#define PROTOCOL_UBS3_OFFSET (0x510 - 0x500) // offset to the USB3 protocol struct
+#define PROTOCOL_UBS2_OFFSET (0x524 - 0x500) // offset to the USB2 protocol struct
+static Bit8u ext_caps[EXT_CAPS_SIZE] = {
/* 0x500 */ 0x01, // Legacy Support
0x04, // next = 0x04 * 4 = 0x510
0x00, 0x00, // Semaphores
@@ -80,25 +85,27 @@ Bit8u ext_caps[EXT_CAPS_SIZE] = {
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // filler
+ // this block is modified by init()
/* 0x510 */ 0x02, // Port Protocol
0x05, // next = 0x05 * 4 = 0x524
0x00, 0x03, // Version 3.0
0x55, 0x53, 0x42, 0x20, // "USB "
0x01, // 1-based starting index
0x02, // count of 2 port registers starting at base above
- 0x00, 0x00, // reserved
- 0x00, 0x00, 0x00, 0x00, // reserved
- 0x00, 0x00, 0x00, 0x00, // filler
+ 0x00, 0x00, // flags
+ 0x00, 0x00, 0x00, 0x00, // 8 bytes of filler (unnecessary to this emulation
+ 0x00, 0x00, 0x00, 0x00, // but it exists on the uPD720202 hardware)
+ // this block is modified by init()
/* 0x524 */ 0x02, // Port Protocol
0x07, // next = 0x07 * 4 = 0x540
0x00, 0x02, // Version 2.0
0x55, 0x53, 0x42, 0x20, // "USB "
0x03, // 1-based starting index
0x02, // count of 2 port registers starting at base above
- 0x00, 0x00, //
- 0x00, 0x00, 0x00, 0x00, //
- 0x00, 0x00, 0x00, 0x00, //
+ 0x00, 0x00, // flags
+ 0x00, 0x00, 0x00, 0x00, // 16 bytes of filler (unnecessary to this emulation
+ 0x00, 0x00, 0x00, 0x00, // but it exists on the uPD720202 hardware)
0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, //
@@ -138,28 +145,31 @@ Bit8u ext_caps[EXT_CAPS_SIZE] = {
0x00, 0x00, 0x00, 0x00 // filler
};
-/* See Section 6.2.6 of xHCI version 1.00
- * Bandwidth of each port (4) of each speed.
- */
-Bit8u port_band_width[4][1 + USB_XHCI_PORTS] = {
-/* rsvd, port1, port2, port3, port4 */
-/* Full Speed */ { 0x00, 0, 0, 90, 90 },
-/* Low Speed */ { 0x00, 0, 0, 90, 90 },
-/* High Speed */ { 0x00, 0, 0, 80, 80 },
-/* Super Speed */ { 0x00, 90, 90, 0, 0 }
-};
-
// builtin configuration handling functions
-
Bit32s usb_xhci_options_parser(const char *context, int num_params, char *params[])
{
+ int max_ports;
+
if (!strcmp(params[0], "usb_xhci")) {
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_USB_XHCI);
for (int i = 1; i < num_params; i++) {
if (!strncmp(params[i], "enabled=", 8)) {
SIM->get_param_bool(BXPN_XHCI_ENABLED)->set(atol(¶ms[i][8]));
+ } else if (!strncmp(params[i], "model=", 6)) {
+ if (!strcmp(¶ms[i][6], "uPD720202"))
+ SIM->get_param_enum(BXPN_XHCI_MODEL)->set(XHCI_HC_uPD720202);
+ else if (!strcmp(¶ms[i][6], "uPD720201"))
+ SIM->get_param_enum(BXPN_XHCI_MODEL)->set(XHCI_HC_uPD720201);
+ else
+ BX_PANIC(("%s: unknown parameter '%s' for usb_xhci: model=", context, ¶ms[i][6]));
+ } else if (!strncmp(params[i], "n_ports=", 8)) {
+ max_ports = (int) strtol(¶ms[i][8], NULL, 10);
+ if ((max_ports >= 2) && (max_ports <= USB_XHCI_PORTS_MAX) && !(max_ports & 1))
+ SIM->get_param_num(BXPN_XHCI_N_PORTS)->set(max_ports);
+ else
+ BX_PANIC(("%s: n_ports= must be at least 2, no more than %d, and an even number.", context, USB_XHCI_PORTS_MAX));
} else if (!strncmp(params[i], "port", 4) || !strncmp(params[i], "options", 7)) {
- if (SIM->parse_usb_port_params(context, params[i], BX_N_USB_XHCI_PORTS, base) < 0) {
+ if (SIM->parse_usb_port_params(context, params[i], USB_XHCI_PORTS_MAX, base) < 0) {
return -1;
}
} else {
@@ -175,7 +185,7 @@ Bit32s usb_xhci_options_parser(const char *context, int num_params, char *params
Bit32s usb_xhci_options_save(FILE *fp)
{
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_USB_XHCI);
- SIM->write_usb_options(fp, BX_N_USB_XHCI_PORTS, base);
+ SIM->write_usb_options(fp, USB_XHCI_PORTS_MAX, base);
return 0;
}
@@ -187,7 +197,7 @@ PLUGIN_ENTRY_FOR_MODULE(usb_xhci)
theUSB_XHCI = new bx_usb_xhci_c();
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUSB_XHCI, BX_PLUGIN_USB_XHCI);
// add new configuration parameter for the config interface
- SIM->init_usb_options("xHCI", "xhci", BX_N_USB_XHCI_PORTS);
+ SIM->init_usb_options("xHCI", "xhci", USB_XHCI_PORTS_MAX);
// register add-on option for bochsrc and command line
SIM->register_addon_option("usb_xhci", usb_xhci_options_parser, usb_xhci_options_save);
} else if (mode == PLUGIN_FINI) {
@@ -219,7 +229,7 @@ bx_usb_xhci_c::~bx_usb_xhci_c()
SIM->unregister_runtime_config_handler(rt_conf_id);
- for (int i=0; iget_param_enum(pname, SIM->get_param(BXPN_USB_XHCI))->set_handler(NULL);
sprintf(pname, "port%d.options", i+1);
@@ -235,11 +245,13 @@ bx_usb_xhci_c::~bx_usb_xhci_c()
void bx_usb_xhci_c::init(void)
{
- unsigned i;
+ unsigned i, j;
char pname[6];
+ Bit8u *p;
bx_list_c *xhci, *port;
bx_param_enum_c *device;
bx_param_string_c *options;
+ struct XHCI_PROTOCOL *protocol;
/* If you wish to set DEBUG=report in the code, instead of
* in the configuration, simply uncomment this line. I use
@@ -264,16 +276,42 @@ void bx_usb_xhci_c::init(void)
DEV_register_pci_handlers(this, &BX_XHCI_THIS devfunc, BX_PLUGIN_USB_XHCI,
"Experimental USB xHCI");
- // initialize readonly registers
- // 0x1912 = vendor (Renesas)
- // 0x0015 = device (0x0014 = uPD720201, 0x0015 = uPD720202)
- // revision number (0x03 = uPD720201, 0x02 = uPD720202)
- init_pci_conf(0x1912, 0x0015, 0x02, 0x0C0330, 0x00, BX_PCI_INTD);
+ // get the desired host controller type
+ BX_XHCI_THIS hub.HostController = SIM->get_param_enum(BXPN_XHCI_MODEL)->get();
+ if (BX_XHCI_THIS hub.HostController == XHCI_HC_uPD720202) {
+ BX_INFO(("xHCI Host Controller: uPD720202"));
+ BX_XHCI_THIS hub.n_ports = USB_XHCI_PORTS; // we hard code to USB_XHCI_PORTS for this host controller (default)
+ // 0x1912 = vendor (Renesas)
+ // 0x0015 = device (0x0015 = uPD720202)
+ // revision number (0x02 = uPD720202)
+ init_pci_conf(0x1912, 0x0015, 0x02, 0x0C0330, 0x00, BX_PCI_INTD);
+ } else if (BX_XHCI_THIS hub.HostController == XHCI_HC_uPD720201) {
+ BX_INFO(("xHCI Host Controller: uPD720201"));
+ BX_XHCI_THIS hub.n_ports = 8; // we hard code to 8 for this host controller
+ // 0x1912 = vendor (Renesas)
+ // 0x0014 = device (0x0014 = uPD720201)
+ // revision number (0x03 = uPD720201)
+ init_pci_conf(0x1912, 0x0014, 0x03, 0x0C0330, 0x00, BX_PCI_INTD);
+ } else {
+ BX_PANIC(("Unknown xHCI Host Controller specified..."));
+ return;
+ }
+
+ // set the number of ports used
+ // if n_ports is not given in the Bochsrc.txt file, the number of ports
+ // is defaulted to the controller type above
+ Bit32s n_ports = SIM->get_param_num(BXPN_XHCI_N_PORTS)->get();
+ if (n_ports > -1) BX_XHCI_THIS hub.n_ports = n_ports;
+ if ((BX_XHCI_THIS hub.n_ports < 2) || (BX_XHCI_THIS hub.n_ports > USB_XHCI_PORTS_MAX) || (BX_XHCI_THIS hub.n_ports & 1)) {
+ BX_PANIC(("n_ports (%d) must be at least 2, not more than 10, and must be an even number.", BX_XHCI_THIS hub.n_ports));
+ return;
+ }
+
BX_XHCI_THIS init_bar_mem(0, IO_SPACE_SIZE, read_handler, write_handler);
// initialize capability registers
BX_XHCI_THIS hub.cap_regs.HcCapLength = (VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | OPS_REGS_OFFSET;
- BX_XHCI_THIS hub.cap_regs.HcSParams1 = (USB_XHCI_PORTS << 24) | (INTERRUPTERS << 8) | MAX_SLOTS;
+ BX_XHCI_THIS hub.cap_regs.HcSParams1 = (BX_XHCI_THIS hub.n_ports << 24) | (INTERRUPTERS << 8) | MAX_SLOTS;
BX_XHCI_THIS hub.cap_regs.HcSParams2 =
// MAX_SCRATCH_PADS 4:0 in bits 31:27 (v1.00) and bits 9:5 in bits 21:25 (v1.01+)
((MAX_SCRATCH_PADS >> 5) << 21) | ((MAX_SCRATCH_PADS & 0x1F) << 27) |
@@ -296,7 +334,7 @@ void bx_usb_xhci_c::init(void)
bx_list_c *usb_rt = (bx_list_c *) SIM->get_param(BXPN_MENU_RUNTIME_USB);
bx_list_c *xhci_rt = new bx_list_c(usb_rt, "xhci", "xHCI Runtime Options");
xhci_rt->set_options(xhci_rt->SHOW_PARENT | xhci_rt->USE_BOX_TITLE);
- for (i=0; iget_param(pname, xhci);
xhci_rt->add(port);
@@ -314,9 +352,71 @@ void bx_usb_xhci_c::init(void)
BX_XHCI_THIS device_change = 0;
BX_XHCI_THIS packets = NULL;
- for (i=0; istart_index = 1; // 1 based starting index
+ protocol->count = BX_XHCI_THIS hub.n_ports / 2;
+ // then the USB2 (second half ports)
+ protocol = (struct XHCI_PROTOCOL *) &ext_caps[PROTOCOL_UBS2_OFFSET];
+ protocol->start_index = (BX_XHCI_THIS hub.n_ports / 2) + 1; // 1 based starting index
+ protocol->count = BX_XHCI_THIS hub.n_ports / 2;
+
+ // done initializing
BX_INFO(("USB xHCI initialized"));
}
@@ -355,7 +455,7 @@ void bx_usb_xhci_c::reset(unsigned type)
// capabilities list:
{ 0x50, 0x01 }, // PCI Power Management
-// { 0x51, 0x70 }, // Pointer to next item (0x00 = no more)
+// { 0x51, 0x70 }, // Pointer to next item (0x70 -> MSI stuff)
{ 0x51, 0x00 }, // Pointer to next item (0x00 = no more)
{ 0x52, 0xC3 }, { 0x53, 0xC9 }, // Capabilities: version = 1.2, Aux Current = 375mA,
@@ -466,7 +566,7 @@ void bx_usb_xhci_c::reset(unsigned type)
void bx_usb_xhci_c::reset_hc()
{
- int i;
+ unsigned int i;
char pname[6];
// Command
@@ -542,7 +642,7 @@ void bx_usb_xhci_c::reset_hc()
BX_XHCI_THIS hub.op_regs.HcConfig.MaxSlotsEn = 0;
// Ports[x]
- for (i=0; ipacket);
@@ -627,7 +727,7 @@ void bx_usb_xhci_c::reset_port(int p)
}
BX_XHCI_THIS hub.usb_port[p].has_been_reset = 0;
- BX_XHCI_THIS hub.usb_port[p].needs_psce = 0;
+ BX_XHCI_THIS hub.usb_port[p].psceg = 0;
}
void bx_usb_xhci_c::reset_port_usb3(int port, const int reset_type)
@@ -643,7 +743,6 @@ void bx_usb_xhci_c::reset_port_usb3(int port, const int reset_type)
BX_XHCI_THIS hub.usb_port[port].device->usb_send_msg(USB_MSG_RESET);
if (BX_XHCI_THIS hub.usb_port[port].is_usb3 && (reset_type == WARM_RESET))
BX_XHCI_THIS hub.usb_port[port].portsc.wrc = 1;
- BX_XHCI_THIS hub.usb_port[port].portsc.prc = 1;
}
} else {
BX_XHCI_THIS hub.usb_port[port].portsc.pls = PLS_RXDETECT;
@@ -763,10 +862,10 @@ bool bx_usb_xhci_c::restore_hc_state(void)
void bx_usb_xhci_c::register_state(void)
{
- unsigned i, j;
+ unsigned i, j, k;
char tmpname[16];
bx_list_c *hub, *port, *reg, *reg_grp, *reg_grp1;
- bx_list_c *list1, *list2, *item, *entries, *entry;
+ bx_list_c *list1, *list2, *list3, *item, *entries, *entry, *entry1;
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "usb_xhci", "USB xHCI State");
hub = new bx_list_c(list, "hub");
@@ -826,11 +925,11 @@ void bx_usb_xhci_c::register_state(void)
BXRS_PARAM_BOOL(reg_grp, HcConfig_cie, BX_XHCI_THIS hub.op_regs.HcConfig.cie);
#endif
- for (i = 0; i < USB_XHCI_PORTS; i++) {
+ for (i = 0; i < BX_XHCI_THIS hub.n_ports; i++) {
sprintf(tmpname, "port%d", i+1);
port = new bx_list_c(hub, tmpname);
BXRS_PARAM_BOOL(port, has_been_reset, BX_XHCI_THIS hub.usb_port[i].has_been_reset);
- BXRS_PARAM_BOOL(port, needs_psce, BX_XHCI_THIS hub.usb_port[i].needs_psce);
+ BXRS_HEX_PARAM_FIELD(port, psceg, BX_XHCI_THIS hub.usb_port[i].psceg);
reg = new bx_list_c(port, "portsc");
BXRS_PARAM_BOOL(reg, wpr, BX_XHCI_THIS hub.usb_port[i].portsc.wpr);
BXRS_PARAM_BOOL(reg, dr, BX_XHCI_THIS hub.usb_port[i].portsc.dr);
@@ -943,6 +1042,13 @@ void bx_usb_xhci_c::register_state(void)
BXRS_PARAM_BOOL(entry, rcs, BX_XHCI_THIS hub.slots[i].ep_context[j].rcs);
BXRS_PARAM_BOOL(entry, retry, BX_XHCI_THIS hub.slots[i].ep_context[j].retry);
BXRS_DEC_PARAM_FIELD(entry, retry_counter, BX_XHCI_THIS hub.slots[i].ep_context[j].retry_counter);
+ for (k = 0; k < MAX_PSA_SIZE_NUM; k++) {
+ sprintf(tmpname, "%d", k);
+ entry1 = new bx_list_c(entry, tmpname);
+ list3 = new bx_list_c(entry1, "ep_stream");
+ BXRS_HEX_PARAM_FIELD(list3, tr_dequeue_pointer, BX_XHCI_THIS hub.slots[i].ep_context[j].stream[k].tr_dequeue_pointer);
+ BXRS_PARAM_BOOL(list3, dcs, BX_XHCI_THIS hub.slots[i].ep_context[j].stream[k].dcs);
+ }
}
}
@@ -973,7 +1079,7 @@ void bx_usb_xhci_c::register_state(void)
void bx_usb_xhci_c::after_restore_state(void)
{
bx_pci_device_c::after_restore_pci_state(NULL);
- for (int j=0; jafter_restore_state();
}
@@ -1173,7 +1279,7 @@ bool bx_usb_xhci_c::read_handler(bx_phy_address addr, unsigned len, void *data,
} else
// Register Port Sets
- if ((offset >= PORT_SET_OFFSET) && (offset < (PORT_SET_OFFSET + (USB_XHCI_PORTS * 16)))) {
+ if ((offset >= PORT_SET_OFFSET) && (offset < (PORT_SET_OFFSET + (BX_XHCI_THIS hub.n_ports * 16)))) {
unsigned port = (((offset - PORT_SET_OFFSET) >> 4) & 0x3F); // calculate port number
if (BX_XHCI_THIS hub.usb_port[port].portsc.pp) {
// the speed field is only valid for USB3 before a port reset. If a reset has not
@@ -1480,7 +1586,7 @@ bool bx_usb_xhci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
BX_XHCI_THIS hub.op_regs.HcCommand.hcrst = 0;
// the controller will send a reset to all USB 3.0 ports,
// enabling the port if a device is attached.
- for (int port=0; port= PORT_SET_OFFSET) && (offset < (PORT_SET_OFFSET + (USB_XHCI_PORTS * 16)))) {
+ if ((offset >= PORT_SET_OFFSET) && (offset < (PORT_SET_OFFSET + (BX_XHCI_THIS hub.n_ports * 16)))) {
unsigned port = (((offset - PORT_SET_OFFSET) >> 4) & 0x3F); // calculate port number
switch (offset & 0x0000000F) {
case 0x00:
@@ -1662,21 +1768,6 @@ bool bx_usb_xhci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
if (BX_XHCI_THIS hub.usb_port[port].portsc.pp == 0) {
// a "has been reset" is false.
BX_XHCI_THIS hub.usb_port[port].has_been_reset = 0;
-
- /* Per section 4.19.3 of the xHCI 1.0 specs, we need to present
- * a "Port Status Change Event".
- * Also, we should only present this event once if any other bits
- * change, only presenting it again when all change bits are written
- * back to zero, and a change bit goes from 0 to 1.
- * (However, I don't know if that is a worry here, at this moment?)
- */
- if (BX_XHCI_THIS hub.usb_port[port].needs_psce == 1) {
- if (BX_XHCI_THIS hub.op_regs.HcStatus.hch == 0) {
- BX_INFO(("Port #%d Status Change Event.", port + 1));
- write_event_TRB(0, ((port + 1) << 24), TRB_SET_COMP_CODE(1), TRB_SET_TYPE(PORT_STATUS_CHANGE), 1);
- }
- BX_XHCI_THIS hub.usb_port[port].needs_psce = 0;
- }
}
BX_XHCI_THIS hub.usb_port[port].portsc.pp = 1;
if (value & (1<<16)) { // LWS
@@ -1896,10 +1987,55 @@ bool bx_usb_xhci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
// (value & 0xFF) = ep (1 = control, 2 = ep1 out, 3 = ep1 in, etc);
int ep = (value & 0xFF);
BX_DEBUG(("Rang Doorbell: slot = %d ep = %d (%s)", doorbell, ep, (ep & 1) ? "IN" : "OUT"));
- if (ep > 31)
+ if (ep > 31) {
BX_ERROR(("Doorbell rang with EP > 31 (ep = %d)", ep));
- else
- process_transfer_ring(doorbell, ep);
+ } else {
+#if MAX_PSA_SIZE > 0
+ // is this endpoint using streams?
+ if (BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.max_pstreams > 0) { // specifying streams
+ // the ep must be a super-speed bulk endpoint
+ if ((BX_XHCI_THIS hub.slots[doorbell].slot_context.speed == XHCI_SPEED_SUPER) && // is a super-speed device
+ ((BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.ep_type == 2) || // is a bulk out
+ (BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.ep_type == 6))) { // or bulk in
+ // if the lsa bit is set, use primary streams only
+ if (BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.lsa == 1) {
+ unsigned primary_id = PSA_PRIMARY_MASK(value, BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.max_pstreams);
+ if ((primary_id > 0) && (primary_id < MAX_PSA_SIZE_NUM) &&
+ (primary_id < PSA_MAX_SIZE_NUM(BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.max_pstreams)) &&
+ BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].stream[primary_id].valid == 1) {
+ BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].stream[primary_id].tr_dequeue_pointer =
+ BX_XHCI_THIS process_transfer_ring(doorbell, ep, BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].stream[primary_id].tr_dequeue_pointer,
+ &BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].stream[primary_id].dcs, primary_id);
+ } else {
+ BX_ERROR(("Stream Context with bad Primary Stream ID (%d)", primary_id));
+ // no TRB is associated, so can't include a TRB address. Does this make the EventTRB invalid?
+ //write_event_TRB(0, 0, TRB_SET_COMP_CODE(INVALID_STREAM_ID), TRB_SET_SLOT(doorbell) | TRB_SET_EP(ep) | TRB_SET_TYPE(TRANS_EVENT), 1);
+ //BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.ep_state = EP_STATE_HALTED; // call a function to do this correctly.
+ }
+ } else {
+ #if NO_SSD_SUPPORT
+ BX_ERROR(("The EP's context lsa bit is zero. Secondary Streams are not supported."));
+ #else
+ // secondary streams are used
+ unsigned primary_id = PSA_PRIMARY_MASK(value, BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.max_pstreams);
+ unsigned secondary_id = PSA_SECONDARY_MASK(value, BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].ep_context.max_pstreams);
+ BX_ERROR(("The EP's context lsa bit is set. Secondary Streams are not yet supported."));
+ // TODO:
+ #endif
+ }
+ } else {
+ BX_ERROR(("EP:MaxPStream > 0 on a non-Bulk SS device endpoint"));
+ }
+ } else {
+#endif
+ // standard trb ring
+ BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].enqueue_pointer =
+ BX_XHCI_THIS process_transfer_ring(doorbell, ep, BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].enqueue_pointer,
+ &BX_XHCI_THIS hub.slots[doorbell].ep_context[ep].rcs, 0);
+#if MAX_PSA_SIZE > 0
+ }
+#endif
+ }
}
} else
BX_ERROR(("register write to unknown offset 0x%08X: 0x%08X%08X (len=%d)", offset, (Bit32u) value_hi, (Bit32u) value, len));
@@ -1907,6 +2043,42 @@ bool bx_usb_xhci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
return 1;
}
+void bx_usb_xhci_c::get_stream_info(struct STREAM_CONTEXT *context, const Bit64u address, const int index) {
+ struct STREAM_CONTEXT stream_context;
+ Bit8u buffer[16];
+
+ // the first stream context is reserved
+ if ((index > 0) && (index < MAX_PSA_SIZE_NUM)) {
+ // read in the stream context
+ DEV_MEM_READ_PHYSICAL((bx_phy_address) address + (index * 16), 16, buffer);
+ copy_stream_from_buffer(&stream_context, buffer);
+ if ((stream_context.sct == 1) && (stream_context.tr_dequeue_pointer != 0)) {
+ context->valid = 1; // mark that this is a valid stream context
+ context->tr_dequeue_pointer = stream_context.tr_dequeue_pointer;
+ context->dcs = stream_context.dcs;
+ context->sct = stream_context.sct;
+ } else {
+#if NO_SSD_SUPPORT
+ context->valid = 0;
+ BX_DEBUG(("Stream Context index %d with SCT != 1 (%d)", index, stream_context.sct));
+#else
+ BX_DEBUG(("Stream Context index %d with SCT == 0 (%d)...Secondary Streams not yet supported.", index));
+#endif
+ }
+ }
+}
+
+void bx_usb_xhci_c::put_stream_info(struct STREAM_CONTEXT *context, const Bit64u address, const int index) {
+ Bit8u buffer[16];
+
+ // the first stream context is reserved
+ if ((index > 0) && (index < MAX_PSA_SIZE_NUM)) {
+ copy_stream_to_buffer(buffer, context);
+ // write the stream context
+ DEV_MEM_WRITE_PHYSICAL((bx_phy_address) address + (index * 16), 16, buffer);
+ }
+}
+
void xhci_event_handler(int event, USBPacket *packet, void *dev, int port)
{
((bx_usb_xhci_c*)dev)->event_handler(event, packet, port);
@@ -1922,7 +2094,16 @@ void bx_usb_xhci_c::event_handler(int event, USBPacket *packet, int port)
p->done = 1;
slot = (p->slot_ep >> 8);
ep = (p->slot_ep & 0xff);
- BX_XHCI_THIS process_transfer_ring(slot, ep);
+ if (BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.max_pstreams > 0) { // specifying streams
+ BX_DEBUG(("Event Handler: USB_EVENT_ASYNC: slot %d, ep %d, stream ID %d", slot, ep, p->packet.strm_pid));
+ BX_XHCI_THIS hub.slots[slot].ep_context[ep].stream[p->packet.strm_pid].tr_dequeue_pointer =
+ BX_XHCI_THIS process_transfer_ring(slot, ep, BX_XHCI_THIS hub.slots[slot].ep_context[ep].stream[p->packet.strm_pid].tr_dequeue_pointer,
+ &BX_XHCI_THIS hub.slots[slot].ep_context[ep].stream[p->packet.strm_pid].dcs, p->packet.strm_pid);
+ } else {
+ BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer =
+ BX_XHCI_THIS process_transfer_ring(slot, ep, BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer,
+ &BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs, 0);
+ }
} else if (event == USB_EVENT_WAKEUP) {
if (BX_XHCI_THIS hub.usb_port[port].portsc.pls != PLS_U3_SUSPENDED) {
return;
@@ -1941,7 +2122,7 @@ void bx_usb_xhci_c::event_handler(int event, USBPacket *packet, int port)
}
// This function checks and processes all enqueued TRB's in the EP's transfer ring
-void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
+Bit64u bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep, Bit64u ring_addr, bool *rcs, const int primary_sid)
{
struct TRB trb;
Bit64u address = 0, org_addr;
@@ -1966,16 +2147,16 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
// if the ep is disabled, return an error event trb.
if ((BX_XHCI_THIS hub.slots[slot].slot_context.slot_state == SLOT_STATE_DISABLED_ENABLED)
|| (BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.ep_state == EP_STATE_DISABLED)) {
- org_addr = BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer;
+ org_addr = ring_addr;
write_event_TRB(0, org_addr, TRB_SET_COMP_CODE(EP_NOT_ENABLED),
TRB_SET_SLOT(slot) | TRB_SET_EP(ep) | TRB_SET_TYPE(TRANS_EVENT), 1);
- return;
+ return ring_addr;
}
// if the ep is in the halted or error state, ignore the doorbell ring.
if ((BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.ep_state == EP_STATE_HALTED)
|| (BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.ep_state == EP_STATE_ERROR))
- return;
+ return ring_addr;
// if the ep_context::type::direction field is not correct for the ep type of this ep, then ignore the doorbell
if (ep >= 2) {
@@ -1983,7 +2164,7 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
int ep_type = (ep & 1) ? EP_DIR_IN : EP_DIR_OUT;
if (endpoint_dir[BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.ep_type] != ep_type) {
BX_ERROR(("Endpoint_context::Endpoint_type::direction is not correct for this endpoint number. Ignoring doorbell ring."));
- return;
+ return ring_addr;
}
}
@@ -1995,14 +2176,11 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
}
// read in the TRB
- read_TRB((bx_phy_address) BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer, &trb);
- BX_DEBUG(("Found TRB: address = 0x" FORMATADDRESS " 0x" FMT_ADDRX64 " 0x%08X 0x%08X %d",
- (bx_phy_address) BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer,
- trb.parameter, trb.status, trb.command, BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs));
- while ((trb.command & 1) == BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs) {
- org_addr = BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer;
+ read_TRB((bx_phy_address) ring_addr, &trb);
+ while ((trb.command & 1) == *rcs) {
+ org_addr = ring_addr;
BX_DEBUG(("Found TRB: address = 0x" FORMATADDRESS " 0x" FMT_ADDRX64 " 0x%08X 0x%08X %d (SPD occurred = %d)",
- (bx_phy_address) org_addr, trb.parameter, trb.status, trb.command, BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs, spd_occurred));
+ (bx_phy_address) org_addr, trb.parameter, trb.status, trb.command, *rcs, spd_occurred));
trb_count++;
// these are used in some/most items.
// If not used, won't hurt to extract bad data.
@@ -2012,7 +2190,7 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
is_transfer_trb = 0; // assume not a transfer
ioc = TRB_IOC(trb.command);
- // if a SPD occurred, we only process the LINK and EVENT TRB's in this TD, until either on of these two or end of TD
+ // if a SPD occurred, we only process the LINK and EVENT TRB's in this TD, until either one of these two or end of TD
if (!spd_occurred ||
(spd_occurred && ((TRB_GET_TYPE(trb.command) == LINK) || (TRB_GET_TYPE(trb.command) == EVENT_DATA)))) {
// is the data in trb.parameter? (Immediate data?)
@@ -2028,16 +2206,16 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
if (ioc)
write_event_TRB(int_target, org_addr, TRB_SET_COMP_CODE(TRB_SUCCESS), TRB_SET_TYPE(LINK), 1);
if (TRB_TOGGLE(trb.command))
- BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs ^= 1;
- BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer = trb.parameter & (Bit64u) ~0xF;
+ (*rcs) ^= 1;
+ ring_addr = trb.parameter & (Bit64u) ~0xF;
BX_DEBUG(("0x" FORMATADDRESS ": Transfer Ring (slot = %d) (ep = %d): LINK TRB: New dq_pointer = 0x" FMT_ADDRX64 " (%d)",
- (bx_phy_address) org_addr, slot, ep, BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer, BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs));
+ (bx_phy_address) org_addr, slot, ep, ring_addr, *rcs));
#if ((VERSION_MAJOR == 0) && (VERSION_MINOR == 0x95))
// https://patchwork.kernel.org/patch/51191/
if (!TRB_CHAIN(trb.command))
BX_DEBUG(("Chain Bit in Link TRB not set."));
#endif
- read_TRB((bx_phy_address) BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer, &trb);
+ read_TRB((bx_phy_address) ring_addr, &trb);
continue;
// Setup Stage TRB
@@ -2115,10 +2293,10 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
// is there a transfer to be done?
if (is_transfer_trb) {
- p = find_async_packet(&BX_XHCI_THIS packets, org_addr);
+ p = find_async_packet(&BX_XHCI_THIS packets, ring_addr);
bool completion = (p != NULL);
if (completion && !p->done) {
- return;
+ return ring_addr;
}
comp_code = TRB_SUCCESS; // assume good trans event
if (completion) {
@@ -2135,6 +2313,7 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
#endif
p->packet.complete_cb = xhci_event_handler;
p->packet.complete_dev = BX_XHCI_THIS_PTR;
+ p->packet.strm_pid = primary_sid;
p->slot_ep = (slot << 8) | ep;
switch (cur_direction) {
case USB_TOKEN_OUT:
@@ -2233,13 +2412,16 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
}
// advance the Dequeue pointer and continue;
- BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer += 16;
- read_TRB((bx_phy_address) BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer, &trb);
+ ring_addr += 16;
+ read_TRB((bx_phy_address) ring_addr, &trb);
}
BX_DEBUG(("Process Transfer Ring: Processed %d TRB's", trb_count));
if (trb_count == 0)
BX_ERROR(("Process Transfer Ring: Doorbell rang, but no TRB's were enqueued in the ring."));
+
+ // return the new address
+ return ring_addr;
}
// This function call starts at the current position in the Command Ring,
@@ -2250,8 +2432,9 @@ void bx_usb_xhci_c::process_transfer_ring(const int slot, const int ep)
void bx_usb_xhci_c::process_command_ring(void)
{
struct TRB trb;
- int i, slot, ep, comp_code = 0, new_addr = 0, bsr = 0, trb_command;
- Bit32u a_flags = 0, d_flags, tmpval1, tmpval2;
+ unsigned i, j;
+ int slot, ep, comp_code = 0, new_addr = 0, bsr = 0, trb_command;
+ Bit32u a_flags, d_flags;
Bit64u org_addr;
Bit8u buffer[CONTEXT_SIZE + (32 * CONTEXT_SIZE)];
struct SLOT_CONTEXT slot_context;
@@ -2285,7 +2468,7 @@ void bx_usb_xhci_c::process_command_ring(void)
// NEC: Get Firmware version
case NEC_TRB_TYPE_GET_FW:
write_event_TRB(0, org_addr, TRB_SET_COMP_CODE(1) | 0x3021, TRB_SET_SLOT(0) | TRB_SET_TYPE(NEC_TRB_TYPE_CMD_COMP), 1);
- BX_INFO(("NEC GET Firmware Version TRB found. Returning 0x3021"));
+ BX_DEBUG(("NEC GET Firmware Version TRB found. Returning 0x3021"));
break;
/* NEC: Get Verification
@@ -2300,7 +2483,7 @@ void bx_usb_xhci_c::process_command_ring(void)
* should be fine.
*/
case NEC_TRB_TYPE_GET_UN:
- BX_INFO(("NEC GET Verification TRB found."));
+ BX_DEBUG(("NEC GET Verification TRB found."));
break;
/* Bochs Dump Controller command:
@@ -2326,8 +2509,8 @@ void bx_usb_xhci_c::process_command_ring(void)
BX_XHCI_THIS hub.slots[i].slot_context.slot_state = SLOT_STATE_DISABLED_ENABLED;
BX_XHCI_THIS hub.slots[i].enabled = 1;
slot = i;
- for (i=1; i<32; i++)
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.ep_state = EP_STATE_DISABLED;
+ for (j=1; j<32; j++)
+ BX_XHCI_THIS hub.slots[i].ep_context[j].ep_context.ep_state = EP_STATE_DISABLED;
comp_code = TRB_SUCCESS;
break;
}
@@ -2361,19 +2544,17 @@ void bx_usb_xhci_c::process_command_ring(void)
if (BX_XHCI_THIS hub.slots[slot].enabled == 1) {
get_dwords((bx_phy_address) trb.parameter, (Bit32u *) buffer, (CONTEXT_SIZE + (CONTEXT_SIZE * 2)) >> 2);
bsr = ((trb.command & (1<<9)) == (1<<9));
- //DEV_MEM_READ_PHYSICAL((bx_phy_address) trb.parameter, 4, (Bit8u *) &tmpval1);
- //DEV_MEM_READ_PHYSICAL((bx_phy_address) trb.parameter + 4, 4, (Bit8u *) &tmpval2);
- tmpval1 = * (Bit32u *) &buffer[0];
- tmpval2 = * (Bit32u *) &buffer[4];
- if (((tmpval1 & 0x03) == 0) && ((tmpval2 & 0x03) == 3)) {
- if ((tmpval1 & ~0x03) || (tmpval2 & ~0x03)) {
+ d_flags = * (Bit32u *) &buffer[0];
+ a_flags = * (Bit32u *) &buffer[4];
+ if (((d_flags & 0x03) == 0) && ((a_flags & 0x03) == 3)) {
+ if ((d_flags & ~0x03) || (a_flags & ~0x03)) {
BX_ERROR(("D2->D31 and A2->A31 must be zero..."));
}
// Use temporary slot and ep context incase there is an error we don't modify the main contexts
copy_slot_from_buffer(&slot_context, &buffer[CONTEXT_SIZE]);
copy_ep_from_buffer(&ep_context, &buffer[CONTEXT_SIZE + CONTEXT_SIZE]);
// check that the Input contexts are valid
- if ((validate_slot_context(&slot_context) == TRB_SUCCESS) &&
+ if ((validate_slot_context(&slot_context, trb_command, slot) == TRB_SUCCESS) &&
(validate_ep_context(&ep_context, trb_command, 0, slot_context.rh_port_num - 1, 1) == TRB_SUCCESS)) {
if (bsr == 1) { // BSR flag set
if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state == SLOT_STATE_DISABLED_ENABLED) {
@@ -2421,109 +2602,60 @@ void bx_usb_xhci_c::process_command_ring(void)
(bx_phy_address) org_addr, bsr, new_addr, slot, comp_code));
break;
- case EVALUATE_CONTEXT: {
- slot = TRB_GET_SLOT(trb.command); // slots are 1 based
- if (BX_XHCI_THIS hub.slots[slot].enabled == 1) {
- get_dwords((bx_phy_address) trb.parameter, (Bit32u *) buffer, (CONTEXT_SIZE + (CONTEXT_SIZE * 32)) >> 2);
- //DEV_MEM_READ_PHYSICAL((bx_phy_address) trb.parameter + 4, 4, (Bit8u *) &a_flags);
- a_flags = * (Bit32u *) &buffer[4];
- // only the Slot context and EP1 (control EP) contexts are evaluated. Section 6.2.3.3
- // If the slot is not addressed or configured, then return error
- // XHCI specs 1.0, page 102 says DEFAULT or higher, while page 321 states higher than DEFAULT!!!
- // (specs 1.1 removed the DEFAULT word and require it to be greater than the DEFAULT state)
+ case EVALUATE_CONTEXT:
+ slot = TRB_GET_SLOT(trb.command); // slots are 1 based
+ if (BX_XHCI_THIS hub.slots[slot].enabled == 1) {
+ get_dwords((bx_phy_address) trb.parameter, (Bit32u *) buffer, (CONTEXT_SIZE + (CONTEXT_SIZE * 32)) >> 2);
+ a_flags = * (Bit32u *) &buffer[4];
+ // only the Slot context and EP0 (control EP) contexts are evaluated. Section 6.2.3.3, p326
+ // If the slot is not addressed or configured, then return error
+ // XHCI specs 1.0, page 102 says DEFAULT or higher, while page 321 states higher than DEFAULT!!!
+ // (specs 1.1 removed the DEFAULT word and require it to be greater than the DEFAULT state)
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
- if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state > SLOT_STATE_DEFAULT) {
+ if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state > SLOT_STATE_DEFAULT) {
#else
- if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state >= SLOT_STATE_DEFAULT) {
+ if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state >= SLOT_STATE_DEFAULT) {
#endif
- comp_code = TRB_SUCCESS; // assume good completion
- if (a_flags & (1<<0)) {
- copy_slot_from_buffer(&slot_context, &buffer[CONTEXT_SIZE]);
- comp_code = validate_slot_context(&slot_context);
- }
- if (comp_code == TRB_SUCCESS) {
- if (a_flags & (1<<1)) {
- copy_ep_from_buffer(&ep_context, &buffer[CONTEXT_SIZE + CONTEXT_SIZE]);
- comp_code = validate_ep_context(&ep_context, trb_command, a_flags, BX_XHCI_THIS hub.slots[slot].slot_context.rh_port_num - 1, 1);
- }
- }
- } else {
- comp_code = CONTEXT_STATE_ERROR;
- BX_DEBUG(("Evaluate Context with an illegal slot state"));
+ comp_code = TRB_SUCCESS; // assume good completion
+ if (a_flags & (1<<0)) {
+ copy_slot_from_buffer(&slot_context, &buffer[CONTEXT_SIZE]);
+ comp_code = validate_slot_context(&slot_context, trb_command, slot);
}
-
- // if all were good, go ahead and update our contexts
if (comp_code == TRB_SUCCESS) {
- for (i=0; i<32; i++) {
- if (a_flags & (1<get_speed()) {
- case USB_SPEED_LOW:
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_packet_size = 8;
- break;
- case USB_SPEED_FULL:
- case USB_SPEED_HIGH:
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_packet_size = 64;
- break;
- case USB_SPEED_SUPER:
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_packet_size = 512;
- break;
- }
- } else
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_packet_size = ep_context.max_packet_size;
-
- switch (ep_context.ep_type) {
- case 4: // control
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.cerr = ep_context.cerr;
- break;
- case 2: // Bulk out
- case 6: // Bulk in
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_burst_size = ep_context.max_burst_size;
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.cerr = ep_context.cerr;
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_pstreams = 0; // we don't support streams yet
- break;
- default: // ISO or interrupt
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_burst_size = ep_context.max_burst_size;
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.mult = ep_context.mult;
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.cerr = 3;
- BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.max_pstreams = 0; // we don't support streams yet
- break;
- }
- // update our internal enqueue pointer
- //BX_XHCI_THIS hub.slots[slot].ep_context[i].enqueue_pointer = ep_context.tr_dequeue_pointer;
- BX_XHCI_THIS hub.slots[slot].ep_context[i].rcs = ep_context.dcs;
-
- // update the DCBAAP slot's ep
- update_ep_context(slot, i);
- }
- }
+ if (a_flags & (1<<1)) {
+ copy_ep_from_buffer(&ep_context, &buffer[CONTEXT_SIZE + CONTEXT_SIZE]);
+ comp_code = validate_ep_context(&ep_context, trb_command, a_flags, BX_XHCI_THIS hub.slots[slot].slot_context.rh_port_num - 1, 1);
}
}
- } else
- comp_code = SLOT_NOT_ENABLED;
+ } else {
+ comp_code = CONTEXT_STATE_ERROR;
+ BX_DEBUG(("Evaluate Context with an illegal slot state"));
+ }
- write_event_TRB(0, org_addr, TRB_SET_COMP_CODE(comp_code), TRB_SET_SLOT(slot) | TRB_SET_TYPE(COMMAND_COMPLETION), 1);
- BX_DEBUG(("0x" FORMATADDRESS ": Command Ring: Evaluate TRB (slot = %d) (a_flags = 0x%08X) (returning %d)",
- (bx_phy_address) org_addr, slot, a_flags, comp_code));
- }
+ // if all were good, go ahead and update our contexts
+ if (comp_code == TRB_SUCCESS) {
+ if (a_flags & (1<<0)) { // slot context
+ // See section 6.2.2.3 for what fields will be updated
+ copy_slot_from_buffer(&slot_context, &buffer[CONTEXT_SIZE]);
+ // only the Interrupter Target and Max Exit Latency are updated by this command
+ BX_XHCI_THIS hub.slots[slot].slot_context.int_target = slot_context.int_target;
+ BX_XHCI_THIS hub.slots[slot].slot_context.max_exit_latency = slot_context.max_exit_latency;
+ update_slot_context(slot); // update the DCBAAP slot
+ }
+ if (a_flags & (1<<1)) { // control ep context
+ // See section 6.2.3.3 for what fields will be updated
+ copy_ep_from_buffer(&ep_context, &buffer[CONTEXT_SIZE + CONTEXT_SIZE]);
+ BX_XHCI_THIS hub.slots[slot].ep_context[1].ep_context.max_packet_size = ep_context.max_packet_size;
+ update_ep_context(slot, 1);
+ }
+ }
+ } else
+ comp_code = SLOT_NOT_ENABLED;
+
+ write_event_TRB(0, org_addr, TRB_SET_COMP_CODE(comp_code), TRB_SET_SLOT(slot) | TRB_SET_TYPE(COMMAND_COMPLETION), 1);
+ BX_DEBUG(("0x" FORMATADDRESS ": Command Ring: Evaluate TRB (slot = %d) (d_flags = 0x%08X) (a_flags = 0x%08X) (returning %d)",
+ (bx_phy_address) org_addr, slot, d_flags, a_flags, comp_code));
+
break;
case CONFIG_EP: {
@@ -2531,28 +2663,33 @@ void bx_usb_xhci_c::process_command_ring(void)
bool dc = TRB_DC(trb.command);
if (BX_XHCI_THIS hub.slots[slot].enabled) {
get_dwords((bx_phy_address) trb.parameter, (Bit32u *) buffer, (CONTEXT_SIZE + (CONTEXT_SIZE * 32)) >> 2);
- //DEV_MEM_READ_PHYSICAL((bx_phy_address) trb.parameter, 4, (Bit8u *) &d_flags);
- //DEV_MEM_READ_PHYSICAL((bx_phy_address) trb.parameter + 4, 4, (Bit8u *) &a_flags);
d_flags = * (Bit32u *) &buffer[0];
a_flags = * (Bit32u *) &buffer[4];
copy_slot_from_buffer(&slot_context, &buffer[CONTEXT_SIZE]); // so we get entry_count
- if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state == SLOT_STATE_CONFIGURED) {
- for (i=2; i<32; i++) {
- if (dc || (d_flags & (1<= SLOT_STATE_ADDRESSED)) {
+ if (dc) {
+ BX_XHCI_THIS hub.slots[slot].slot_context.entries = 1;
+ BX_XHCI_THIS hub.slots[slot].slot_context.slot_state = SLOT_STATE_ADDRESSED;
+ update_slot_context(slot);
+ } else
+
+ if (BX_XHCI_THIS hub.slots[slot].slot_context.slot_state >= SLOT_STATE_ADDRESSED) {
comp_code = TRB_SUCCESS; // assume good completion
+ if ((d_flags & 3) > 0) // the D0 (slot) and D1 (ep0) bits must be cleared
+ comp_code = PARAMETER_ERROR;
+ if ((a_flags & 3) == 1) { // the A0 (slot) bit must be set, and A1 (ep0) must be cleared
+ comp_code = validate_slot_context(&slot_context, trb_command, slot);
+ } else {
+ comp_code = PARAMETER_ERROR;
+ }
+
// Check all the input context entries with an a_flag == 1
for (i=2; i<32; i++) {
if (a_flags & (1< 0) {
+ for (j=1; j EP_STATE_DISABLED) {
+ if (BX_XHCI_THIS hub.slots[slot].ep_context[i].ep_context.ep_state == EP_STATE_RUNNING) {
BX_XHCI_THIS hub.slots[slot].slot_context.slot_state = SLOT_STATE_CONFIGURED;
break;
}
}
- BX_XHCI_THIS hub.slots[slot].slot_context.entries = slot_context.entries; ///////
+
+ // now update the slot
update_slot_context(slot);
}
} else
@@ -2604,11 +2761,9 @@ void bx_usb_xhci_c::process_command_ring(void)
} else
comp_code = SLOT_NOT_ENABLED;
- // TODO: Page 101 (change to context entries)
-
write_event_TRB(0, org_addr, TRB_SET_COMP_CODE(comp_code), TRB_SET_SLOT(slot) | TRB_SET_TYPE(COMMAND_COMPLETION), 1);
- BX_DEBUG(("0x" FORMATADDRESS ": Command Ring: Found Config_EP TRB (slot = %d) (returning %d)",
- (bx_phy_address) org_addr, slot, comp_code));
+ BX_DEBUG(("0x" FORMATADDRESS ": Command Ring: Found Config_EP TRB (slot = %d) (dc = %d) (d_flags = 0x%08X) (a_flags = 0x%08X) (returning %d)",
+ (bx_phy_address) org_addr, slot, dc, d_flags, a_flags, comp_code));
}
break;
@@ -2665,14 +2820,15 @@ void bx_usb_xhci_c::process_command_ring(void)
(bx_phy_address) org_addr, slot, ep, ((trb.command & (1<<23)) == (1<<23))));
break;
- // Get Port Bandwidth (only available on version 0.96 or 0.95.)
+ // Get Port Bandwidth (manditory on versions 0.95, 0.96, and 1.00. Optional on later versions)
case GET_PORT_BAND:
{
unsigned hub_id = TRB_GET_SLOT(trb.command);
unsigned band_speed = ((trb.command & (0x0F << 16)) >> 16) - 1;
+ unsigned offset = band_speed * (((1 + BX_XHCI_THIS hub.n_ports) + 7) & ~7);
if (hub_id == 0) { // root hub
if (band_speed < 4) {
- DEV_MEM_WRITE_PHYSICAL_DMA((bx_phy_address) trb.parameter, 1 + USB_XHCI_PORTS, port_band_width[band_speed]);
+ DEV_MEM_WRITE_PHYSICAL_DMA((bx_phy_address) trb.parameter, 1 + BX_XHCI_THIS hub.n_ports, &BX_XHCI_THIS hub.port_band_width[offset]);
comp_code = TRB_SUCCESS;
} else {
comp_code = TRB_ERROR;
@@ -2769,7 +2925,15 @@ void bx_usb_xhci_c::write_event_TRB(const unsigned interrupter, const Bit64u par
{
// write the TRB
write_TRB((bx_phy_address) BX_XHCI_THIS hub.ring_members.event_rings[interrupter].cur_trb, parameter, status,
- command | (Bit32u)BX_XHCI_THIS hub.ring_members.event_rings[interrupter].rcs); // set the cycle bit
+ command | (Bit32u) BX_XHCI_THIS hub.ring_members.event_rings[interrupter].rcs); // set the cycle bit
+
+ BX_DEBUG(("Write Event TRB: table index: %d, trb index: %d",
+ BX_XHCI_THIS hub.ring_members.event_rings[interrupter].count,
+ BX_XHCI_THIS hub.ring_members.event_rings[interrupter].entrys[BX_XHCI_THIS hub.ring_members.event_rings[interrupter].count].size -
+ BX_XHCI_THIS hub.ring_members.event_rings[interrupter].trb_count));
+ BX_DEBUG(("Write Event TRB: address = 0x" FORMATADDRESS " 0x" FMT_ADDRX64 " 0x%08X 0x%08X (type = %d)",
+ (bx_phy_address) BX_XHCI_THIS hub.ring_members.event_rings[interrupter].cur_trb,
+ parameter, status, command, TRB_GET_TYPE(command)));
// calculate position for next event TRB
BX_XHCI_THIS hub.ring_members.event_rings[interrupter].cur_trb += 16;
@@ -2815,7 +2979,7 @@ void bx_usb_xhci_c::update_slot_context(const int slot)
{
Bit32u buffer[16];
- memset(buffer, 0, 64);
+ memset(buffer, 0, 16 * sizeof(Bit32u));
copy_slot_to_buffer(buffer, slot);
Bit64u slot_addr = (BX_XHCI_THIS hub.op_regs.HcDCBAAP.dcbaap + (slot * sizeof(Bit64u)));
DEV_MEM_READ_PHYSICAL((bx_phy_address) slot_addr, sizeof(Bit64u), (Bit8u *) &slot_addr);
@@ -2825,12 +2989,29 @@ void bx_usb_xhci_c::update_slot_context(const int slot)
void bx_usb_xhci_c::update_ep_context(const int slot, const int ep)
{
Bit32u buffer[16];
+ unsigned i;
- memset(buffer, 0, 64);
+ memset(buffer, 0, 16 * sizeof(Bit32u));
copy_ep_to_buffer(buffer, slot, ep);
Bit64u slot_addr = (BX_XHCI_THIS hub.op_regs.HcDCBAAP.dcbaap + (slot * sizeof(Bit64u)));
DEV_MEM_READ_PHYSICAL((bx_phy_address) slot_addr, sizeof(Bit64u), (Bit8u *) &slot_addr);
put_dwords((bx_phy_address) (slot_addr + (ep * CONTEXT_SIZE)), buffer, CONTEXT_SIZE >> 2);
+
+#if MAX_PSA_SIZE > 0
+ // do we need to update the stream context?
+ if (BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.max_pstreams > 0) {
+ for (i=1; i> 1));
+ BX_INFO((" DCS: %d (%d)", (context[0] & (1 << 0)) >> 0));
+}
+
void bx_usb_xhci_c::copy_slot_from_buffer(struct SLOT_CONTEXT *slot_context, const Bit8u *buffer)
{
Bit32u *buffer32 = (Bit32u *) buffer;
@@ -2983,27 +3172,91 @@ void bx_usb_xhci_c::copy_ep_to_buffer(Bit32u *buffer32, const int slot, const in
BX_XHCI_THIS hub.slots[slot].ep_context[ep].ep_context.average_trb_len;
}
+void bx_usb_xhci_c::copy_stream_from_buffer(struct STREAM_CONTEXT *context, const Bit8u *buffer)
+{
+ Bit32u *buffer32 = (Bit32u *) buffer;
+
+ context->tr_dequeue_pointer = (buffer32[0] & (Bit64u) ~0xF) | ((Bit64u) buffer32[1] << 32);
+ context->dcs = (buffer32[0] & (1<<0));
+ context->sct = ((buffer32[0] & (7<<1)) >> 1);
+#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
+ context->stopped_EDTLA = (buffer32[2] & 0x00FFFFFF);
+#endif
+}
+
+void bx_usb_xhci_c::copy_stream_to_buffer(Bit8u *buffer, const struct STREAM_CONTEXT *context)
+{
+ Bit32u *buffer32 = (Bit32u *) buffer;
+
+ buffer32[0] = (Bit32u) context->tr_dequeue_pointer | (((Bit32u) context->sct) << 1) | (Bit32u) context->dcs;
+ buffer32[1] = (Bit32u)(context->tr_dequeue_pointer >> 32);
+#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
+ buffer[2] = context->stopped_EDTLA;
+#endif
+}
+
// Validate a slot context
+// specs 1.0: sect 6.2.2.2 (p321):
// specs 1.0: sect 6.2.2.3 (p321):
// "A 'valid' Input Slot Context for an Evaluate Context Command requires the Interrupter Target and
// Max Exit Latency fields to be initialized."
-int bx_usb_xhci_c::validate_slot_context(const struct SLOT_CONTEXT *slot_context)
+int bx_usb_xhci_c::validate_slot_context(const struct SLOT_CONTEXT *slot_context, const int trb_command, const int slot)
{
int ret = TRB_SUCCESS;
+ unsigned MaxIntrs;
+ //int speed = -1;
+ //int port_num = slot_context->rh_port_num - 1;
- // valid values for the Interrupt Target are 0 to MaxIntrs-1, inclusive.
- unsigned int MaxIntrs = (BX_XHCI_THIS hub.cap_regs.HcSParams1 & (0x7FF << 8)) >> 8;
- if (slot_context->int_target > MaxIntrs)
- ret = PARAMETER_ERROR;
+ //if ((port_num >= 0) && (BX_XHCI_THIS hub.usb_port[port_num].device != NULL)) {
+ // speed = BX_XHCI_THIS hub.usb_port[port_num].device->get_speed();
+ //} else {
+ // BX_ERROR(("Validate Slot Context: Invalid port_num (%d) sent.", port_num));
+ // return PARAMETER_ERROR;
+ //}
- // all high-speed and lower devices must have a Max Exit Latency value of zero
- // (this may fail, because the speed field may not yet be valid)
- //if ((slot_context->max_exit_latency > 0) && (slot_context->speed < XHCI_SPEED_SUPER))
- // ret = PARAMETER_ERROR;
-
- if (ret != TRB_SUCCESS) {
- BX_ERROR(("Validate Slot Context: int_target = %d (0 -> %d), slot_context->max_exit_latency = %d",
- slot_context->int_target, MaxIntrs, slot_context->max_exit_latency));
+ switch (trb_command) {
+ case ADDRESS_DEVICE:
+ case EVALUATE_CONTEXT:
+ // valid values for the Interrupt Target are 0 to MaxIntrs-1, inclusive.
+ MaxIntrs = (BX_XHCI_THIS hub.cap_regs.HcSParams1 & (0x7FF << 8)) >> 8;
+ if (slot_context->int_target > MaxIntrs)
+ ret = PARAMETER_ERROR;
+
+ // all high-speed and lower devices must have a Max Exit Latency value of zero
+ // (this will fail because 'rh_port_num' hasn't been initialized yet, so 'speed' will be -1)
+ //if ((slot_context->max_exit_latency > 0) && (speed < USB_SPEED_SUPER))
+ // ret = PARAMETER_ERROR;
+
+ if (ret != TRB_SUCCESS) {
+ BX_ERROR(("Validate Slot Context: int_target = %d (0 -> %d), slot_context->max_exit_latency = %d",
+ slot_context->int_target, MaxIntrs, slot_context->max_exit_latency));
+ }
+ break;
+
+ case CONFIG_EP:
+ // The config command needs to check the Context Entries field
+ if (slot_context->entries < BX_XHCI_THIS hub.slots[slot].slot_context.entries)
+ ret = PARAMETER_ERROR;
+
+ // if 'hub' is 1 and device is a high-speed device, then the 'tt think time', 'multi-tt',
+ // and 'number of ports' fields must be initialized.
+ // (this will fail because 'rh_port_num' hasn't been initialized yet, so 'speed' will be -1)
+ //if (slot_context->hub) {
+ // if ((speed == USB_SPEED_HIGH) && (slot_context->ttt == 0) || (slot_context->mtt == 0)) {
+ // ret = PARAMETER_ERROR;
+ // }
+ //} else {
+ // if (slot_context->num_ports != 0) {
+ // ret = PARAMETER_ERROR;
+ // }
+ //}
+
+ if (ret != TRB_SUCCESS) {
+ BX_ERROR(("Validate Slot Context: entry count = %d (%d), hub = %d",
+ slot_context->entries, BX_XHCI_THIS hub.slots[slot].slot_context.entries,
+ slot_context->hub));
+ }
+ break;
}
return ret;
@@ -3021,7 +3274,7 @@ int bx_usb_xhci_c::validate_ep_context(const struct EP_CONTEXT *ep_context, cons
int speed = -1;
// get the device's speed
- if (BX_XHCI_THIS hub.usb_port[port_num].device != NULL) {
+ if ((port_num >= 0) && (BX_XHCI_THIS hub.usb_port[port_num].device != NULL)) {
speed = BX_XHCI_THIS hub.usb_port[port_num].device->get_speed();
} else {
BX_ERROR(("Validate EP Context: Invalid port_num (%d) sent.", port_num));
@@ -3070,7 +3323,7 @@ int bx_usb_xhci_c::validate_ep_context(const struct EP_CONTEXT *ep_context, cons
// 6) all other fields are within the valid range of values.
- // The Max Burst Size, and EP State values shall be cleared to '0'.
+ // The Max Burst Size, and EP State values shall be cleared to 0.
if ((ep_context->max_burst_size != 0) || (ep_context->ep_state != 0))
ret = PARAMETER_ERROR;
}
@@ -3160,7 +3413,8 @@ int bx_usb_xhci_c::send_set_address(const int addr, const int port_num, const in
return ret;
}
-int bx_usb_xhci_c::broadcast_speed(const int slot) {
+int bx_usb_xhci_c::broadcast_speed(const int slot)
+{
int ret = -1;
switch (BX_XHCI_THIS hub.slots[slot].slot_context.speed) {
@@ -3200,19 +3454,63 @@ void bx_usb_xhci_c::xhci_timer_handler(void *this_ptr)
class_ptr->xhci_timer();
}
+// return the status of all the PSCEG (Port Status Change Event Generation) bits
+Bit8u bx_usb_xhci_c::get_psceg(const int port)
+{
+ Bit8u ret;
+
+ ret = (BX_XHCI_THIS hub.usb_port[port].portsc.csc) ? PSCEG_CSC : 0;
+ ret |= (BX_XHCI_THIS hub.usb_port[port].portsc.pec) ? PSCEG_PEC : 0;
+ ret |= (BX_XHCI_THIS hub.usb_port[port].portsc.wrc) ? PSCEG_WRC : 0;
+ ret |= (BX_XHCI_THIS hub.usb_port[port].portsc.occ) ? PSCEG_OCC : 0;
+ ret |= (BX_XHCI_THIS hub.usb_port[port].portsc.prc) ? PSCEG_PRC : 0;
+ ret |= (BX_XHCI_THIS hub.usb_port[port].portsc.plc) ? PSCEG_PLC : 0;
+ ret |= (BX_XHCI_THIS hub.usb_port[port].portsc.cec) ? PSCEG_CEC : 0;
+
+ return ret;
+}
+
void bx_usb_xhci_c::xhci_timer(void)
{
int slot, ep;
+ unsigned int port;
+ Bit8u new_psceg;
if (BX_XHCI_THIS hub.op_regs.HcStatus.hch)
return;
+ /* Per section 4.19.3 of the xHCI 1.0 specs, we need to present
+ * a "Port Status Change Event".
+ * Also, we should only present this event once if any other bits
+ * change, only presenting it again when all change bits are written
+ * back to zero, and a change bit goes from 0 to 1.
+ */
+ for (port=0; port 0) { // specifying streams
+ //
+ // ben: TODO:
+ //
+ BX_ERROR(("Retry on a streamed endpoint."));
+
+ } else {
+ BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer =
+ BX_XHCI_THIS process_transfer_ring(slot, ep, BX_XHCI_THIS hub.slots[slot].ep_context[ep].enqueue_pointer,
+ &BX_XHCI_THIS hub.slots[slot].ep_context[ep].rcs, 0);
+ }
}
}
}
@@ -3228,10 +3526,10 @@ void bx_usb_xhci_c::runtime_config_handler(void *this_ptr)
void bx_usb_xhci_c::runtime_config(void)
{
- int i;
+ unsigned int i;
char pname[6];
- for (i = 0; i < BX_N_USB_XHCI_PORTS; i++) {
+ for (i = 0; i < BX_XHCI_THIS hub.n_ports; i++) {
// device change support
if ((BX_XHCI_THIS device_change & (1 << i)) != 0) {
if (!BX_XHCI_THIS hub.usb_port[i].portsc.ccs) {
@@ -3291,15 +3589,27 @@ bool bx_usb_xhci_c::usb_set_connect_status(Bit8u port, bool connected)
{
const bool ccs_org = BX_XHCI_THIS hub.usb_port[port].portsc.ccs;
const bool ped_org = BX_XHCI_THIS hub.usb_port[port].portsc.ped;
+ int otherportnum = BX_XHCI_THIS hub.paired_portnum[port];
usb_device_c *device = BX_XHCI_THIS hub.usb_port[port].device;
if (device != NULL) {
if (connected) {
+ // make sure the user has not tried to put a device on a paired port number
+ // (this is invalid in all but external USB3 hubs)
+ if (BX_XHCI_THIS hub.usb_port[otherportnum].portsc.ccs) {
+ BX_PANIC(("Port #%d: Paired port number #%d already in use.", port + 1, otherportnum + 1));
+ return 0;
+ }
if ((device->get_speed() == USB_SPEED_SUPER) &&
!BX_XHCI_THIS hub.usb_port[port].is_usb3) {
BX_PANIC(("Super-speed device not supported on USB2 port."));
return 0;
}
+ if ((device->get_speed() < USB_SPEED_SUPER) &&
+ BX_XHCI_THIS hub.usb_port[port].is_usb3) {
+ BX_PANIC(("Non super-speed device not supported on USB3 port."));
+ return 0;
+ }
if (BX_XHCI_THIS hub.usb_port[port].is_usb3) {
if (!device->set_speed(USB_SPEED_SUPER)) {
BX_PANIC(("Only super-speed devices supported on USB3 port."));
@@ -3340,27 +3650,11 @@ bool bx_usb_xhci_c::usb_set_connect_status(Bit8u port, bool connected)
BX_XHCI_THIS hub.usb_port[port].portsc.speed = 0;
remove_device(port);
}
+ // did we change?
if (ccs_org != BX_XHCI_THIS hub.usb_port[port].portsc.ccs)
BX_XHCI_THIS hub.usb_port[port].portsc.csc = 1;
if (ped_org != BX_XHCI_THIS hub.usb_port[port].portsc.ped)
BX_XHCI_THIS hub.usb_port[port].portsc.pec = 1;
-
- /* Per section 4.19.3 of the xHCI 1.0 specs, we need to present
- * a "Port Status Change Event".
- * Also, we should only present this event once if any other bits
- * change, only presenting it again when all change bits are written
- * back to zero, and a change bit goes from 0 to 1.
- * (However, I don't know if that is a worry here, at this moment?)
- */
- if (BX_XHCI_THIS hub.usb_port[port].portsc.pp == 1) {
- if (BX_XHCI_THIS hub.op_regs.HcStatus.hch == 0) {
- BX_INFO(("Port #%d Status Change Event.", port + 1));
- write_event_TRB(0, ((port + 1) << 24), TRB_SET_COMP_CODE(1), TRB_SET_TYPE(PORT_STATUS_CHANGE), 1);
- }
- BX_XHCI_THIS hub.usb_port[port].needs_psce = 0;
- } else {
- BX_XHCI_THIS hub.usb_port[port].needs_psce = 1;
- }
}
return connected;
}
@@ -3373,7 +3667,7 @@ Bit64s bx_usb_xhci_c::usb_param_handler(bx_param_c *param, bool set, Bit64s val)
if (set) {
portnum = atoi((param->get_parent())->get_name()+4) - 1;
bool empty = (val == 0);
- if ((portnum >= 0) && (portnum < USB_XHCI_PORTS)) {
+ if ((portnum >= 0) && (portnum < (int) BX_XHCI_THIS hub.n_ports)) {
if (empty && BX_XHCI_THIS hub.usb_port[portnum].portsc.ccs) {
BX_XHCI_THIS device_change |= (1 << portnum);
} else if (!empty && !BX_XHCI_THIS hub.usb_port[portnum].portsc.ccs) {
@@ -3396,15 +3690,16 @@ bool bx_usb_xhci_c::usb_param_enable_handler(bx_param_c *param, bool en)
if (en && (BX_XHCI_THIS hub.usb_port[portnum].device != NULL)) {
en = 0;
}
+
return en;
}
-void bx_usb_xhci_c::dump_xhci_core(const int slots, const int eps)
+void bx_usb_xhci_c::dump_xhci_core(const unsigned int slots, const unsigned int eps)
{
bx_phy_address addr = BX_XHCI_THIS pci_bar[0].addr;
Bit32u dword;
Bit64u qword, slot_addr;
- int p, i;
+ unsigned int p, i;
Bit8u buffer[4096];
// dump the caps registers
@@ -3441,7 +3736,7 @@ void bx_usb_xhci_c::dump_xhci_core(const int slots, const int eps)
BX_XHCI_THIS read_handler(addr + 0x58, 4, &dword, NULL);
BX_INFO((" CONFIG: 0x%08X", dword));
- for (i=0, p=0; i USB_XHCI_PORTS_MAX) || ((USB_XHCI_PORTS & 1) == 1))
+ #error "USB_XHCI_PORTS must be at least 2 and no more than USB_XHCI_PORTS_MAX and must be an even number."
#endif
// HCSPARAMS2
@@ -83,10 +94,16 @@
#define SEC_DOMAIN_BAND 1 // version 0.96 and below only (MUST BE 1 in v1.00+)
#define STOPPED_EDTLA 0
#define CONT_FRAME_ID 0
-#define MAX_PSA_SIZE 0x05
+#define MAX_PSA_SIZE 0x05 // 2^(5+1) = 63 Primary Streams (first one is reserved)
+ #define MAX_PSA_SIZE_NUM (1 << (MAX_PSA_SIZE + 1))
#define EXT_CAPS_OFFSET 0x500
#define EXT_CAPS_SIZE 144
+// doorbell masks
+#define PSA_MAX_SIZE_NUM(m) (1UL << ((m) + 1))
+#define PSA_PRIMARY_MASK(d, m) (((d) >> 16) & ((1 << ((m) + 1)) - 1))
+#define PSA_SECONDARY_MASK(d, m) (((d) >> 16) >> ((m) + 1))
+
// HCCPARAMS2 (v1.10+)
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
#define U3_ENTRY_CAP 0
@@ -141,16 +158,6 @@
# error "NO_SSD_SUPPORT must be used with in version 1.10 and above"
#endif
-// Each controller supports its own number of ports. We must adhere to that for now.
-// (The Extended Capabilities register set is hardcoded for this as of now.)
-// As long as BX_N_USB_XHCI_PORTS was defined as greater than what we have now, then
-// we are fine.
-// Note: BX_N_USB_XHCI_PORTS should have been defined as twice the amount of ports wanted.
-// ie.: Each physical port (socket) has two defined port register sets. One for USB3, one for USB2
-// Only one port type may be used at a time. Socket0 or Socket1, not both. If Socket0 is used, then
-// Socket1 must be vacant.
-#define BX_N_USB_XHCI_PORTS 4
-
// xHCI speed values
#define XHCI_SPEED_FULL 1
#define XHCI_SPEED_LOW 2
@@ -160,6 +167,26 @@
#define USB2 0
#define USB3 1
+// Port Status Change Bits
+#define PSCEG_CSC (1<<0)
+#define PSCEG_PEC (1<<1)
+#define PSCEG_WRC (1<<2)
+#define PSCEG_OCC (1<<3)
+#define PSCEG_PRC (1<<4)
+#define PSCEG_PLC (1<<5)
+#define PSCEG_CEC (1<<6)
+
+// Extended Capabilities: Protocol
+struct XHCI_PROTOCOL {
+ Bit8u id;
+ Bit8u next;
+ Bit16u version;
+ Bit8u name[4];
+ Bit8u start_index;
+ Bit8u count;
+ Bit16u flags;
+};
+
// our saved ring members
struct RING_MEMBERS {
struct {
@@ -213,6 +240,16 @@ struct EP_CONTEXT {
unsigned average_trb_len;
};
+struct STREAM_CONTEXT {
+ bool valid; // is this context valid
+ Bit64u tr_dequeue_pointer;
+ bool dcs;
+ int sct;
+#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
+ Bit32u stopped_EDTLA;
+#endif
+};
+
struct HC_SLOT_CONTEXT {
bool enabled;
struct SLOT_CONTEXT slot_context;
@@ -224,6 +261,7 @@ struct HC_SLOT_CONTEXT {
bool rcs;
bool retry;
int retry_counter;
+ struct STREAM_CONTEXT stream[MAX_PSA_SIZE_NUM]; // first one is reserved
} ep_context[32]; // first one is ignored by controller.
};
@@ -322,7 +360,14 @@ struct TRB {
Bit32u command;
};
+enum {
+ XHCI_HC_uPD720202, // Renesas/NEC uPD720202 (2 sockets) (default)
+ XHCI_HC_uPD720201 // Renesas/NEC uPD720201 (4 sockets)
+};
+
typedef struct {
+ Bit32u HostController;
+ unsigned int n_ports;
struct XHCI_CAP_REGS {
Bit32u HcCapLength;
@@ -419,7 +464,7 @@ typedef struct {
usb_device_c *device; // device connected to this port
bool is_usb3; // set if usb3 port, cleared if usb2 port.
bool has_been_reset; // set if the port has been reset aftet powered up.
- bool needs_psce; // needs a port status change event on port powered.
+ Bit8u psceg; // current port status change event
struct {
bool wpr; // 1 bit Warm Port Reset = 0b RW or RsvdZ
@@ -483,7 +528,7 @@ typedef struct {
Bit8u hirdd; // 4 bit host initiated resume duration deep
Bit32u RsvdP; // 18 bit reserved and preseved = 0x00000000 RW
} porthlpmc;
- } usb_port[USB_XHCI_PORTS];
+ } usb_port[USB_XHCI_PORTS_MAX];
// Extended Caps Registers
Bit8u extended_caps[EXT_CAPS_SIZE];
@@ -523,6 +568,13 @@ typedef struct {
struct HC_SLOT_CONTEXT slots[MAX_SLOTS]; // first one is ignored by controller.
struct RING_MEMBERS ring_members;
+
+ // filled at runtime with ex: { USB3, USB3, USB2, USB2 };
+ //Bit8u port_speed_allowed[USB_XHCI_PORTS_MAX];
+ // four speeds of: 'reserved' + a port count of bytes rounded up to and 8 byte size (ie: 8, 16, 24, 32 bytes each speed)
+ Bit8u port_band_width[4 * ((1 + USB_XHCI_PORTS_MAX) + 8)]; // + 8 gives us ample room for a boundary of 8-byte entries per speed
+ // the port's paired port num. i.e., with 4 ports, 1 is paired with 3, 2 is paired with 4
+ int paired_portnum[USB_XHCI_PORTS_MAX];
} bx_usb_xhci_t;
// Version 3.0.23.0 of the Renesas uPD720202 driver, even though the card is
@@ -572,11 +624,14 @@ private:
static int broadcast_speed(const int slot);
static int broadcast_packet(USBPacket *p, const int port);
+ static Bit8u get_psceg(const int port);
static void xhci_timer_handler(void *);
void xhci_timer(void);
- static void process_transfer_ring(const int slot, const int ep);
+ static Bit64u process_transfer_ring(const int slot, const int ep, Bit64u ring_addr, bool *rcs, const int primary_sid);
static void process_command_ring(void);
+ static void get_stream_info(struct STREAM_CONTEXT *context, const Bit64u address, const int index);
+ static void put_stream_info(struct STREAM_CONTEXT *context, const Bit64u address, const int index);
static void write_event_TRB(const unsigned interrupter, const Bit64u parameter, const Bit32u status,
const Bit32u command, const bool fire_int);
static Bit32u NEC_verification(const Bit64u parameter);
@@ -587,16 +642,19 @@ private:
static void update_ep_context(const int slot, const int ep);
static void dump_slot_context(const Bit32u *context, const int slot);
static void dump_ep_context(const Bit32u *context, const int slot, const int ep);
+ static void dump_stream_context(const Bit32u *context);
static void copy_slot_from_buffer(struct SLOT_CONTEXT *slot_context, const Bit8u *buffer);
static void copy_ep_from_buffer(struct EP_CONTEXT *ep_context, const Bit8u *buffer);
static void copy_slot_to_buffer(Bit32u *buffer, const int slot);
static void copy_ep_to_buffer(Bit32u *buffer, const int slot, const int ep);
- static int validate_slot_context(const struct SLOT_CONTEXT *slot_context);
+ static void copy_stream_from_buffer(struct STREAM_CONTEXT *context, const Bit8u *buffer);
+ static void copy_stream_to_buffer(Bit8u *buffer, const struct STREAM_CONTEXT *context);
+ static int validate_slot_context(const struct SLOT_CONTEXT *slot_context, const int trb_command, const int slot);
static int validate_ep_context(const struct EP_CONTEXT *ep_context, const int trb_command, const Bit32u a_flags, int port_num, int ep_num);
static int create_unique_address(const int slot);
static int send_set_address(const int addr, const int port_num, const int slot);
- static void dump_xhci_core(const int slots, const int eps);
+ static void dump_xhci_core(const unsigned int slots, const unsigned int eps);
#if BX_USE_USB_XHCI_SMF
static bool read_handler(bx_phy_address addr, unsigned len, void *data, void *param);
diff --git a/bochs/param_names.h b/bochs/param_names.h
index 7fe1f3c57..3ae365f2a 100644
--- a/bochs/param_names.h
+++ b/bochs/param_names.h
@@ -159,6 +159,8 @@
#define BXPN_EHCI_ENABLED "ports.usb.ehci.enabled"
#define BXPN_USB_XHCI "ports.usb.xhci"
#define BXPN_XHCI_ENABLED "ports.usb.xhci.enabled"
+#define BXPN_XHCI_MODEL "ports.usb.xhci.model"
+#define BXPN_XHCI_N_PORTS "ports.usb.xhci.n_ports"
#define BXPN_NE2K "network.ne2k"
#define BXPN_PNIC "network.pcipnic"
#define BXPN_E1000 "network.e1000"