Initial import of libxhcidbg library
authorNico Huber <nico.huber@secunet.com>
Fri, 5 May 2017 16:43:12 +0000 (18:43 +0200)
committerAdrian-Ken Rueegsegger <ken@codelabs.ch>
Fri, 5 May 2017 16:43:12 +0000 (18:43 +0200)
This libxhcidbg library implements support for the xHCI debug capability
as specified in "eXtensible Host Controller Interface for Universal
Serial Bus (xHCI)", revision 1.1, section 7.6.

Signed-off-by: Nico Huber <nico.huber@secunet.com>
27 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.inc [new file with mode: 0644]
README.md [new file with mode: 0644]
TODO.md [new file with mode: 0644]
configs/example [new file with mode: 0644]
misc/xhcidbg-log [new file with mode: 0755]
src/Makefile.inc [new file with mode: 0644]
src/hw-dbc-contexts.adb [new file with mode: 0644]
src/hw-dbc-contexts.ads [new file with mode: 0644]
src/hw-dbc-dma_buffers.ads [new file with mode: 0644]
src/hw-dbc-events.adb [new file with mode: 0644]
src/hw-dbc-events.ads [new file with mode: 0644]
src/hw-dbc-intel_quirk.adb [new file with mode: 0644]
src/hw-dbc-intel_quirk.ads [new file with mode: 0644]
src/hw-dbc-transfer_info.adb [new file with mode: 0644]
src/hw-dbc-transfer_info.ads [new file with mode: 0644]
src/hw-dbc-transfer_rings.adb [new file with mode: 0644]
src/hw-dbc-transfer_rings.ads [new file with mode: 0644]
src/hw-dbc-transfers.adb [new file with mode: 0644]
src/hw-dbc-transfers.ads [new file with mode: 0644]
src/hw-dbc-trbs.adb [new file with mode: 0644]
src/hw-dbc-trbs.ads [new file with mode: 0644]
src/hw-dbc.adb [new file with mode: 0644]
src/hw-dbc.ads [new file with mode: 0644]
src/hw-dbc_config.ads.template [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..69f6818
--- /dev/null
@@ -0,0 +1,5 @@
+/proof-allconfigs/
+/build/
+/dest/
+/*.gpr
+/.config
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..45f4740
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+name := xhcdbc
+
+xhcdbc-deplibs := libhw
+
+libhw-dir ?= ../libhwbase/dest
+include $(libhw-dir)/Makefile
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644 (file)
index 0000000..129621e
--- /dev/null
@@ -0,0 +1 @@
+subdirs-y += src
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..9da849b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,66 @@
+# Libxhcidbg - xHCI Debug Capability library
+
+## Introduction
+
+This **libxhcidbg** library implements support for the xHCI debug
+capability as specified in [*eXtensible Host Controller Interface for
+Universal Serial Bus (xHCI)*, revision 1.1, section 7.6][1]. Libxhcidbg
+advertises the debug capability with vendor-id:device-id ffff:dbc1 and
+device revision 00.01. Descriptor strings are set as follows:
+
+    Product: HW.DbC
+    Manufacturer: secunet
+    SerialNumber: 1
+
+## Usage with Linux host
+
+The `usb-debug` driver of Linux can be used to interact with the xHCI
+debug capability. To work with HW.DbC, one has to specify the
+vendor-id:device-id tuple after driver loading:
+
+    $ modprobe usb-debug
+    $ echo ffff dbc1 >/sys/bus/usb-serial/drivers/debug/new_id
+
+When the USB debugging cable is cold plugged, the host usually turns the
+port into idle state and will not detect a remote debug capability
+coming up. To prevent that, one has to disable automatic power
+management. For an Intel xHCI host the following should do the trick:
+
+    $ echo on | tee /sys/devices/pci0000\:00/0000\:00\:14.0/usb?/power/control
+
+The debug capability should be visible as:
+`/dev/serial/by-id/usb-secunet_HW.DbC_1-if00-port0`
+
+For convenience, the `xhcidbc-log` shell script in the `misc/` directory
+can be used to gather debug output from the device. It uses the
+`inotifywait` utility (part of [*inotify-tools*][2]) to wait for the
+device to be created and then display the output using `cat`. Either
+execute the script as root or make sure the user has the necessary
+access rights, e.g. is member of the `dialout` group.
+
+If there is no visible console output despite the serial device being present,
+the tty setting may be incorrect. Issue the following command to adjust the
+parameters:
+
+    $ stty -F /dev/serial/by-id/usb-secunet_HW.DbC_1-if00-port0 raw -echo
+
+`stty` is part of the [*coreutils* software collection][3].
+
+## BIOS considerations
+
+On hardware platforms that provide USB port-switching, e.g. Intel Haswell
+or Broadwell, the BIOS option *USB 3.0 Mode* **must** be set to
+*Enabled*. Otherwise the USB ports may not be routed to the xHCI
+controller and thus no connected device will be recognized during
+initialization.
+
+## ModemManager considerations
+
+The ModemManager service may interact with the USB serial device created by the
+debugging capability. To avoid any interference, the service should be stopped:
+
+    $ sudo systemctl stop ModemManager.service
+
+[1]: https://www-ssl.intel.com/content/www/us/en/io/universal-serial-bus/extensible-host-controler-interface-usb-xhci.html
+[2]: https://github.com/rvoicilas/inotify-tools/wiki
+[3]: https://www.gnu.org/software/coreutils/
diff --git a/TODO.md b/TODO.md
new file mode 100644 (file)
index 0000000..d3f53e1
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,58 @@
+# General
+
+
+## Documentation
+
+Document the build process for a stand-alone library (utilizing
+*libhwbase*'s build system).
+
+
+## SPARK
+
+* Rework the code to be valid SPARK (no errors from `gnatprove
+  -m check`)
+
+* Prove absence of runtime errors
+
+
+## Configuration options
+
+There are various configuration options throughout the code that
+should be moved into `HW.DbC_Config`.
+
+
+# Current Code
+
+
+## `Receive`
+
+ * Do not unconditionally poll in Receive and respect poll
+   deadline.
+
+
+## `Transfers.Send`
+
+* On `Start_Now`, make sure we start transferring even if we
+  only appended to an already queued transfer. Or make it part
+  of the contract that we are never called with `Start_Now =>
+  True` when an already queued but not started transfer exists
+  (which is currently true).
+
+* Don't acquire a `Transfer_Id` (or release it on failure) if
+  we can't enqueue it. Or make it part of the contract, that
+  the `Transfer_Rings` are always big enough to hold all
+  `Transfer_Id`s (which is currently true).
+
+
+## `Transfer_Info`
+
+* Use better strings in receive statistics
+
+* Use wider numeric types for statistics
+
+
+## `DMA_Buffers`
+
+Currently, DMA addresses are precalculated which results in
+redundancies with constants used throughout the code. We could
+calculate DMA addresses based on compile time constants instead.
diff --git a/configs/example b/configs/example
new file mode 100644 (file)
index 0000000..d23a235
--- /dev/null
@@ -0,0 +1,3 @@
+CONFIG_XHCDBC_DMA_BASE = 16#0020_0000#
+CONFIG_XHCDBC_XHCI_BASE        = 16#e000_0000#
+CONFIG_XHCDBC_XHCI_SIZE        = 16#0001_0000#
diff --git a/misc/xhcidbg-log b/misc/xhcidbg-log
new file mode 100755 (executable)
index 0000000..6f1e574
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+wait_for_file()
+{
+       local dir=$1
+       local file=$2
+       until [ -e "$dir/$file" ]
+       do
+               filename=$(inotifywait -q -e create $dir | cut -d' ' -f 3)
+               if [ "$filename" = "$file" ] ; then
+                       break;
+               fi
+       done
+
+}
+
+command -v inotifywait >/dev/null 2>&1 || \
+{
+       echo >&2 "Command 'inotifywait' missing.";
+       echo >&2 "Try 'apt-get install inotify-tools' to install.";
+       exit 1;
+}
+
+wait_for_file "/dev" "serial"
+wait_for_file "/dev/serial" "by-id"
+
+while true;
+do
+       for f in "/dev/serial/by-id/usb-secunet_HW.DbC"*; do
+               if [ -e "$f" ] ; then
+                       cat "$f"
+                       break 2;
+               else
+                       inotifywait -qe create /dev/serial/by-id 2>&1
+               fi
+               break;
+       done
+done
+
+exit 0
diff --git a/src/Makefile.inc b/src/Makefile.inc
new file mode 100644 (file)
index 0000000..48270d0
--- /dev/null
@@ -0,0 +1,27 @@
+xhcdbc-y += hw-dbc-contexts.adb
+xhcdbc-y += hw-dbc-contexts.ads
+xhcdbc-y += hw-dbc-dma_buffers.ads
+xhcdbc-y += hw-dbc-events.adb
+xhcdbc-y += hw-dbc-events.ads
+xhcdbc-y += hw-dbc-intel_quirk.adb
+xhcdbc-y += hw-dbc-intel_quirk.ads
+xhcdbc-y += hw-dbc-transfer_info.adb
+xhcdbc-y += hw-dbc-transfer_info.ads
+xhcdbc-y += hw-dbc-transfer_rings.adb
+xhcdbc-y += hw-dbc-transfer_rings.ads
+xhcdbc-y += hw-dbc-transfers.adb
+xhcdbc-y += hw-dbc-transfers.ads
+xhcdbc-y += hw-dbc-trbs.adb
+xhcdbc-y += hw-dbc-trbs.ads
+xhcdbc-y += hw-dbc.adb
+xhcdbc-y += hw-dbc.ads
+
+hw-dbc-config-ads := $(subst //,/,$(call src-to-obj,,$(dir)/hw-dbc_config).ads)
+
+$(hw-dbc-config-ads): $(dir)/hw-dbc_config.ads.template $(cnf)
+       printf "    GENERATE   $(patsubst /%,%,$(subst $(obj)/,,$@))\n"
+       sed -e's/<<DMA_BASE>>/$(CONFIG_XHCDBC_DMA_BASE)/' \
+           -e's/<<XHCI_BASE>>/$(CONFIG_XHCDBC_XHCI_BASE)/' \
+           -e's/<<XHCI_SIZE>>/$(CONFIG_XHCDBC_XHCI_SIZE)/' \
+           $< >$@
+xhcdbc-gen-y += $(hw-dbc-config-ads)
diff --git a/src/hw-dbc-contexts.adb b/src/hw-dbc-contexts.adb
new file mode 100644 (file)
index 0000000..4d1bdeb
--- /dev/null
@@ -0,0 +1,109 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with System.Storage_Elements;
+with Ada.Unchecked_Conversion;
+
+with HW.Debug;
+
+package body HW.DbC.Contexts
+with SPARK_Mode => Off
+is
+
+   procedure Clear_DbC_Context (Context : out DbC_Context)
+   is
+
+      --  These constants must not be global, see [Q213-030].
+
+      Null_Info_Context : constant DbC_Info_Context := DbC_Info_Context'
+        (String_0_Address                 => 0,
+         Manufacturer_String_Address      => 0,
+         Product_String_Address           => 0,
+         Serial_Number_String_Address     => 0,
+         String_0_Length                  => 0,
+         Manufacturer_String_Length       => 0,
+         Product_String_Length            => 0,
+         Serial_Number_String_Length      => 0,
+         Reserved_Z                       => 0,
+         Reserved_Z_2                     => 0,
+         Reserved_Z_3                     => 0,
+         Reserved_Z_4                     => 0);
+
+      Null_Endpoint_Context : constant Endpoint_Context := Endpoint_Context'
+        (EP_State              => Disabled,
+         Reserved_Z            => 0,
+         Mult                  => 0,
+         Max_P_Streams         => 0,
+         Linear_Stream_Array   => 0,
+         Interval              => 0,
+         Reserved_Z_2          => 0,
+         Reserved_Z_3          => 0,
+         Error_Count           => 0,
+         EP_Type               => NA,
+         Reserved_Z_4          => 0,
+         Host_Initiate_Disable => 0,
+         Max_Burst_Size        => 0,
+         Max_Packet_Size       => 0,
+         Dequeue_Cycle_State   => 0,
+         Reserved_Z_5          => 0,
+         TR_Dequeue_Pointer_Lo => 0,
+         TR_Dequeue_Pointer_Hi => 0,
+         Average_TRB_Length    => 0,
+         Max_ESIT_Payload      => 0,
+         reserved1             => 0,
+         reserved2             => 0,
+         reserved3             => 0);
+   begin
+      Context := DbC_Context'
+        (DbC_Info => Null_Info_Context,
+         OUT_EP   => Null_Endpoint_Context,
+         IN_EP    => Null_Endpoint_Context);
+   end Clear_DbC_Context;
+
+   ----------------------------------------------------------------------------
+
+   procedure Dump_Endpoint_Context (EC : in Endpoint_Context)
+   is
+      function EP_State_To_Word is new Ada.Unchecked_Conversion (Endpoint_State, Word3);
+      function EP_Type_To_Word is new Ada.Unchecked_Conversion (Endpoint_Type, Word3);
+   begin
+      Debug.Put_Reg64 ("Endpoint Context", Word64 (System.Storage_Elements.To_Integer (EC'Address)));
+      Debug.Put_Reg8  ("EP_State             ", Word8  (EP_State_To_Word (EC.EP_State)));
+      Debug.Put_Reg8  ("RsvdZ                ", Word8  (EC.Reserved_Z));
+      Debug.Put_Reg8  ("Mult                 ", Word8  (EC.Mult));
+      Debug.Put_Reg8  ("Max_P_Streams        ", Word8  (EC.Max_P_Streams));
+      Debug.Put_Reg8  ("Linear_Stream_Array  ", Word8  (EC.Linear_Stream_Array));
+      Debug.Put_Reg8  ("Interval             ",         EC.Interval);
+      Debug.Put_Reg8  ("RsvdZ                ",         EC.Reserved_Z_2);
+      Debug.Put_Reg8  ("RsvdZ                ", Word8  (EC.Reserved_Z_3));
+      Debug.Put_Reg8  ("Error_Count          ", Word8  (EC.Error_Count));
+      Debug.Put_Reg8  ("EP_Type              ", Word8  (EP_Type_To_Word (EC.EP_Type)));
+      Debug.Put_Reg8  ("RsvdZ                ", Word8  (EC.Reserved_Z_4));
+      Debug.Put_Reg8  ("Host_Initiate_Disable", Word8  (EC.Host_Initiate_Disable));
+      Debug.Put_Reg8  ("Max_Burst_Size       ",         EC.Max_Burst_Size);
+      Debug.Put_Reg16 ("Max_Packet_Size      ",         EC.Max_Packet_Size);
+      Debug.Put_Reg8  ("Dequeue_Cycle_State  ", Word8  (EC.Dequeue_Cycle_State));
+      Debug.Put_Reg8  ("RsvdZ                ", Word8  (EC.Reserved_Z_5));
+      Debug.Put_Reg32 ("TR_Dequeue_Pointer_Lo", Word32 (EC.TR_Dequeue_Pointer_Lo));
+      Debug.Put_Reg32 ("TR_Dequeue_Pointer_Hi",         EC.TR_Dequeue_Pointer_Hi);
+      Debug.Put_Reg16 ("Average_TRB_Length   ",         EC.Average_TRB_Length);
+      Debug.Put_Reg16 ("Max_ESIT_Payload     ",         EC.Max_ESIT_Payload);
+      Debug.Put_Reg32 ("RsvdO                ",         EC.reserved1);
+      Debug.Put_Reg32 ("RsvdO                ",         EC.reserved2);
+      Debug.Put_Reg32 ("RsvdO                ",         EC.reserved3);
+   end Dump_Endpoint_Context;
+
+end HW.DbC.Contexts;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-contexts.ads b/src/hw-dbc-contexts.ads
new file mode 100644 (file)
index 0000000..92f8735
--- /dev/null
@@ -0,0 +1,159 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+private package HW.DbC.Contexts
+is
+
+   Context_Size : constant := 64;
+
+   ----------------------------------------------------------------------------
+
+   type DbC_Info_Context is record
+      String_0_Address             : Word64;
+      Manufacturer_String_Address  : Word64;
+      Product_String_Address       : Word64;
+      Serial_Number_String_Address : Word64;
+      String_0_Length              : Word8;
+      Manufacturer_String_Length   : Word8;
+      Product_String_Length        : Word8;
+      Serial_Number_String_Length  : Word8;
+      Reserved_Z                   : Word32;
+      Reserved_Z_2                 : Word64;
+      Reserved_Z_3                 : Word64;
+      Reserved_Z_4                 : Word64;
+   end record
+   with
+      Pack,
+      Volatile,
+      Alignment => Context_Size;
+
+   ----------------------------------------------------------------------------
+
+   type Endpoint_State is (
+      Disabled,
+      Running,
+      Halted,
+      Stopped,
+      Error,
+      Reserved1,
+      Reserved2,
+      Reserved3)
+   with Size => 3;
+   for Endpoint_State use
+     (Disabled  => 0,
+      Running   => 1,
+      Halted    => 2,
+      Stopped   => 3,
+      Error     => 4,
+      Reserved1 => 5,
+      Reserved2 => 6,
+      Reserved3 => 7);
+
+   type Endpoint_Type is (
+      NA,
+      Isoch_O,
+      Bulk_O,
+      Interrupt_O,
+      Control,
+      Isoch_I,
+      Bulk_I,
+      Interrupt_I)
+   with Size => 3;
+   for Endpoint_Type use
+     (NA          => 0,
+      Isoch_O     => 1,
+      Bulk_O      => 2,
+      Interrupt_O => 3,
+      Control     => 4,
+      Isoch_I     => 5,
+      Bulk_I      => 6,
+      Interrupt_I => 7);
+
+   type Endpoint_Context is record
+      EP_State              : Endpoint_State;
+      Reserved_Z            : Word5;
+      Mult                  : Word2;
+      Max_P_Streams         : Word5;
+      Linear_Stream_Array   : Bit;
+      Interval              : Word8;
+      Reserved_Z_2          : Word8;
+      Reserved_Z_3          : Bit;
+      Error_Count           : Word2;
+      EP_Type               : Endpoint_Type;
+      Reserved_Z_4          : Bit;
+      Host_Initiate_Disable : Bit;
+      Max_Burst_Size        : Word8;
+      Max_Packet_Size       : Word16;
+      Dequeue_Cycle_State   : Bit;
+      Reserved_Z_5          : Word3;
+      TR_Dequeue_Pointer_Lo : Word28;
+      TR_Dequeue_Pointer_Hi : Word32;
+      Average_TRB_Length    : Word16;
+      Max_ESIT_Payload      : Word16;
+      reserved1             : Word32;
+      reserved2             : Word32;
+      reserved3             : Word32;
+   end record
+   with
+      Volatile,
+      Alignment => Context_Size;
+   for Endpoint_Context use record
+      EP_State              at 16#00# range 0 .. 2;
+      Reserved_Z            at 16#00# range 3 .. 7;
+      Mult                  at 16#00# range 8 .. 9;
+      Max_P_Streams         at 16#00# range 10 .. 14;
+      Linear_Stream_Array   at 16#00# range 15 .. 15;
+      Interval              at 16#00# range 16 .. 23;
+      Reserved_Z_2          at 16#00# range 24 .. 31;
+      Reserved_Z_3          at 16#04# range 0 .. 0;
+      Error_Count           at 16#04# range 1 .. 2;
+      EP_Type               at 16#04# range 3 .. 5;
+      Reserved_Z_4          at 16#04# range 6 .. 6;
+      Host_Initiate_Disable at 16#04# range 7 .. 7;
+      Max_Burst_Size        at 16#04# range 8 .. 15;
+      Max_Packet_Size       at 16#04# range 16 .. 31;
+      Dequeue_Cycle_State   at 16#08# range 0 .. 0;
+      Reserved_Z_5          at 16#08# range 1 .. 3;
+      TR_Dequeue_Pointer_Lo at 16#08# range 4 .. 31;
+      TR_Dequeue_Pointer_Hi at 16#0c# range 0 .. 31;
+      Average_TRB_Length    at 16#10# range 0 .. 15;
+      Max_ESIT_Payload      at 16#10# range 16 .. 31;
+      reserved1             at 16#14# range 0 .. 31;
+      reserved2             at 16#18# range 0 .. 31;
+      reserved3             at 16#1c# range 0 .. 31;
+   end record;
+
+   ----------------------------------------------------------------------------
+
+   type DbC_Context is record
+      DbC_Info : DbC_Info_Context;
+      OUT_EP   : Endpoint_Context;
+      IN_EP    : Endpoint_Context;
+   end record
+   with
+      Volatile,
+      Pack,
+      Alignment => Context_Size;
+   -- Use size for alignment:
+   -- It always fullfills page crossing requirements.
+
+   -----------------------------------------------------------------------
+
+   procedure Clear_DbC_Context (Context : out DbC_Context);
+
+   procedure Dump_Endpoint_Context (EC : in Endpoint_Context);
+
+end HW.DbC.Contexts;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-dma_buffers.ads b/src/hw-dbc-dma_buffers.ads
new file mode 100644 (file)
index 0000000..14b508e
--- /dev/null
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with HW.DbC_Config;
+
+private package HW.DbC.DMA_Buffers is
+
+   Base : constant := DbC_Config.DMA_BASE;
+
+   --  First 64 * 4KiB are reserved for the transferred data.
+
+   --  <dma_buffer name="Transfer_Rings" size="1024 * 2" align="16" />
+   Transfer_Rings_Base           : constant := Base + 16#0004_0000#;
+
+   --  <dma_buffer name="Event_Ring" size="1024" align="1024" />
+   Event_Ring_Base               : constant := Base + 16#0004_0800#;
+
+   --  <dma_buffer name="DbC_Context" size="64 * 3" align="64" />
+   DbC_Context_Base              : constant := Base + 16#0004_0c00#;
+
+   --  <dma_buffer name="Descriptor_Strings" size="32 * 4" align="1" />
+   Descriptor_Strings_Base       : constant := Base + 16#0004_0cc0#;
+
+   --  <dma_buffer name="Event_Ring_Segment_Table" size="16" align="64" />
+   Event_Ring_Segment_Table_Base : constant := Base + 16#0004_0d40#;
+
+end HW.DbC.DMA_Buffers;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-events.adb b/src/hw-dbc-events.adb
new file mode 100644 (file)
index 0000000..fab3f31
--- /dev/null
@@ -0,0 +1,190 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with System;
+
+with HW.Debug;
+with HW.DbC.DMA_Buffers;
+with HW.DbC.Transfer_Rings;
+with HW.DbC.TRBs;
+
+package body HW.DbC.Events
+with
+   Refined_State =>
+     (State => (Next, CCS),
+      DMA   => (Ring))
+is
+
+   Debug_TD_Events : constant Boolean := False;
+
+   ----------------------------------------------------------------------------
+
+   Ring : TRBs.Transfer_Ring
+   with
+      Async_Readers,
+      Async_Writers,
+      Volatile,
+      Address => System'To_Address (DMA_Buffers.Event_Ring_Base);
+
+   Next : TRBs.Ring_Range;
+   CCS  : Bit;
+
+   ----------------------------------------------------------------------------
+
+   function Last_Physical return Word64
+   is
+      use type Word64;
+      use type TRBs.Ring_Range;
+   begin
+      return DMA_Buffers.Event_Ring_Base + Word64 (Next - 1) * TRBs.TRB_Size;
+   end Last_Physical;
+
+   ----------------------------------------------------------------------------
+
+   procedure Reset_Ring
+   is
+   begin
+      TRBs.Clear_Ring (Ring, 1);
+      Next := 0;
+      CCS := 1;
+   end Reset_Ring;
+
+   ----------------------------------------------------------------------------
+
+   function Ready return Boolean
+   is
+   begin
+      return TRBs.Get_Cycle (Ring (Next)) = CCS;
+   end Ready;
+
+   procedure Update_Dequeue_Ptr
+   is
+      use type Word64;
+      Phys : constant Word64 := Last_Physical;
+   begin
+      Regs.Write (ER_Dequeue_Ptr_Lo, Phys and 16#ffff_ffff#);
+      Regs.Write (ER_Dequeue_Ptr_Hi, Shift_Right (Phys, 32));
+   end Update_Dequeue_Ptr;
+
+   --  advances to the next event
+   --  event ring has no link TRBs, it simply rolls over
+   procedure Advance
+   is
+      use type TRBs.Ring_Range;
+   begin
+      Next := Next + 1;
+      if Next = 0 then
+         CCS := 1 - CCS;
+
+         --  Update event dequeue pointer regularly.
+         Update_Dequeue_Ptr;
+      end if;
+   end Advance;
+
+   ----------------------------------------------------------------------------
+
+   procedure Handle_Transfer_Event
+   is
+      use type Word8;
+      ID : constant Word8 := TRBs.Get_Slot_ID (Ring (Next));
+      EP : constant Natural := TRBs.Get_Endpoint_ID (Ring (Next));
+      Pointer : constant Word64 := TRBs.Get_Parameter (Ring (Next));
+      Length : constant Natural := TRBs.Get_Event_Length (Ring (Next));
+      CC : constant TRBs.Completion_Code :=
+         TRBs.Get_Completion_Code (Ring (Next));
+   begin
+      if ID = 1 and EP in Endpoint_Range then
+         pragma Debug (Debug_TD_Events, Debug.Put ("TD finished; Length: "));
+         pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (Length)));
+         pragma Debug (Debug_TD_Events, Debug.Put ("; Status: "));
+         pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (CC)));
+         pragma Debug (Debug_TD_Events, Debug.Put_Reg64 ("; Pointer", Pointer));
+         Transfer_Rings.Dequeue
+           (EP                => EP,
+            Pointer           => Pointer,
+            Status            => TRBs.CC_To_Usb_Error (CC),
+            Remaining_Length  => Length);
+      else
+         pragma Debug (Debug.Put
+           ("WARNING: Spurious transfer event for ID "));
+         pragma Debug (Debug.Put_Int32 (Int32 (ID)));
+         pragma Debug (Debug.Put (", EP "));
+         pragma Debug (Debug.Put_Int32 (Int32 (EP)));
+         pragma Debug (Debug.Put_Line (":"));
+         pragma Debug (Debug.Put_Reg64 ("  Pointer", Pointer));
+         pragma Debug (Debug.Put_Reg32 ("       TL", Word32 (Length)));
+         pragma Debug (Debug.Put_Reg8 ("       CC", Word8 (CC)));
+         null;
+      end if;
+      Advance;
+   end Handle_Transfer_Event;
+
+   procedure Handle_Host_Controller_Event
+   is
+      CC : TRBs.Completion_Code;
+   begin
+      CC := TRBs.Get_Completion_Code (Ring (Next));
+      case CC is
+      when TRBs.Event_Ring_Full_Error =>
+         pragma Debug (Debug.Put_Line ("Event ring full!"));
+         --  If we get here, we have processed the whole queue:
+         --  xHC pushes this event, when it sees the ring full,
+         --  full of other events.
+         --  IMO it's save and necessary to update the dequeue
+         --  pointer here.
+         Advance;
+         Update_Dequeue_Ptr;
+      when others =>
+         pragma Debug (Debug.Put_Reg8
+           ("WARNING: Spurious host controller event", Word8 (CC)));
+         Advance;
+      end case;
+   end Handle_Host_Controller_Event;
+
+   procedure Handle_Event
+   is
+      TRB_Type : TRBs.TRB_Types;
+   begin
+      TRB_Type := TRBs.Get_Type (Ring (Next));
+      case TRB_Type is
+         when TRBs.Transfer_Event =>
+            Handle_Transfer_Event;
+         when TRBs.Port_Status_Change_Event =>
+            pragma Debug (Debug.Put_Line ("Port status change event."));
+            Advance;
+         when TRBs.Host_Controller_Event =>
+            Handle_Host_Controller_Event;
+         when others =>
+            pragma Debug (Debug.Put_Reg8
+              ("WARNING: Spurious event", Word8 (TRB_Type)));
+            Advance;
+      end case;
+   end Handle_Event;
+
+   procedure Handle_Events
+   is
+      Update_Ptr : Boolean;
+   begin
+      Update_Ptr := Ready;
+      while Ready loop
+         Handle_Event;
+      end loop;
+      if Update_Ptr then
+         Update_Dequeue_Ptr;
+      end if;
+   end Handle_Events;
+
+end HW.DbC.Events;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-events.ads b/src/hw-dbc-events.ads
new file mode 100644 (file)
index 0000000..7a6a9ac
--- /dev/null
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+private package HW.DbC.Events
+with
+   Abstract_State => ((State with Part_Of  => HW.DbC.State),
+                      (DMA   with Part_Of  => HW.DbC.DMA,
+                                  External => (Async_Readers, Async_Writers)))
+is
+
+   type ERST_Entry is record
+      Segment_Base : Word64;
+      Segment_Size : Word32;
+      Reserved_Z   : Word32;
+   end record
+   with
+      Pack,
+      Volatile,
+      Alignment => 64;
+
+   ----------------------------------------------------------------------------
+
+   procedure Reset_Ring;
+
+   procedure Handle_Events;
+
+end HW.DbC.Events;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-intel_quirk.adb b/src/hw-dbc-intel_quirk.adb
new file mode 100644 (file)
index 0000000..a76cae5
--- /dev/null
@@ -0,0 +1,56 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+package body HW.DbC.Intel_Quirk
+is
+
+   procedure Reset_Port
+   is
+      use type Word8;
+      Success : Boolean;
+      Temp8, Op_Offset : Word8;
+      Port_Count, Port_Offset : Natural;
+   begin
+      Cap_Regs.Read (Op_Offset, Capability_Registers_Length);
+      xCap_Regs.Byte_Offset := 0;
+      loop
+         Find_Next_xCap (2, Success);
+         exit when not Success;
+
+         Sup_Protocol_Regs.Byte_Offset := xCap_Regs.Byte_Offset;
+         Sup_Protocol_Regs.Read (Temp8, Revision_Major);
+         if Temp8 = 16#03# then
+            Sup_Protocol_Regs.Read (Temp8, Compatible_Port_Offset);
+            if Temp8 /= 16#00# then
+               Port_Offset := Natural (Temp8 - 1);
+
+               Sup_Protocol_Regs.Read (Temp8, Compatible_Port_Count);
+               Port_Count := Natural (Temp8);
+
+               for Idx in Port_Offset .. Port_Offset + Port_Count - 1 loop
+                  Port_Regs.Byte_Offset :=
+                     Natural (Op_Offset) + 16#400# + Idx * 16#10#;
+                  Port_Regs.Read (Temp8, Current_Connect_Status);
+                  if Temp8 = 16#00# then
+                     Port_Regs.Write (Port_Reset, Word8'(1));
+                  end if;
+               end loop;
+            end if;
+         end if;
+      end loop;
+   end Reset_Port;
+
+end HW.DbC.Intel_Quirk;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-intel_quirk.ads b/src/hw-dbc-intel_quirk.ads
new file mode 100644 (file)
index 0000000..907d7af
--- /dev/null
@@ -0,0 +1,50 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with HW.Sub_Regs;
+with HW.MMIO_Regs;
+pragma Elaborate_All (HW.MMIO_Regs);
+
+private package HW.DbC.Intel_Quirk
+is
+
+   procedure Reset_Port;
+
+private
+
+   type Sup_Protocol_Registers is
+     (Revision_Major,
+      Compatible_Port_Offset,
+      Compatible_Port_Count);
+   package Sup_Protocol_S_Regs is new Sub_Regs (Sup_Protocol_Registers);
+   Sup_Protocol_Reg_Descs : constant Sup_Protocol_S_Regs.Array_T :=
+     (Revision_Major          => (16#00#, 31, 24),
+      Compatible_Port_Offset  => (16#08#,  7,  0),
+      Compatible_Port_Count   => (16#08#, 15,  8));
+   package Sup_Protocol_Regs
+   is
+      new MMIO_Regs (DbC.MMIO, 0, Sup_Protocol_S_Regs, Sup_Protocol_Reg_Descs);
+
+   type Port_Registers is
+     (Current_Connect_Status,
+      Port_Reset);
+   package Port_S_Regs is new Sub_Regs (Port_Registers);
+   Port_Reg_Descs : constant Port_S_Regs.Array_T :=
+     (Current_Connect_Status  => (16#00#, 0, 0),
+      Port_Reset              => (16#00#, 4, 4));
+   package Port_Regs is new MMIO_Regs (DbC.MMIO, 0, Port_S_Regs, Port_Reg_Descs);
+
+end HW.DbC.Intel_Quirk;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-transfer_info.adb b/src/hw-dbc-transfer_info.adb
new file mode 100644 (file)
index 0000000..44165d9
--- /dev/null
@@ -0,0 +1,336 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with HW.Debug;
+with HW.DbC.DMA_Buffers;
+
+package body HW.DbC.Transfer_Info
+with
+   Refined_State => (State => (Start_Counter, Stats, Xfers))
+is
+
+   Start_Counter : Word64 := 0;  -- Should not overflow
+
+   type Xfer_Info is record
+      Endpoint : Endpoint_Range;
+      Token    : Word64;
+      Offset   : Natural;
+      Length   : Natural;
+      Status   : Error;
+      Started  : Boolean;
+      Finished : Boolean;
+   end record;
+
+   type Xfer_Array is array (Transfer_Id) of Xfer_Info;
+
+   Xfers : Xfer_Array := Xfer_Array'
+     (Transfer_Id => Xfer_Info'
+        (Endpoint => Endpoint_Range'First,
+         Token    => 16#0000_0000_0000_0000#,
+         Offset   => 0,
+         Length   => 0,
+         Status   => Error'First,
+         Started  => False,
+         Finished => False));
+
+   type Stats_Info is record
+      Enqueued    : Natural;
+      Transfered  : Natural;
+      Lost        : Natural;
+   end record;
+   type Stats_Array is array (Endpoint_Range) of Stats_Info;
+   Stats : Stats_Array := (Endpoint_Range => (others => 0));
+
+   ----------------------------------------------------------------------------
+
+   function Get_Endpoint (Id : Transfer_Id) return Endpoint_Range
+   is
+   begin
+      return Xfers (Id).Endpoint;
+   end Get_Endpoint;
+
+   function Get_Offset (Id : Transfer_Id) return Natural
+   is
+   begin
+      return Xfers (Id).Offset;
+   end Get_Offset;
+
+   procedure Set_Offset (Id : Transfer_Id; Offset : Natural)
+   is
+   begin
+      Xfers (Id).Offset := Offset;
+   end Set_Offset;
+
+   function Get_Length (Id : Transfer_Id) return Natural
+   is
+   begin
+      return Xfers (Id).Length;
+   end Get_Length;
+
+   function Get_Status (Id : Transfer_Id) return Error
+   is
+   begin
+      return Xfers (Id).Status;
+   end Get_Status;
+
+   function Physical (Id : Transfer_Id) return Word64
+   is
+      use type Word64;
+   begin
+      return DMA_Buffers.Base + Word64 (Id) * Max_Bulk_Size;
+   end Physical;
+
+   ----------------------------------------------------------------------------
+
+   procedure Start
+     (Endpoint : in     Endpoint_Range;
+      Length   : in     Natural;
+      Id       :    out Transfer_Id;
+      Success  :    out Boolean)
+   is
+      use type Word64;
+   begin
+      Success := False;
+      Id := Transfer_Id'First;
+      for I in Transfer_Id loop
+         if not Xfers (I).Started then
+            Xfers (I) :=
+              (Endpoint => Endpoint,
+               Token    => Start_Counter,
+               Offset   => 0,
+               Length   => Length,
+               Status   => Error'First,
+               Started  => True,
+               Finished => False);
+            Stats (Endpoint).Enqueued := Stats (Endpoint).Enqueued + Length;
+            Start_Counter := Start_Counter + 1;
+            Success := True;
+            Id := I;
+         end if;
+         exit when Success;
+      end loop;
+   end Start;
+
+   procedure Restart
+     (Id       : Transfer_Id;
+      Length   : Natural)
+   is
+      use type Word64;
+
+      Endpoint : constant Endpoint_Range := Xfers (Id).Endpoint;
+   begin
+      Xfers (Id) :=
+        (Endpoint => Endpoint,
+         Token    => Start_Counter,
+         Offset   => 0,
+         Length   => Length,
+         Status   => Error'First,
+         Started  => True,
+         Finished => False);
+      Start_Counter := Start_Counter + 1;
+      Stats (Endpoint).Enqueued := Stats (Endpoint).Enqueued + Length;
+   end Restart;
+
+   procedure Append
+     (Endpoint : in     Endpoint_Range;
+      Length   : in out Natural;
+      Offset   :    out Natural;
+      Id       :    out Transfer_Id)
+   is
+      use type Word64;
+      Success : Boolean;
+      Max_Counter : Word64;
+   begin
+      Success := False;
+      Max_Counter := 0;
+      Id := Transfer_Id'First;
+      for I in Transfer_Id loop
+         if Xfers (I).Started and
+            Xfers (I).Endpoint = Endpoint and
+            Xfers (I).Token >= Max_Counter
+         then
+            Max_Counter := Xfers (I).Token;
+            Success := True;
+            Id := I;
+         end if;
+      end loop;
+      if Success then
+         Length := Natural'Min (Length, Max_Bulk_Size - Xfers (Id).Length);
+         Offset := Xfers (Id).Length;
+         Xfers (Id).Length := Xfers (Id).Length + Length;
+         Stats (Endpoint).Enqueued := Stats (Endpoint).Enqueued + Length;
+      else
+         Length := 0;
+         Offset := 0;
+      end if;
+   end Append;
+
+   procedure Finish
+     (DMA_Physical      : Word64;
+      Remaining_Length  : Natural;
+      Status            : Error)
+   is
+      use type Word64;
+      Id : Transfer_Id;
+   begin
+      if Physical (Transfer_Id'First) <= DMA_Physical and
+         DMA_Physical <= Physical (Transfer_Id'Last) and
+         (DMA_Physical - Physical (Transfer_Id'First)) mod Max_Bulk_Size = 0
+      then
+         Id := Transfer_Id
+           ((DMA_Physical - Physical (Transfer_Id'First)) / Max_Bulk_Size);
+         if Xfers (Id).Started then
+            Xfers (Id).Finished := True;
+            Xfers (Id).Status := Status;
+            Xfers (Id).Offset := 0;
+            if Remaining_Length <= Xfers (Id).Length then
+               Xfers (Id).Length := Xfers (Id).Length - Remaining_Length;
+               Stats (Xfers (Id).Endpoint).Transfered :=
+                  Stats (Xfers (Id).Endpoint).Transfered + Xfers (Id).Length;
+               Stats (Xfers (Id).Endpoint).Lost :=
+                  Stats (Xfers (Id).Endpoint).Lost + Remaining_Length;
+            else
+               Stats (Xfers (Id).Endpoint).Lost :=
+                  Stats (Xfers (Id).Endpoint).Lost + Xfers (Id).Length;
+               Xfers (Id).Length := 0;
+               Xfers (Id).Status := Controller_Error;
+            end if;
+         else
+            pragma Debug (Debug.Put_Reg8
+              ("Spurious finish for transfer", Word8 (Id)));
+            null;
+         end if;
+      else
+         pragma Debug (Debug.Put_Reg64
+           ("WARNING: Invalid DMA pointer", DMA_Physical));
+         pragma Debug (Debug.Put_Reg64
+           ("           first DMA address", Physical (Transfer_Id'First)));
+         pragma Debug (Debug.Put_Reg64
+           ("            last DMA address", Physical (Transfer_Id'Last)));
+         null;
+      end if;
+   end Finish;
+
+   procedure Reset (Id : Transfer_Id)
+   is
+   begin
+      Xfers (Id).Started := False;
+   end Reset;
+
+   ----------------------------------------------------------------------------
+
+   generic
+      with function Check (Id : Transfer_Id) return Boolean;
+   procedure Walk
+     (Minimum_Ctr : in out Word64;
+      Id          :    out Transfer_Id;
+      Success     :    out Boolean);
+
+   procedure Walk
+     (Minimum_Ctr : in out Word64;
+      Id          :    out Transfer_Id;
+      Success     :    out Boolean)
+   is
+      use type Word64;
+   begin
+      Success := False;
+      Id := Transfer_Id'First;
+      for I in Transfer_Id loop
+         if Check (I) and Xfers (I).Token >= Minimum_Ctr then
+            if (Success and Xfers (I).Token < Xfers (Id).Token) or
+               not Success
+            then
+               Id := I;
+               Success := True;
+            end if;
+         end if;
+      end loop;
+      if Success then
+         Minimum_Ctr := Xfers (Id).Token + 1;
+      end if;
+   end Walk;
+
+   function Check_Started (Id : Transfer_Id) return Boolean
+   is
+   begin
+      return Xfers (Id).Started and not Xfers (Id).Finished;
+   end Check_Started;
+   procedure Walk_Started_Inst is new Walk (Check_Started);
+   procedure Walk_Started
+     (Minimum_Ctr : in out Word64;
+      Id          :    out Transfer_Id;
+      Success     :    out Boolean)
+      renames Walk_Started_Inst;
+
+   procedure Walk_Finished
+     (Endpoint    : in     Endpoint_Range;
+      Minimum_Ctr : in out Word64;
+      Id          :    out Transfer_Id;
+      Success     :    out Boolean)
+   is
+      generic
+         Endpoint : Endpoint_Range;
+      function Check_Finished (Id : Transfer_Id) return Boolean;
+      function Check_Finished (Id : Transfer_Id) return Boolean
+      is
+      begin
+         return
+            Xfers (Id).Endpoint = Endpoint and
+            Xfers (Id).Started and
+            Xfers (Id).Finished;
+      end Check_Finished;
+      function Check_Finished_Inst is new Check_Finished (Endpoint);
+      procedure Walk_Finished_Inst is new Walk (Check_Finished_Inst);
+   begin
+      Walk_Finished_Inst (Minimum_Ctr, Id, Success);
+   end Walk_Finished;
+
+   ----------------------------------------------------------------------------
+
+   procedure Dump_Stats
+   is
+   begin
+      Debug.Put_Line ("DbC statistics:");
+      for EP in Endpoint_Range loop
+         Debug.Put ("  Endpoint #");
+         Debug.Put_Int8 (Int8 (EP));
+         Debug.New_Line;
+         Debug.Put ("      enqueued: ");
+         Debug.Put_Word32 (Word32 (Stats (EP).Enqueued));
+         Debug.Put (" (");
+         Debug.Put_Int32 (Int32 (Stats (EP).Enqueued));
+         Debug.Put_Line (")");
+         Debug.Put ("    transfered: ");
+         Debug.Put_Word32 (Word32 (Stats (EP).Transfered));
+         Debug.Put (" (");
+         Debug.Put_Int32 (Int32 (Stats (EP).Transfered));
+         Debug.Put_Line (")");
+         Debug.Put ("          lost: ");
+         Debug.Put_Word32 (Word32 (Stats (EP).Lost));
+         Debug.Put (" (");
+         Debug.Put_Int32 (Int32 (Stats (EP).Lost));
+         Debug.Put_Line (")");
+         Debug.Put ("      in queue: ");
+         Debug.Put_Word32 (Word32
+           (Stats (EP).Enqueued - Stats (EP).Transfered - Stats (EP).Lost));
+         Debug.Put (" (");
+         Debug.Put_Int32 (Int32
+           (Stats (EP).Enqueued - Stats (EP).Transfered - Stats (EP).Lost));
+         Debug.Put_Line (")");
+      end loop;
+   end Dump_Stats;
+
+end HW.DbC.Transfer_Info;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-transfer_info.ads b/src/hw-dbc-transfer_info.ads
new file mode 100644 (file)
index 0000000..51605b2
--- /dev/null
@@ -0,0 +1,76 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+private package HW.DbC.Transfer_Info
+with
+   Abstract_State => (State with Part_Of  => HW.DbC.State)
+is
+
+   procedure Start
+     (Endpoint : in     Endpoint_Range;
+      Length   : in     Natural;
+      Id       :    out Transfer_Id;
+      Success  :    out Boolean);
+
+   procedure Restart
+     (Id       : Transfer_Id;
+      Length   : Natural);
+
+   procedure Append
+     (Endpoint : in     Endpoint_Range;
+      Length   : in out Natural;
+      Offset   :    out Natural;
+      Id       :    out Transfer_Id);
+
+   procedure Finish
+     (DMA_Physical      : Word64;
+      Remaining_Length  : Natural;
+      Status            : Error);
+
+   procedure Reset (Id : Transfer_Id);
+
+   ----------------------------------------------------------------------------
+
+   procedure Walk_Started
+     (Minimum_Ctr : in out Word64;
+      Id          :    out Transfer_Id;
+      Success     :    out Boolean);
+
+   procedure Walk_Finished
+     (Endpoint    : in     Endpoint_Range;
+      Minimum_Ctr : in out Word64;
+      Id          :    out Transfer_Id;
+      Success     :    out Boolean);
+
+   ----------------------------------------------------------------------------
+
+   function Get_Endpoint (Id : Transfer_Id) return Endpoint_Range;
+
+   function Get_Offset (Id : Transfer_Id) return Natural;
+
+   procedure Set_Offset (Id : Transfer_Id; Offset : Natural);
+
+   function Get_Length (Id : Transfer_Id) return Natural;
+
+   function Get_Status (Id : Transfer_Id) return Error;
+
+   function Physical (Id : Transfer_Id) return Word64;
+
+   ----------------------------------------------------------------------------
+
+   procedure Dump_Stats;
+
+end HW.DbC.Transfer_Info;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-transfer_rings.adb b/src/hw-dbc-transfer_rings.adb
new file mode 100644 (file)
index 0000000..f5847d9
--- /dev/null
@@ -0,0 +1,271 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with System;
+with System.Storage_Elements;
+
+with HW.Debug;
+with HW.DbC.DMA_Buffers;
+with HW.DbC.Transfer_Info;
+with HW.DbC.TRBs;
+
+package body HW.DbC.Transfer_Rings
+with
+   Refined_State =>
+     (State => (PCS, Pointers),
+      DMA   => (Rings))
+is
+
+   type Transfer_Ring_Array is array (Endpoint_Range) of TRBs.Transfer_Ring
+   with
+      Pack;
+   Rings : Transfer_Ring_Array
+   with
+      Volatile,
+      Async_Readers,
+      Async_Writers,
+      Address => System'To_Address (DMA_Buffers.Transfer_Rings_Base);
+
+   type PCS_Array is array (Endpoint_Range) of Bit;
+   PCS : PCS_Array;
+
+   type Transfer_Pointers is record
+      Enqueue  : TRBs.Ring_Range;
+      Dequeue  : TRBs.Ring_Range;
+      Full     : Boolean;
+      Toggle   : TRBs.Ring_Range;
+      Overrun  : Boolean;
+   end record;
+   type Transfer_Pointers_Array is array (Endpoint_Range) of Transfer_Pointers;
+   Pointers : Transfer_Pointers_Array;
+
+   --  Returns the physical address of the transfer ring specified by given EP.
+   function Get_Ring_Address (EP : Endpoint_Range) return Word64;
+
+   ----------------------------------------------------------------------------
+
+   function Physical (EP : Endpoint_Range) return Word64
+   is
+   begin
+      return
+         DMA_Buffers.Transfer_Rings_Base + (Word64 (EP) - 2) * TRBs.Ring_Size;
+   end Physical;
+
+   function Full (EP : Endpoint_Range) return Boolean
+   is
+   begin
+      return Pointers (EP).Full;
+   end Full;
+
+   function Last_Started (EP : Endpoint_Range) return Boolean
+   is
+      use type TRBs.Ring_Range;
+   begin
+      return
+         not Pointers (EP).Overrun and
+         Pointers (EP).Toggle = Pointers (EP).Enqueue;
+   end Last_Started;
+
+   function Get_Ring_Address (EP : Endpoint_Range) return Word64
+   with SPARK_Mode => Off
+   is
+   begin
+      return Word64
+        (System.Storage_Elements.To_Integer (Rings (EP)'Address));
+   end Get_Ring_Address;
+
+   ----------------------------------------------------------------------------
+
+   procedure Initialize (EP : Endpoint_Range)
+   is
+   begin
+      pragma Debug (Debug.Put ("Initializing Transfer_Ring at "));
+      pragma Debug (Debug.Put_Word64 (Get_Ring_Address (EP)));
+      pragma Debug (Debug.Put (" (phys: "));
+      pragma Debug (Debug.Put_Word64 (Physical (EP)));
+      pragma Debug (Debug.Put_Line (")"));
+      TRBs.Init_Cycle_Ring
+        (Physical => Physical (EP),
+         Ring     => Rings (EP));
+
+      PCS (EP)       := 1;
+      Pointers (EP)  := Transfer_Pointers'
+        (Enqueue => TRBs.Ring_Range'First,
+         Dequeue => TRBs.Ring_Range'First,
+         Full    => False,
+         Toggle  => TRBs.Ring_Range'First,
+         Overrun => False);
+   end Initialize;
+
+   ----------------------------------------------------------------------------
+
+   procedure Toggle_CS (EP : Endpoint_Range)
+   is
+      use type TRBs.Ring_Range;
+      Toggle : TRBs.Ring_Range;
+   begin
+      Toggle := Pointers (EP).Toggle;
+      while Toggle /= Pointers (EP).Enqueue or Pointers (EP).Overrun loop
+         TRBs.Set_Cycle (Rings (EP) (Toggle), PCS (EP));
+         if Toggle = TRBs.Ring_Range'Last then
+            PCS (EP) := 1 - PCS (EP);
+         end if;
+         Toggle := Toggle + 1;
+
+         Pointers (EP).Overrun := False;
+      end loop;
+      Pointers (EP).Toggle := Toggle;
+   end Toggle_CS;
+
+   ----------------------------------------------------------------------------
+
+   function Prev (Current : TRBs.Ring_Range) return TRBs.Ring_Range
+   is
+      use type TRBs.Ring_Range;
+   begin
+      return Current - (if Current = TRBs.Ring_Range'First then 2 else 1);
+   end Prev;
+
+   function Next (Current : TRBs.Ring_Range) return TRBs.Ring_Range
+   is
+      use type TRBs.Ring_Range;
+   begin
+      return Current + (if Current + 1 = TRBs.Ring_Range'Last then 2 else 1);
+   end Next;
+
+   procedure Enqueue_TRB (EP : Endpoint_Range)
+   is
+      use type TRBs.Ring_Range;
+   begin
+      Pointers (EP).Enqueue := Next (Pointers (EP).Enqueue);
+      if Pointers (EP).Enqueue = Pointers (EP).Toggle then
+         Pointers (EP).Overrun := True;
+      end if;
+      if Pointers (EP).Enqueue = Pointers (EP).Dequeue then
+         Pointers (EP).Full := True;
+      end if;
+   end Enqueue_TRB;
+
+   procedure Dequeue_TRB (EP : Endpoint_Range)
+   is
+   begin
+      Pointers (EP).Dequeue := Next (Pointers (EP).Dequeue);
+      Pointers (EP).Full := False;
+   end Dequeue_TRB;
+
+   ----------------------------------------------------------------------------
+
+   procedure Dequeue
+     (EP                : Endpoint_Range;
+      Pointer           : Word64;
+      Status            : Error;
+      Remaining_Length  : Natural)
+   is
+      use type TRBs.TRB_Types;
+      use type TRBs.Ring_Range;
+      Current : TRBs.Ring_Range;
+      Invalid : Boolean;
+   begin
+      if Physical (EP) <= Pointer and
+         Pointer - Physical (EP) < TRBs.Ring_Size and
+         (Pointer - Physical (EP)) mod TRBs.TRB_Size = 0
+      then
+         Current := TRBs.Ring_Range
+           ((Pointer - Physical (EP)) / TRBs.TRB_Size);
+
+         if Pointers (EP).Dequeue < Pointers (EP).Enqueue then
+            Invalid :=
+               Current >= Pointers (EP).Enqueue or
+               Current < Pointers (EP).Dequeue;
+         else
+            Invalid :=
+               Current >= Pointers (EP).Enqueue and
+               Current < Pointers (EP).Dequeue;
+         end if;
+         Invalid := Invalid or Current = TRBs.Ring_Range'Last;
+      else
+         Current := TRBs.Ring_Range'First;
+         Invalid := True;
+      end if;
+
+      if not Invalid then
+         while Pointers (EP).Dequeue /= Current loop
+            -- Finish transfers that have been skipped by the controller
+            pragma Debug (Debug.Put_Line ("WARNING: Skipped TD."));
+            Transfer_Info.Finish
+              (DMA_Physical      => TRBs.Get_Parameter
+                                      (Rings (EP) (Pointers (EP).Dequeue)),
+               Status            => Controller_Error,
+               Remaining_Length  => Max_Bulk_Size);
+            Dequeue_TRB (EP);
+         end loop;
+         Transfer_Info.Finish
+           (DMA_Physical      => TRBs.Get_Parameter (Rings (EP) (Current)),
+            Status            => Status,
+            Remaining_Length  => Remaining_Length);
+         Dequeue_TRB (EP);
+      end if;
+
+      pragma Debug (Invalid, Debug.Put_Reg64
+        ("WARNING: Invalid dequeue pointer", Pointer));
+      pragma Debug (Invalid, Debug.Put_Reg64
+        ("                   Ring physical", Physical (EP)));
+      pragma Debug (Invalid, Debug.Put ("Enqueue: "));
+      pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Pointers (EP).Enqueue)));
+      pragma Debug (Invalid, Debug.Put ("; Dequeue: "));
+      pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Pointers (EP).Dequeue)));
+      pragma Debug (Invalid, Debug.Put ("; Current: "));
+      pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Current)));
+      pragma Debug (Invalid, Debug.New_Line);
+   end Dequeue;
+
+   ----------------------------------------------------------------------------
+
+   procedure Enqueue_Data_TRB
+     (EP                : Endpoint_Range;
+      Data_Length       : Natural;
+      Data_Buf          : Word64;
+      Toggle_CS         : Boolean)
+   is
+      Current : TRBs.Ring_Range;
+   begin
+      Current := Pointers (EP).Enqueue;
+
+      TRBs.Clear (Rings (EP) (Current), PCS (EP));
+      TRBs.Set_Parameter (Rings (EP) (Current), Data_Buf);
+      TRBs.Set_Length (Rings (EP) (Current), Data_Length);
+
+      TRBs.Set_Type (Rings (EP) (Current), TRBs.Normal);
+      TRBs.Set_IOC (Rings (EP) (Current));
+      TRBs.Set_ISP (Rings (EP) (Current));
+
+      Enqueue_TRB (EP);
+      if Toggle_CS then
+         Transfer_Rings.Toggle_CS (EP);
+      end if;
+   end Enqueue_Data_TRB;
+
+   procedure Requeue_Data_TRB
+     (EP       : Endpoint_Range;
+      Length   : Natural;
+      Buf_Addr : Word64)
+   is
+   begin
+      Pointers (EP).Enqueue := Prev (Pointers (EP).Enqueue);
+      Enqueue_Data_TRB (EP, Length, Buf_Addr, False);
+   end Requeue_Data_TRB;
+
+end HW.DbC.Transfer_Rings;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-transfer_rings.ads b/src/hw-dbc-transfer_rings.ads
new file mode 100644 (file)
index 0000000..c8e7311
--- /dev/null
@@ -0,0 +1,72 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+private package HW.DbC.Transfer_Rings
+with
+   Abstract_State => ((State with Part_Of  => HW.DbC.State),
+                      (DMA   with Part_Of  => HW.DbC.DMA,
+                                  External => (Async_Readers, Async_Writers)))
+is
+
+   function Physical (EP : Endpoint_Range) return Word64;
+
+   function Full (EP : Endpoint_Range) return Boolean;
+
+   function Last_Started (EP : Endpoint_Range) return Boolean;
+
+   ----------------------------------------------------------------------------
+
+   procedure Toggle_CS (EP : Endpoint_Range);
+
+   ----------------------------------------------------------------------------
+
+   procedure Initialize (EP : Endpoint_Range);
+
+   ----------------------------------------------------------------------------
+
+   procedure Dequeue
+     (EP                : Endpoint_Range;
+      Pointer           : Word64;
+      Status            : Error;
+      Remaining_Length  : Natural);
+
+   ----------------------------------------------------------------------------
+
+   use type Word64;
+   procedure Enqueue_Data_TRB
+     (EP                : Endpoint_Range;
+      Data_Length       : Natural;
+      Data_Buf          : Word64;
+      Toggle_CS         : Boolean)
+   with
+      Pre =>
+         Data_Length <= 2 ** 16 and
+         Data_Buf / 2 ** 16 =
+           (Data_Buf + Word64 (Data_Length) - 1) / 2 ** 16 and
+         Data_Buf + Word64 (Data_Length) - 1 <= Word64'Last;
+
+   procedure Requeue_Data_TRB
+     (EP       : Endpoint_Range;
+      Length   : Natural;
+      Buf_Addr : Word64)
+   with
+      Pre =>
+         Length <= 2 ** 16 and
+         Buf_Addr / 2 ** 16 =
+           (Buf_Addr + Word64 (Length) - 1) / 2 ** 16 and
+         Buf_Addr + Word64 (Length) - 1 <= Word64'Last;
+
+end HW.DbC.Transfer_Rings;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-transfers.adb b/src/hw-dbc-transfers.adb
new file mode 100644 (file)
index 0000000..7d70375
--- /dev/null
@@ -0,0 +1,248 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with System;
+
+with HW.Debug;
+with HW.DbC.Transfer_Info;
+with HW.DbC.Transfer_Rings;
+
+package body HW.DbC.Transfers
+is
+
+   Debug_TDs : constant Boolean := False;
+
+   Num_Receive_TDs : constant := 2;
+
+   ----------------------------------------------------------------------------
+
+   procedure Start_Bulk (Id : DbC.Transfer_Id; Start_Now : Boolean)
+   is
+      EP : constant Endpoint_Range  := Transfer_Info.Get_Endpoint (Id);
+      Len : constant Natural        := Transfer_Info.Get_Length (Id);
+      Pointer : constant Word64     := Transfer_Info.Physical (Id);
+   begin
+      -- It's just one TRB.
+      Transfer_Rings.Enqueue_Data_TRB
+        (EP                => EP,
+         Data_Length       => Len,
+         Data_Buf          => Pointer,
+         Toggle_CS         => Start_Now);
+
+      if Start_Now then
+         Ring_Doorbell (EP);
+      end if;
+
+      pragma Debug (Debug_TDs, Debug.Put ("TD  started; Length: "));
+      pragma Debug (Debug_TDs, Debug.Put_Int32 (Int32 (Len)));
+      pragma Debug (Debug_TDs, Debug.Put ("; Transfer: "));
+      pragma Debug (Debug_TDs, Debug.Put_Word16 (Word16 (Id)));
+      pragma Debug (Debug_TDs, Debug.Put_Reg64 ("; Pointer", Pointer));
+   end Start_Bulk;
+
+   ----------------------------------------------------------------------------
+
+   procedure Reset (Initial_Reset : Boolean)
+   is
+      Id : Transfer_Id;
+      Ctr : Word64;
+      Success : Boolean;
+   begin
+      Transfer_Rings.Initialize (2);
+      Transfer_Rings.Initialize (3);
+
+      if Initial_Reset then
+         for I in 1 .. Num_Receive_TDs loop
+            Transfer_Info.Start
+              (Endpoint    => 3,
+               Length      => Max_Bulk_Size,
+               Id          => Id,
+               Success     => Success);
+            exit when not Success;
+            Start_Bulk (Id, False);
+         end loop;
+      else
+         Ctr := 0;
+         loop
+            Transfer_Info.Walk_Started (Ctr, Id, Success);
+            exit when not Success;
+            Start_Bulk (Id, False);
+         end loop;
+      end if;
+   end Reset;
+
+   procedure Start
+   is
+   begin
+      for EP in Endpoint_Range loop
+         Transfer_Rings.Toggle_CS (EP);
+         Ring_Doorbell (EP);
+      end loop;
+   end Start;
+
+   ----------------------------------------------------------------------------
+
+   subtype DMA_Range is Natural range 0 .. Max_Bulk_Size - 1;
+   subtype DMA_Buffer is Buffer (DMA_Range);
+
+   procedure Copy_DMA_In
+     (Id       : in     Transfer_Id;
+      Buf      : in out Buffer;
+      Len      : in     Natural;
+      DMA_Off  : in     Natural)
+   is
+      use type Word64;
+      DMA_Buf : DMA_Buffer
+      with Address => System'To_Address (Transfer_Info.Physical (Id));
+      DMA_Len : constant Natural := Natural'Min (Max_Bulk_Size - DMA_Off, Len);
+   begin
+      Buf (Buf'First .. Buf'First + DMA_Len - 1) :=
+         DMA_Buf (DMA_Off .. DMA_Off + DMA_Len - 1);
+   end Copy_DMA_In;
+
+   procedure Copy_DMA_Out
+     (Id       : Transfer_Id;
+      Buf      : Buffer;
+      Off      : Natural;
+      Len      : Natural;
+      DMA_Off  : Natural := 0)
+   is
+      use type Word64;
+      DMA_Buf : DMA_Buffer
+      with Address => System'To_Address (Transfer_Info.Physical (Id));
+      DMA_Len : constant Natural := Natural'Min (Max_Bulk_Size - DMA_Off, Len);
+   begin
+      DMA_Buf (DMA_Off .. DMA_Off + DMA_Len - 1) :=
+         Buf (Off .. Off + DMA_Len - 1);
+   end Copy_DMA_Out;
+
+   ----------------------------------------------------------------------------
+
+   procedure Receive
+     (Buf : in out Buffer;
+      Len : in out Natural)
+   is
+      Id : Transfer_Id;
+      Ctr : Word64;
+      Success : Boolean;
+   begin
+      Ctr := 0;
+      loop
+         Transfer_Info.Walk_Finished (3, Ctr, Id, Success);
+         exit when
+            not Success or else
+            (Transfer_Info.Get_Status (Id) = DbC.Success or
+             Transfer_Info.Get_Status (Id) = DbC.Data_Residue);
+         Transfer_Info.Restart (Id, Max_Bulk_Size);  -- ignore failed transfers
+         Start_Bulk (Id, True);
+      end loop;
+      if Success then
+         Len := Natural'Min
+           (Len,
+            Transfer_Info.Get_Length (Id) - Transfer_Info.Get_Offset (Id));
+         Copy_DMA_In
+           (Id       => Id,
+            Buf      => Buf,
+            Len      => Len,
+            DMA_Off  => Transfer_Info.Get_Offset (Id));
+         Transfer_Info.Set_Offset (Id, Transfer_Info.Get_Offset (Id) + Len);
+         if Transfer_Info.Get_Offset (Id) = Transfer_Info.Get_Length (Id) then
+            Transfer_Info.Restart (Id, Max_Bulk_Size);
+            Start_Bulk (Id, True);
+         end if;
+      else
+         Len := 0;
+      end if;
+   end Receive;
+
+   ----------------------------------------------------------------------------
+
+   procedure Process_Finished_Sends
+   is
+      Id : Transfer_Id;
+      Ctr : Word64;
+      Success : Boolean;
+   begin
+      Ctr := 0;
+      loop
+         Transfer_Info.Walk_Finished (2, Ctr, Id, Success);
+         exit when not Success;
+         Transfer_Info.Reset (Id);
+      end loop;
+   end Process_Finished_Sends;
+
+   ----------------------------------------------------------------------------
+
+   procedure Send
+     (Buf         : in     Buffer;
+      Len         : in out Natural;
+      Start_Now   : in     Boolean;
+      Success     :    out Boolean)
+   is
+      Id : Transfer_Id;
+      Offset : Natural;
+      Appended : Natural := 0;
+   begin
+      if not Transfer_Rings.Last_Started (2) then
+         Appended := Len;
+         Transfer_Info.Append (2, Appended, Offset, Id);
+         if Appended > 0 then
+            Copy_DMA_Out
+              (Id       => Id,
+               Buf      => Buf,
+               Off      => Buf'First,
+               Len      => Appended,
+               DMA_Off  => Offset);
+            Transfer_Rings.Requeue_Data_TRB
+              (EP       => 2,
+               Length   => Transfer_Info.Get_Length (Id),
+               Buf_Addr => Transfer_Info.Physical (Id));
+         end if;
+      end if;
+      Success := True;
+
+      if Len > Appended then
+         Len := Natural'Min (Len - Appended, Max_Bulk_Size);
+
+         Process_Finished_Sends;
+
+         Transfer_Info.Start
+           (Endpoint    => 2,
+            Length      => Len,
+            Id          => Id,
+            Success     => Success);
+
+         if Success and not Transfer_Rings.Full (2) then
+            Copy_DMA_Out
+              (Id       => Id,
+               Buf      => Buf,
+               Off      => Buf'First + Appended,
+               Len      => Len);
+
+            Start_Bulk (Id, Start_Now);
+         else
+            Success := False;
+         end if;
+
+         if Success then
+            Len := Len + Appended;
+         else
+            Len := Appended;
+         end if;
+      end if;
+   end Send;
+
+end HW.DbC.Transfers;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-transfers.ads b/src/hw-dbc-transfers.ads
new file mode 100644 (file)
index 0000000..a97fc64
--- /dev/null
@@ -0,0 +1,39 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+private package HW.DbC.Transfers is
+
+   procedure Reset (Initial_Reset : Boolean);
+
+   procedure Start;
+
+   ----------------------------------------------------------------------------
+
+   procedure Receive
+     (Buf : in out Buffer;
+      Len : in out Natural)
+   with
+      Post => Len <= Len'Old;
+
+   procedure Send
+     (Buf         : in     Buffer;
+      Len         : in out Natural;
+      Start_Now   : in     Boolean;
+      Success     :    out Boolean)
+   with
+      Post => Len <= Len'Old;
+
+end HW.DbC.Transfers;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-trbs.adb b/src/hw-dbc-trbs.adb
new file mode 100644 (file)
index 0000000..63f15c2
--- /dev/null
@@ -0,0 +1,201 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with HW.DbC;
+with HW.Debug;
+
+use type HW.Bit;
+use type HW.Word32;
+
+package body HW.DbC.TRBs
+is
+
+   Debug_Error_Codes : constant Boolean := False;
+
+   function CC_To_Usb_Error (CC : Completion_Code) return Error
+   is
+      use type HW.DbC.Error;
+      Err : Error;
+   begin
+      case CC is
+      when Success                     => Err := DbC.Success;
+      when Data_Buffer_Error           => Err := DbC.Communication_Error;
+      when Babble_Detected_Error       => Err := DbC.Communication_Error;
+      when USB_Transaction_Error       => Err := DbC.Communication_Error;
+      when TRB_Error                   => Err := DbC.Driver_Error;
+      when Stall_Error                 => Err := DbC.Stall_Error;
+      when Short_Packet                => Err := DbC.Data_Residue;
+      when Bandwidth_Error             => Err := DbC.Not_Enough_Bandwidth;
+      when Command_Aborted             => Err := DbC.Timeout;
+      when Secondary_Bandwidth_Error   => Err := DbC.Not_Enough_Bandwidth;
+      when Split_Transaction_Error     => Err := DbC.Communication_Error;
+      when others                      => Err := DbC.Unknown_Error;
+         pragma Debug (Debug.Put_Reg8 ("Unknown_Error", Word8 (CC)));
+      end case;
+      pragma Debug
+        (Debug_Error_Codes and Err /= DbC.Success and Err /= DbC.Unknown_Error,
+         Debug.Put_Reg8 ("Completion code", Word8 (CC)));
+      return Err;
+   end CC_To_Usb_Error;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Length (Data : in out T; Length : in Natural)
+   is
+      Status : constant Word32 := Data.Status;
+   begin
+      Data.Status := (Status and not 16#1_ffff#) or
+        (Word32 (Length) and 16#1_ffff#);
+   end Set_Length;
+
+   function Get_Event_Length (Data : T) return Natural
+   is
+      Status : constant Word32 := Data.Status;
+   begin
+      return Natural (Status and 16#ff_ffff#);
+   end Get_Event_Length;
+
+   ----------------------------------------------------------------------------
+
+   function Get_Completion_Code (Data : T) return Completion_Code
+   is
+      Status : constant Word32 := Data.Status;
+   begin
+      return Completion_Code (Shift_Right (Status, 24));
+   end Get_Completion_Code;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Cycle (Data : in out T; Cycle : in Bit)
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      Data.Control := (Control and not 1) or Word32 (Cycle);
+   end Set_Cycle;
+
+   function Get_Cycle (Data : T) return Bit
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      return Bit (Control and 1);
+   end Get_Cycle;
+
+   procedure Set_Toggle_Cycle (Data : in out T)
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      Data.Control := Control or 16#00_02#;
+   end Set_Toggle_Cycle;
+
+   procedure Set_ISP (Data : in out T)
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      Data.Control := Control or 16#00_04#;
+   end Set_ISP;
+
+   procedure Set_IOC (Data : in out T)
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      Data.Control := Control or 16#00_20#;
+   end Set_IOC;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Type (Data : in out T; TRB_Type : in TRB_Types)
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      Data.Control := (Control and not 16#fc00#) or
+        Shift_Left (Word32 (TRB_Type), 10);
+   end Set_Type;
+
+   function Get_Type (Data : T) return TRB_Types
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      return TRB_Types (Shift_Right (Control, 10) and 63);
+   end Get_Type;
+
+   ----------------------------------------------------------------------------
+
+   function Get_Endpoint_ID (Data : T) return Natural
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      return Natural (Shift_Right (Control, 16) and 16#1f#);
+   end Get_Endpoint_ID;
+
+   function Get_Slot_ID (Data : T) return Word8
+   is
+      Control : constant Word32 := Data.Control;
+   begin
+      return Word8 (Shift_Right (Control, 24));
+   end Get_Slot_ID;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Parameter (Data : in out T; Parameter : Word64)
+   is
+   begin
+      Data.Parameter := Parameter;
+   end Set_Parameter;
+
+   function Get_Parameter (Data : T) return Word64
+   is
+      Result : constant Word64 := Data.Parameter;
+   begin
+      return Result;
+   end Get_Parameter;
+
+   ----------------------------------------------------------------------------
+
+   procedure Clear (TR : out T; PCS : in Bit)
+   is
+   begin
+      TR := T'
+        (Parameter => 0, Status => 0, Control => Word32 ((not PCS) and 1));
+   end Clear;
+
+   procedure Clear_Ring (TR : out Transfer_Ring; PCS : in Bit)
+   is
+   begin
+      TR := Transfer_Ring'
+        (Ring_Range => T'
+           (Parameter => 0, Status => 0, Control => Word32 ((not PCS) and 1)));
+   end Clear_Ring;
+
+   procedure Init_Cycle_Ring
+     (Ring     :    out Transfer_Ring;
+      Physical : in     Word64)
+   is
+   begin
+      Clear_Ring (Ring, 1);
+
+      declare
+         Last : constant Ring_Range := Ring'Last;
+      begin
+
+         -- Link last T in the ring to the first.
+
+         Set_Type (Ring (Last), Link);
+         Set_Toggle_Cycle (Ring (Last));
+         Ring (Last).Parameter := Physical;
+      end;
+   end Init_Cycle_Ring;
+
+end HW.DbC.TRBs;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc-trbs.ads b/src/hw-dbc-trbs.ads
new file mode 100644 (file)
index 0000000..e0855a1
--- /dev/null
@@ -0,0 +1,160 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+private package HW.DbC.TRBs is
+
+   TRB_Size : constant := 16;
+   type T is limited private;
+
+   ----------------------------------------------------------------------------
+
+   TRBs_Per_Ring : constant := 2 ** 6;
+   Ring_Size     : constant := TRBs_Per_Ring * TRB_Size;
+   type Ring_Range is mod TRBs_Per_Ring;
+
+   type Transfer_Ring is array (Ring_Range) of T
+   with
+      Pack,
+      Volatile,
+      Alignment => Ring_Size;
+   --  Use size for alignment:
+   --  It always fullfills page crossing requirements.
+
+   ----------------------------------------------------------------------------
+
+   type Transfer_Type is mod 2 ** 2;
+   No_Data_Stage                       : constant Transfer_Type := 0;
+   OUT_Data_Stage                      : constant Transfer_Type := 2;
+   IN_Data_Stage                       : constant Transfer_Type := 3;
+
+   type Direction is mod 2 ** 1;
+   Dir_OUT                             : constant Direction := 0;
+   Dir_IN                              : constant Direction := 1;
+
+   type TRB_Types is mod 2 ** 6;
+   Empty                               : constant TRB_Types := 0;
+   Normal                              : constant TRB_Types := 1;
+   Setup_Stage                         : constant TRB_Types := 2;
+   Data_Stage                          : constant TRB_Types := 3;
+   Status_Stage                        : constant TRB_Types := 4;
+   Isoch                               : constant TRB_Types := 5;
+   Link                                : constant TRB_Types := 6;
+   Event_Data                          : constant TRB_Types := 7;
+   NoOp                                : constant TRB_Types := 8;
+   Enable_Slot_Command                 : constant TRB_Types := 9;
+   Disable_Slot_Command                : constant TRB_Types := 10;
+   Address_Device_Command              : constant TRB_Types := 11;
+   Configure_Endpoint_Command          : constant TRB_Types := 12;
+   Evaluate_Context_Command            : constant TRB_Types := 13;
+   Reset_Endpoint_Command              : constant TRB_Types := 14;
+   Stop_Endpoint_Command               : constant TRB_Types := 15;
+   Set_TR_Dequeue_Pointer_Command      : constant TRB_Types := 16;
+   Reset_Device_Command                : constant TRB_Types := 17;
+   Force_Event_Command                 : constant TRB_Types := 18;
+   Negotiate_Bandwidth_Command         : constant TRB_Types := 19;
+   Set_Latency_Tolerance_Value_Command : constant TRB_Types := 20;
+   Get_Port_Bandwidth_Command          : constant TRB_Types := 21;
+   Force_Header_Command                : constant TRB_Types := 22;
+   NoOp_Command                        : constant TRB_Types := 23;
+   Transfer_Event                      : constant TRB_Types := 32;
+   Command_Completion_Event            : constant TRB_Types := 33;
+   Port_Status_Change_Event            : constant TRB_Types := 34;
+   Bandwidth_Request_Event             : constant TRB_Types := 35;
+   Doorbell_Event                      : constant TRB_Types := 36;
+   Host_Controller_Event               : constant TRB_Types := 37;
+   Device_Notification_Event           : constant TRB_Types := 38;
+   MFINDEX_Wrap_Event                  : constant TRB_Types := 39;
+   NEC_Firmware_Request_Event          : constant TRB_Types := 48;
+   NEC_Firmware_Request_Command        : constant TRB_Types := 49;
+
+   type Completion_Code is mod 2 ** 8;
+   Invalid                          : constant Completion_Code := 0;
+   Success                          : constant Completion_Code := 1;
+   Data_Buffer_Error                : constant Completion_Code := 2;
+   Babble_Detected_Error            : constant Completion_Code := 3;
+   USB_Transaction_Error            : constant Completion_Code := 4;
+   TRB_Error                        : constant Completion_Code := 5;
+   Stall_Error                      : constant Completion_Code := 6;
+   Resource_Error                   : constant Completion_Code := 7;
+   Bandwidth_Error                  : constant Completion_Code := 8;
+   No_Slots_Available_Error         : constant Completion_Code := 9;
+   Invalid_Stream_Type_Error        : constant Completion_Code := 10;
+   Slot_Not_Enabled_Error           : constant Completion_Code := 11;
+   Endpoint_Not_Enabled_Error       : constant Completion_Code := 12;
+   Short_Packet                     : constant Completion_Code := 13;
+   Ring_Underrun                    : constant Completion_Code := 14;
+   Ring_Overrun                     : constant Completion_Code := 15;
+   VF_Event_Ring_Full_Error         : constant Completion_Code := 16;
+   Parameter_Error                  : constant Completion_Code := 17;
+   Bandwidth_Overrun_Error          : constant Completion_Code := 18;
+   Context_State_Error              : constant Completion_Code := 19;
+   No_Ping_Response_Error           : constant Completion_Code := 20;
+   Event_Ring_Full_Error            : constant Completion_Code := 21;
+   Incompatible_Device_Error        : constant Completion_Code := 22;
+   Missed_Service_Error             : constant Completion_Code := 23;
+   Command_Ring_Stopped             : constant Completion_Code := 24;
+   Command_Aborted                  : constant Completion_Code := 25;
+   Stopped                          : constant Completion_Code := 26;
+   Stopped_Length_Invalid           : constant Completion_Code := 27;
+   Max_Exit_Latency_Too_Large_Error : constant Completion_Code := 29;
+   Isoch_Buffer_Overrun             : constant Completion_Code := 31;
+   Event_Lost_Error                 : constant Completion_Code := 32;
+   Undefined_Error                  : constant Completion_Code := 33;
+   Invalid_Stream_ID_Error          : constant Completion_Code := 34;
+   Secondary_Bandwidth_Error        : constant Completion_Code := 35;
+   Split_Transaction_Error          : constant Completion_Code := 36;
+   function CC_To_Usb_Error (CC : Completion_Code) return Error;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Length (Data : in out T; Length : in Natural);
+   function Get_Event_Length (Data : T) return Natural;
+
+   function Get_Completion_Code (Data : T) return Completion_Code;
+
+   procedure Set_Cycle (Data : in out T; Cycle : in Bit);
+   function Get_Cycle (Data : T) return Bit;
+   procedure Set_ISP (Data : in out T);
+   procedure Set_IOC (Data : in out T);
+
+   procedure Set_Type (Data : in out T; TRB_Type : in TRB_Types);
+   function Get_Type (Data : T) return TRB_Types;
+
+   function Get_Endpoint_ID (Data : T) return Natural;
+   function Get_Slot_ID (Data : T) return Word8;
+
+   procedure Set_Parameter (Data : in out T; Parameter : Word64);
+   function Get_Parameter (Data : T) return Word64;
+
+   procedure Clear (TR : out T; PCS : in Bit);
+   procedure Clear_Ring (TR : out Transfer_Ring; PCS : in Bit);
+   procedure Init_Cycle_Ring
+     (Ring     :    out Transfer_Ring;
+      Physical : in     Word64);
+
+private
+
+   type T is record
+      Parameter : Word64;
+      Status    : Word32;
+      Control   : Word32;
+   end record
+   with
+      Pack,
+      Volatile,
+      Alignment => Natural'Max (16, TRB_Size);
+
+end HW.DbC.TRBs;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc.adb b/src/hw-dbc.adb
new file mode 100644 (file)
index 0000000..b711b71
--- /dev/null
@@ -0,0 +1,473 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with System;
+
+with HW.Time;
+with HW.Debug;
+
+with HW.DbC.Intel_Quirk;
+with HW.DbC.DMA_Buffers;
+with HW.DbC.Transfer_Info;
+with HW.DbC.Transfer_Rings;
+with HW.DbC.Transfers;
+with HW.DbC.Contexts;
+with HW.DbC.Events;
+with HW.DbC.TRBs;
+
+package body HW.DbC
+with
+   Refined_State => (State => (Reset_Intermission_End, Connected, Running,
+                               DbC_Run_Deadline, DbC_Poll_Deadline,
+                               DbC_Stat_Deadline, Events.State,
+                               Transfer_Info.State, Transfer_Rings.State),
+                     DMA   => (ERST, DbC_Context, Desc_Strings, Events.DMA,
+                               Transfer_Rings.DMA))
+is
+
+   Perform_Hardware_Reset : constant Boolean := True;
+   Apply_Intel_Quirk : constant Boolean := True;
+   Debug_xCap : constant Boolean := False;
+
+   Reset_Intermission_MS : constant := 736;  -- seems reliable above 722ms
+   Reset_Intermission_End : Time.T;
+
+   Connected,
+   Running : Boolean;
+   DbC_Run_Deadline : Time.T;
+   DbC_Poll_Deadline : Time.T;
+   DbC_Stat_Deadline : Time.T;
+
+   ----------------------------------------------------------------------------
+
+   ERST : Events.ERST_Entry
+   with
+      Address => System'To_Address (DMA_Buffers.Event_Ring_Segment_Table_Base);
+
+   DbC_Context : Contexts.DbC_Context
+   with
+      Address => System'To_Address (DMA_Buffers.DbC_Context_Base);
+
+   ----------------------------------------------------------------------------
+
+   subtype Desc_Str_Range is Natural range 0 .. 14;
+   type Desc_Str is array (Desc_Str_Range) of Word16 with Pack;
+   type String_Descriptor is record
+      bLength           : Byte;
+      bDescriptor_Type  : Byte;
+      wData             : Desc_Str;
+   end record with Pack;
+
+   type Desc_Strings_Type is (String0, Manufacturer, Product, Serial_Number);
+   type Desc_Strings_Array is
+      array (Desc_Strings_Type) of String_Descriptor with Pack;
+   Desc_Strings : Desc_Strings_Array
+   with
+      Address => System'To_Address (DMA_Buffers.Descriptor_Strings_Base);
+
+   procedure String_To_Desc (Dst : out String_Descriptor; Src : in String)
+   is
+      use type Byte;
+   begin
+      Dst.bLength := 2 + 2 * Byte'Min (Dst.wData'Length, Src'Length);
+      Dst.bDescriptor_Type := 16#03#;
+      for I in Desc_Str_Range loop
+         if I < Src'Last then
+            Dst.wData (I) := Character'Pos (Src (I + 1));
+         else
+            Dst.wData (I) := 16#0000#;
+         end if;
+      end loop;
+   end String_To_Desc;
+
+   ----------------------------------------------------------------------------
+
+   procedure Find_Next_xCap (Cap_Id : in Word8; Success : out Boolean)
+   is
+      use type Word8;
+      use type Word32;
+      Current_Id : Word8;
+      Temp_Offset : Word32;
+   begin
+      Success := False;
+      if xCap_Regs.Byte_Offset = 0 then
+         Cap_Regs.Read (Temp_Offset, XHCI_Extended_Caps);
+      else
+         xCap_Regs.Read (Temp_Offset, Next_xCap);
+      end if;
+      loop
+         Temp_Offset := Shift_Left (Temp_Offset, 2);
+         pragma Debug (Debug_xCap, Debug.Put_Reg32
+           ("Find_Next_xCap Offset", Temp_Offset));
+         exit when
+            Temp_Offset = 0 or else
+            xCap_Regs.Byte_Offset > MMIO_Size - Natural (Temp_Offset) - 2;
+
+         xCap_Regs.Byte_Offset := xCap_Regs.Byte_Offset + Natural (Temp_Offset);
+
+         xCap_Regs.Read (Current_Id, Capability_ID);
+         Success := Current_Id = Cap_Id;
+         pragma Debug (Debug_xCap, Debug.Put_Reg8
+           ("Find_Next_xCap Cap_Id", Current_Id));
+         exit when Success;
+
+         xCap_Regs.Read (Temp_Offset, Next_xCap);
+      end loop;
+   end Find_Next_xCap;
+
+   ----------------------------------------------------------------------------
+
+   procedure BIOS_Handover (Success : out Boolean)
+   is
+      use type Word8;
+      BIOS_Owned  : Word8;
+      Deadline    : Time.T;
+   begin
+      xCap_Regs.Byte_Offset := 0;
+      Find_Next_xCap (1, Success);
+      if Success then
+         Legacy_Support_Regs.Byte_Offset := xCap_Regs.Byte_Offset;
+         -- See if the BIOS claims ownership
+         Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
+         if BIOS_Owned = 1 then
+            pragma Debug (Debug.Put_Line ("DbC: BIOS claims ownership."));
+
+            Legacy_Support_Regs.Write (HC_OS_Owned_Semaphore, Word8'(1));
+
+            Deadline := Time.MS_From_Now (5_000);
+            loop
+               Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
+               exit when BIOS_Owned = 0;
+               declare
+                  Timeout : constant Boolean := Time.Timed_Out (Deadline);
+               begin
+                  Success := not Timeout;
+               end;
+               exit when not Success;
+               for I in 0 .. 1234 loop
+                  null; -- Busy loop to reduce pressure on HC BIOS Owned
+                        -- Semaphore. It shouldn't generate an SMI but
+                        -- might congest the xHC?
+               end loop;
+            end loop;
+
+            pragma Debug (not Success, Debug.Put_Line
+              ("ERROR: BIOS didn't hand over xHC within 5s."));
+            pragma Debug (Success, Debug.Put_Line
+              ("DbC: BIOS hand-over succeeded."));
+         end if;
+      end if;
+   end BIOS_Handover;
+
+   procedure Reset_xHC (Success : out Boolean)
+   is
+      use type Word8;
+      HCH,
+      HCR : Word8;
+      Deadline : Time.T;
+   begin
+      Op_Regs.Write (Run_Stop, Word8'(0));
+      Deadline := Time.MS_From_Now (1_000);
+      Success := True;
+      loop
+         Op_Regs.Read (HCH, HC_Halted);
+         exit when HCH = 1;
+         Success := not Time.Timed_Out (Deadline);
+         exit when not Success;
+      end loop;
+      pragma Debug (not Success, Debug.Put_Line
+        ("ERROR: xHC didn't halt within 1s."));
+
+      if Success then
+         Op_Regs.Write (Host_Controller_Reset, Word8'(1));
+         Deadline := Time.MS_From_Now (1_000);
+
+         -- Some Intel xHCI implementations are known to freak out rarely
+         -- (anything can happen up to global reset assertion) if the
+         -- Host Controller Reset bit is polled before the controller is
+         -- ready.
+         Time.M_Delay (1); -- Delay here or hell freezes over
+
+         loop
+            Op_Regs.Read (HCR, Host_Controller_Reset);
+            exit when HCR = 0;
+            Success := not Time.Timed_Out (Deadline);
+            exit when not Success;
+         end loop;
+         pragma Debug (not Success, Debug.Put_Line
+           ("ERROR: xHC didn't finish reset within 1s."));
+      end if;
+   end Reset_xHC;
+
+   procedure Reset (Initial_Reset : Boolean := False);
+
+   procedure Init
+   is
+      use type Word8;
+      CNR : Word8;
+      Deadline : Time.T;
+      Success : Boolean;
+      Cap_Length : Word8;
+   begin
+      Cap_Regs.Read (Cap_Length, Capability_Registers_Length);
+      Op_Regs.Byte_Offset := Natural (Cap_Length);
+
+      Op_Regs.Read (CNR, Controller_Not_Ready);
+      Success := CNR = 0;
+
+      if not Success then
+         pragma Debug (Debug.Put_Line ("WARNING: xHCI not ready!"));
+         Deadline := Time.MS_From_Now (1_000);
+         Success := True;
+         loop
+            Op_Regs.Read (CNR, Controller_Not_Ready);
+            exit when CNR = 0;
+            Success := not Time.Timed_Out (Deadline);
+            exit when not Success;
+         end loop;
+         pragma Debug (not Success, Debug.Put_Line
+           ("ERROR: xHC not ready after 1s."));
+      end if;
+
+      if Success then
+         BIOS_Handover (Success);
+      end if;
+
+      if Perform_Hardware_Reset and then Success then
+         Reset_xHC (Success);
+         Reset_Intermission_End := Time.MS_From_Now (Reset_Intermission_MS);
+      else
+         Reset_Intermission_End := Time.Now;
+      end if;
+
+      if Success then
+         xCap_Regs.Byte_Offset := 0;
+         Find_Next_xCap (10, Success);
+      end if;
+
+      if Success then
+         Regs.Byte_Offset := xCap_Regs.Byte_Offset;
+
+         ERST := Events.ERST_Entry'
+           (Segment_Base   => DMA_Buffers.Event_Ring_Base,
+            Segment_Size   => TRBs.TRBs_Per_Ring,
+            Reserved_Z     => 0);
+
+         Desc_Strings (String0).bLength            := 16#04#;
+         Desc_Strings (String0).bDescriptor_Type   := 16#03#;
+         Desc_Strings (String0).wData := (0 => 16#0409#, others => 16#0000#);
+         String_To_Desc (Desc_Strings (Manufacturer), "secunet");
+         String_To_Desc (Desc_Strings (Product), "HW.DbC");
+         String_To_Desc (Desc_Strings (Serial_Number), "1");
+
+         Reset (Initial_Reset => True);
+      else
+         null;
+         pragma Debug (Debug.Put_Line
+           ("ERROR: Couldn't find xHCI debug capability."));
+      end if;
+   end Init;
+
+   ----------------------------------------------------------------------------
+
+   procedure Reset (Initial_Reset : Boolean := False)
+   is
+      use type Word8;
+      use type Word16;
+      use type Word64;
+      DCE,
+      MBS : Word8;
+   begin
+      if Regs.Byte_Offset /= 0 then
+         Regs.Write (DbC_Enable, Word8'(0));
+         loop
+            Regs.Read (DCE, DbC_Enable);
+            exit when DCE = 0;
+         end loop;
+
+         Transfers.Reset (Initial_Reset);
+
+         Regs.Write (ERST_Size, Word16'(1));
+         Regs.Write (ERST_Base_Lo, Word32
+           (DMA_Buffers.Event_Ring_Segment_Table_Base mod 16#1_0000_0000#));
+         Regs.Write (ERST_Base_Hi, Word32
+           (DMA_Buffers.Event_Ring_Segment_Table_Base  /  16#1_0000_0000#));
+         Events.Reset_Ring;
+
+         Regs.Write (ER_Dequeue_Ptr_Lo, Word32
+           (DMA_Buffers.Event_Ring_Base mod 16#1_0000_0000#));
+         Regs.Write (ER_Dequeue_Ptr_Hi, Word32
+           (DMA_Buffers.Event_Ring_Base  /  16#1_0000_0000#));
+
+         Regs.Write (Context_Pointer_Lo, Word32
+           (DMA_Buffers.DbC_Context_Base mod 16#1_0000_0000#));
+         Regs.Write (Context_Pointer_Hi, Word32
+           (DMA_Buffers.DbC_Context_Base  /  16#1_0000_0000#));
+
+         Contexts.Clear_DbC_Context (DbC_Context);
+         DbC_Context.DbC_Info :=
+           (String_0_Address              => DMA_Buffers.Descriptor_Strings_Base,
+            Manufacturer_String_Address   => DMA_Buffers.Descriptor_Strings_Base
+                                             + 1 * String_Descriptor'Size / 8,
+            Product_String_Address        => DMA_Buffers.Descriptor_Strings_Base
+                                             + 2 * String_Descriptor'Size / 8,
+            Serial_Number_String_Address  => DMA_Buffers.Descriptor_Strings_Base
+                                             + 3 * String_Descriptor'Size / 8,
+            String_0_Length               => Desc_Strings (String0).bLength,
+            Manufacturer_String_Length    => Desc_Strings (Manufacturer).bLength,
+            Product_String_Length         => Desc_Strings (Product).bLength,
+            Serial_Number_String_Length   => Desc_Strings (Serial_Number).bLength,
+            Reserved_Z                    => 0,
+            others                        => 0);
+
+         Regs.Read (MBS, Debug_Max_Burst_Size);
+         DbC_Context.OUT_EP.EP_Type                := Contexts.Bulk_O;
+         DbC_Context.OUT_EP.Max_Burst_Size         := MBS;
+         DbC_Context.OUT_EP.Max_Packet_Size        := 1024;
+         DbC_Context.OUT_EP.TR_Dequeue_Pointer_Lo  := Word28
+           (Shift_Right (Transfer_Rings.Physical (2),  4) and 16#0fff_ffff#);
+         DbC_Context.OUT_EP.TR_Dequeue_Pointer_Hi  := Word32
+           (Shift_Right (Transfer_Rings.Physical (2), 32) and 16#ffff_ffff#);
+         DbC_Context.OUT_EP.Dequeue_Cycle_State    := 1;
+         DbC_Context.OUT_EP.Average_TRB_Length     := Max_Bulk_Size / 2;
+         DbC_Context.IN_EP.EP_Type                 := Contexts.Bulk_I;
+         DbC_Context.IN_EP.Max_Burst_Size          := MBS;
+         DbC_Context.IN_EP.Max_Packet_Size         := 1024;
+         DbC_Context.IN_EP.TR_Dequeue_Pointer_Lo   := Word28
+           (Shift_Right (Transfer_Rings.Physical (3),  4) and 16#0fff_ffff#);
+         DbC_Context.IN_EP.TR_Dequeue_Pointer_Hi   := Word32
+           (Shift_Right (Transfer_Rings.Physical (3), 32) and 16#ffff_ffff#);
+         DbC_Context.IN_EP.Dequeue_Cycle_State     := 1;
+         DbC_Context.IN_EP.Average_TRB_Length      := Max_Bulk_Size / 2;
+
+         Regs.Write (DbC_Protocol, Word16'(0));  -- Debug Target vendor defined.
+         Regs.Write (Vendor_ID, Word16 (16#ffff#));
+         Regs.Write (Product_ID, Word16 (16#dbc1#));
+         Regs.Write (Device_Revision, Word16 (16#0001#));
+
+         Time.Delay_Until (Reset_Intermission_End);
+         Regs.Write (DbC_Enable, Word8'(1));
+         loop
+            Regs.Read (DCE, DbC_Enable);
+            exit when DCE = 1;
+         end loop;
+
+         if Apply_Intel_Quirk then
+            Intel_Quirk.Reset_Port;
+         end if;
+
+         Running := False;
+         Connected := False;
+         DbC_Poll_Deadline := Time.Now;
+         DbC_Stat_Deadline := Time.MS_From_Now (12_345);
+      end if;
+   end Reset;
+
+   procedure Poll (Now : Boolean := False)
+   is
+      use type Word8;
+      use type Word64;
+      Temp8 : Word8;
+      Timed_Out : Boolean;
+   begin
+      if Regs.Byte_Offset /= 0 then
+         Timed_Out := Now or else Time.Timed_Out (DbC_Poll_Deadline);
+         if Timed_Out then
+            Regs.Read (Temp8, DbC_Enable);
+            if Temp8 = 1 then
+               Regs.Read (Temp8, Current_Connect_Status);
+               if Temp8 = 1 then
+                  -- Something is connected...
+                  DbC_Poll_Deadline := Time.MS_From_Now (10);
+                  if not Connected then
+                     pragma Debug (Debug.Put_Line ("DbC connected."));
+                     DbC_Run_Deadline := Time.MS_From_Now (333);
+                     Connected := True;
+                  end if;
+                  Regs.Read (Temp8, DbC_Run);
+                  if Temp8 = 1 then
+                     -- ...configured too
+                     if not Running then
+                        pragma Debug (Debug.Put_Line ("DbC configured."));
+                        Transfers.Start;
+                        Running := True;
+                     end if;
+                  elsif Running then
+                     pragma Debug (Debug.Put_Line
+                       ("DbC still connected but deconfigured."));
+                     DbC_Run_Deadline := Time.MS_From_Now (333);
+                     Running := False;
+                  else
+                     Timed_Out := Time.Timed_Out (DbC_Run_Deadline);
+                     if Timed_Out then
+                        pragma Debug (Debug.Put_Line
+                          ("DbC connection timed out."));
+                        Reset;
+                     end if;
+                  end if;
+               else
+                  -- Nothing connected
+                  DbC_Poll_Deadline := Time.MS_From_Now (333);
+                  if Connected then
+                     pragma Debug (Debug.Put_Line ("DbC disconnected."));
+                     Connected := False;
+                     Running := False;
+                  end if;
+               end if;
+            else
+               Reset_Intermission_End :=
+                  Time.MS_From_Now (Reset_Intermission_MS);
+               pragma Debug (Debug.Put_Line ("DbC got disabled, huh?"));
+               Reset;
+            end if;
+            Events.Handle_Events;
+            Timed_Out := Time.Timed_Out (DbC_Stat_Deadline);
+            if Timed_Out then
+               pragma Debug (Transfer_Info.Dump_Stats);
+               DbC_Stat_Deadline := Time.MS_From_Now (12_345);
+            end if;
+         end if;
+      end if;
+   end Poll;
+
+   procedure Receive (Buf : in out Buffer; Len : in out Natural)
+   is
+   begin
+      Poll (Now => True);
+
+      Transfers.Receive (Buf, Len);
+   end Receive;
+
+   procedure Send (Buf : Buffer; Len : in out Natural; Success : out Boolean)
+   is
+   begin
+      Poll (Now => True);
+
+      Transfers.Send
+        (Buf         => Buf,
+         Len         => Len,
+         Start_Now   => Running,
+         Success     => Success);
+   end Send;
+
+   procedure Ring_Doorbell (EP : Endpoint_Range)
+   is
+      use type Word8;
+   begin
+      Regs.Write (Doorbell_Target, Word8 (EP) - 2);
+   end Ring_Doorbell;
+
+end HW.DbC;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc.ads b/src/hw-dbc.ads
new file mode 100644 (file)
index 0000000..e7a51ca
--- /dev/null
@@ -0,0 +1,235 @@
+--
+-- Copyright (C) 2016-2017 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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 General Public License for more details.
+--
+
+with HW.Sub_Regs;
+with HW.MMIO_Regs;
+pragma Elaborate_All (HW.MMIO_Regs);
+with HW.MMIO_Range;
+pragma Elaborate_All (HW.MMIO_Range);
+with HW.DbC_Config;
+
+package HW.DbC
+with
+   Abstract_State => (State, (DMA with External))
+is
+
+   procedure Init;
+
+   procedure Poll (Now : Boolean := False);
+
+   procedure Receive
+     (Buf : in out Buffer;
+      Len : in out Natural)
+   with
+      Post => Len <= Len'Old;
+
+   procedure Send
+     (Buf      : in     Buffer;
+      Len      : in out Natural;
+      Success  :    out Boolean)
+   with
+      Post => Len <= Len'Old;
+
+   ----------------------------------------------------------------------------
+
+private
+
+   type Word2  is mod 2 **  2;
+   type Word3  is mod 2 **  3;
+   type Word4  is mod 2 **  4;
+   type Word5  is mod 2 **  5;
+   type Word10 is mod 2 ** 10;
+   type Word19 is mod 2 ** 19;
+   type Word20 is mod 2 ** 20;
+   type Word28 is mod 2 ** 28;
+
+   type Error is (
+      Success,
+      Communication_Error,
+      Driver_Error,
+      Stall_Error,
+      Data_Residue,
+      Bad_Descriptor,
+      Not_Enough_Bandwidth,
+      Unsupported_Size,
+      Unsupported_Count,
+      No_Space_Left,
+      Unknown_Device_Class,
+      Invalid_Data,
+      Invalid_Request,
+      Timeout,
+      Class_Prohibited,
+      Initialization_Error,
+      Controller_Error,
+      No_USB2_Address,
+      Device_Not_Supported,
+      Unknown_Error);
+
+   ----------------------------------------------------------------------------
+
+   Max_DMA_Xfers : constant   := 64;
+   Max_Bulk_Size : constant   := 4096;
+   Highest_Address : constant := 16#0000_ffff_ffff_ffff#;
+
+   Endpoint_Count : constant  := 3;
+   -- Endpoint 1 (the control endpoint) is handled by hardware
+   -- Endpoint 2 is the bulk out endpoint
+   -- Endpoint 3 is the bulk in endpoint
+   subtype Endpoint_Range is Positive range 2 .. Endpoint_Count;
+
+   type Global_Transfer_Id is range 0 .. Max_DMA_Xfers - 1;
+   -- DMA space for the last transfer is reserved for the kernel.
+   subtype Transfer_Id is
+      Global_Transfer_Id range 0 .. Global_Transfer_Id'Last - 1;
+
+   ----------------------------------------------------------------------------
+
+   MMIO_Size : constant := DbC_Config.XHCI_SIZE;
+   type MMIO_Index is range 0 .. MMIO_Size / 4 - 1;
+   type MMIO_Array is array (MMIO_Index) of Word32
+   with
+      Atomic_Components,
+      Size => MMIO_Size * 8;
+   pragma Warnings (Off, "atomic synchronization set");
+   package MMIO is new MMIO_Range
+     (Base_Addr   => DbC_Config.XHCI_BASE,
+      Element_T   => Word32,
+      Index_T     => MMIO_Index,
+      Array_T     => MMIO_Array);
+   pragma Warnings (On, "atomic synchronization set");
+
+   type Cap_Registers is
+     (Capability_Registers_Length,
+      XHCI_Extended_Caps);
+   package Cap_S_Regs is new Sub_Regs (Cap_Registers);
+   Cap_Reg_Descs : constant Cap_S_Regs.Array_T :=
+     (Capability_Registers_Length      => (16#00#,  7,  0),
+      XHCI_Extended_Caps               => (16#10#, 31, 16));
+   package Cap_Regs is new MMIO_Regs (MMIO, 0, Cap_S_Regs, Cap_Reg_Descs);
+
+   type Op_Registers is
+     (Run_Stop,
+      Host_Controller_Reset,
+      HC_Halted,
+      Controller_Not_Ready);
+   package Op_S_Regs is new Sub_Regs (Op_Registers);
+   Op_Reg_Descs : constant Op_S_Regs.Array_T :=
+     (Run_Stop                         => (16#00#,  0,  0),
+      Host_Controller_Reset            => (16#00#,  1,  1),
+      HC_Halted                        => (16#04#,  0,  0),
+      Controller_Not_Ready             => (16#04#, 11, 11));
+   package Op_Regs is new MMIO_Regs (MMIO, 0, Op_S_Regs, Op_Reg_Descs);
+
+   type xCap_Registers is
+     (Capability_ID,
+      Next_xCap);
+   package xCap_S_Regs is new Sub_Regs (xCap_Registers);
+   xCap_Reg_Descs : constant xCap_S_Regs.Array_T :=
+     (Capability_ID                    => (16#00#,  7,  0),
+      Next_xCap                        => (16#00#, 15,  8));
+   package xCap_Regs is new MMIO_Regs (MMIO, 0, xCap_S_Regs, xCap_Reg_Descs);
+   procedure Find_Next_xCap (Cap_Id : in Word8; Success : out Boolean);
+
+   type Legacy_Support_Registers is
+     (HC_BIOS_Owned_Semaphore,
+      HC_OS_Owned_Semaphore);
+   package Legacy_Support_S_Regs is new Sub_Regs (Legacy_Support_Registers);
+   Legacy_Support_Reg_Descs : constant Legacy_Support_S_Regs.Array_T :=
+     (HC_BIOS_Owned_Semaphore => (16#00#, 16, 16),
+      HC_OS_Owned_Semaphore   => (16#00#, 24, 24));
+   package Legacy_Support_Regs is new MMIO_Regs
+     (DbC.MMIO, 0, Legacy_Support_S_Regs, Legacy_Support_Reg_Descs);
+
+   type Registers is
+     (Doorbell_Target,
+      ERST_Size,
+      ERST_Base_Lo,
+      ERST_Base_Hi,
+      ER_Dequeue_Ptr_Lo,
+      ER_Dequeue_Ptr_Hi,
+      DBC_CONTROL,
+      DbC_Run,
+      Link_Status_Event_Enable,
+      Halt_OUT_TR,
+      Halt_IN_TR,
+      DbC_Run_Change,
+      Debug_Max_Burst_Size,
+      Device_Address,
+      DbC_Enable,
+      DBC_STATUS,
+      Event_Ring_Not_Empty,
+      DbC_System_Bus_Reset,
+      Debug_Port_Number,
+      DBC_PORTSC,
+      Current_Connect_Status,
+      Port_Enable_Disable,
+      Port_Reset,
+      Port_Link_State,
+      Port_Speed,
+      Connect_Status_Change,
+      Port_Reset_Change,
+      Port_Link_Status_Change,
+      Port_Config_Error_Change,
+      Context_Pointer_Lo,
+      Context_Pointer_Hi,
+      DbC_Protocol,
+      Vendor_ID,
+      Product_ID,
+      Device_Revision);
+   package S_Regs is new Sub_Regs (Registers);
+   Reg_Descs : constant S_Regs.Array_T :=
+     (Doorbell_Target                  => (16#04#, 15,  8),
+      ERST_Size                        => (16#08#, 15,  0),
+      ERST_Base_Lo                     => (16#10#, 31,  0),
+      ERST_Base_Hi                     => (16#14#, 31,  0),
+      ER_Dequeue_Ptr_Lo                => (16#18#, 31,  0),
+      ER_Dequeue_Ptr_Hi                => (16#1c#, 31,  0),
+      DBC_CONTROL                      => (16#20#, 31,  0),
+      DbC_Run                          => (16#20#,  0,  0),
+      Link_Status_Event_Enable         => (16#20#,  1,  1),
+      Halt_OUT_TR                      => (16#20#,  2,  2),
+      Halt_IN_TR                       => (16#20#,  3,  3),
+      DbC_Run_Change                   => (16#20#,  4,  4),
+      Debug_Max_Burst_Size             => (16#20#, 23, 16),
+      Device_Address                   => (16#20#, 30, 24),
+      DbC_Enable                       => (16#20#, 31, 31),
+      DBC_STATUS                       => (16#24#, 31,  0),
+      Event_Ring_Not_Empty             => (16#24#,  0,  0),
+      DbC_System_Bus_Reset             => (16#24#,  1,  1),
+      Debug_Port_Number                => (16#24#, 31, 24),
+      DBC_PORTSC                       => (16#28#, 31,  0),
+      Current_Connect_Status           => (16#28#,  0,  0),
+      Port_Enable_Disable              => (16#28#,  1,  1),
+      Port_Reset                       => (16#28#,  4,  4),
+      Port_Link_State                  => (16#28#,  8,  5),
+      Port_Speed                       => (16#28#, 13, 10),
+      Connect_Status_Change            => (16#28#, 17, 17),
+      Port_Reset_Change                => (16#28#, 21, 21),
+      Port_Link_Status_Change          => (16#28#, 22, 22),
+      Port_Config_Error_Change         => (16#28#, 23, 23),
+      Context_Pointer_Lo               => (16#30#, 31,  0),
+      Context_Pointer_Hi               => (16#34#, 31,  0),
+      DbC_Protocol                     => (16#38#,  7,  0),
+      Vendor_ID                        => (16#38#, 31, 16),
+      Product_ID                       => (16#3c#, 15,  0),
+      Device_Revision                  => (16#3c#, 31, 16));
+   package Regs is new MMIO_Regs (MMIO, 0, S_Regs, Reg_Descs);
+
+   procedure Ring_Doorbell (EP : Endpoint_Range)
+   with
+      Pre => Regs.Byte_Offset /= 0;
+
+end HW.DbC;
+
+--  vim: set ts=8 sts=3 sw=3 et:
diff --git a/src/hw-dbc_config.ads.template b/src/hw-dbc_config.ads.template
new file mode 100644 (file)
index 0000000..83f0f8b
--- /dev/null
@@ -0,0 +1,8 @@
+package HW.DbC_Config is
+
+   DMA_BASE : constant := <<DMA_BASE>>;
+
+   XHCI_BASE : constant := <<XHCI_BASE>>;
+   XHCI_SIZE : constant := <<XHCI_SIZE>>;
+
+end HW.DbC_Config;