Add 'fw/' from commit '5b94dee9cf'

git-subtree-dir: fw
git-subtree-mainline: 6eddc61626
git-subtree-split: 5b94dee9cf
This commit is contained in:
jaseg 2021-03-02 19:27:52 +01:00
commit b328ef6059
63 changed files with 13005 additions and 0 deletions

15
fw/.gitignore vendored Normal file
View file

@ -0,0 +1,15 @@
*.o
*.d
*.elf
*.map
*.bin
*.hex
*.cproject
*.project
*.a
doc
Makefile
CMakeCache.txt
cmake_install.cmake
CMakeFiles
src/demo

3
fw/.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "libopencm3"]
path = libopencm3
url = https://amirhammad@github.com/amirhammad/libopencm3

17
fw/.travis.yml Normal file
View file

@ -0,0 +1,17 @@
language: c
compiler:
- gcc
before_install:
- sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key 6D1D8367A3421AFB
- sudo apt-get update -o Dir::Etc::sourcelist="sources.list.d/terry_guo-gcc-arm-embedded-precise.list" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
- sudo apt-get install gcc-arm-none-eabi
script:
- cd build && cmake $FLAGS_matrix .. && make
env:
matrix:
- FLAGS_matrix="-DUSE_USART_DEBUG=FALSE -DUSE_STM32F4_HS=TRUE -DUSE_STM32F4_FS=TRUE"
- FLAGS_matrix="-DUSE_USART_DEBUG=TRUE -DUSE_STM32F4_HS=TRUE -DUSE_STM32F4_FS=TRUE"
- FLAGS_matrix="-DUSE_USART_DEBUG=TRUE -DUSE_STM32F4_HS=FALSE -DUSE_STM32F4_FS=TRUE"
- FLAGS_matrix="-DUSE_USART_DEBUG=TRUE -DUSE_STM32F4_HS=TRUE -DUSE_STM32F4_FS=FALSE"

108
fw/CMakeLists.txt Normal file
View file

@ -0,0 +1,108 @@
cmake_minimum_required (VERSION 2.6)
# initialize compiler
include (cmake/toolchain.cmake)
# initialize flashing
include (cmake/openocd_flash.cmake)
# initilize doc
include (cmake/doc.cmake)
project (libusbhost C)
# Declare cached variables
set (USE_STM32F4_FS TRUE CACHE BOOL "Use USB full speed (FS) host periphery")
set (USE_STM32F4_HS TRUE CACHE BOOL "Use USB high speed (HS) host periphery")
set (USE_USART_DEBUG TRUE CACHE BOOL "Enable human-readable serial debug output")
set (DEBUG_USART USART1 CACHE STRING "USART to use for debug output")
set (DEBUG_USART_BAUDRATE 1000000 CACHE STRING "Baud rate to use for debug USART")
set (DEBUG_USART_DMA_NUM 2 CACHE STRING "DMA controller number to use for debug usart")
set (DEBUG_USART_DMA_STREAM_NUM 7 CACHE STRING "DMA stream number to use for debug usart. This must be the stream mapped to the [DEBUG_USART]_TX channel")
set (DEBUG_USART_DMA_CHANNEL_NUM 4 CACHE STRING "DMA channel number to use for debug usart. This must be the channel mapped to the [DEBUG_USART]_TX channel")
# Set compiler and linker flags
set (FP_FLAGS
"-mfloat-abi=hard -mfpu=fpv4-sp-d16 -mfp16-format=alternative"
)
set (ARCH_FLAGS
"-mthumb -mcpu=cortex-m4 ${FP_FLAGS}"
)
set (COMMON_FLAGS
"-O2 -g -Wextra -Wshadow -Wredundant-decls -fno-common -ffunction-sections -fdata-sections --specs=nosys.specs"
)
set (CMAKE_C_FLAGS
"${COMMON_FLAGS} ${ARCH_FLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wimplicit-function-declaration"
)
set (CMAKE_CXX_FLAGS
"${COMMON_FLAGS} ${ARCH_FLAGS} -Weffc++"
)
# C preprocessor flags
set (CPP_FLAGS
" -MD -Wall -Wundef"
)
add_definitions (${CPP_FLAGS})
# set platform
add_definitions (-DSTM32F4)
set (CMAKE_EXE_LINKER_FLAGS
"--static -nostartfiles -T${CMAKE_SOURCE_DIR}/libusbhost_stm32f4.ld -Wl,-Map=FIXME_ONE.map -Wl,--gc-sections -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group"
)
include_directories (
${CMAKE_SOURCE_DIR}/include
src/crypto/noise-c/include
)
function (init_libopencm3)
include_directories (${CMAKE_SOURCE_DIR}/libopencm3/include)
link_directories (${CMAKE_SOURCE_DIR}/libopencm3/lib)
set (LIBOPENCM3_LIB opencm3_stm32f4 PARENT_SCOPE)
execute_process (
COMMAND sh "${CMAKE_SOURCE_DIR}/initRepo.sh"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_QUIET
)
endfunction (init_libopencm3)
message (STATUS "Initializing repository")
init_libopencm3 ()
message (STATUS "Repository initialized")
# Process cached varibles
message (STATUS "Setuping build")
if (USE_STM32F4_FS)
message (STATUS "... Using USB full speed (FS) host periphery")
add_definitions (-DUSE_STM32F4_USBH_DRIVER_FS)
endif (USE_STM32F4_FS)
if (USE_STM32F4_HS)
message (STATUS "... Using USB high speed (HS) host periphery")
add_definitions (-DUSE_STM32F4_USBH_DRIVER_HS)
endif (USE_STM32F4_HS)
if (USE_USART_DEBUG)
message (STATUS "... Using debug uart output")
add_definitions (-DUSART_DEBUG)
endif (USE_USART_DEBUG)
message (STATUS "Setup done")
add_definitions (-DDEBUG_USART=${DEBUG_USART})
add_definitions (-DDEBUG_USART_BAUDRATE=${DEBUG_USART_BAUDRATE})
add_definitions (-DDEBUG_USART_DMA_NUM=${DEBUG_USART_DMA_NUM})
add_definitions (-DDEBUG_USART_DMA_STREAM_NUM=${DEBUG_USART_DMA_STREAM_NUM})
add_definitions (-DDEBUG_USART_DMA_CHANNEL_NUM=${DEBUG_USART_DMA_CHANNEL_NUM})
add_custom_target (README.md
SOURCES README.md
)
add_subdirectory (src)

674
fw/COPYING.GPL3 Normal file
View file

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. 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
them 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 prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. 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.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey 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;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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 that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
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.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
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.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
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
state 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 3 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, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program 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, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU 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. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

165
fw/COPYING.LGPL3 Normal file
View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser 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
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

2406
fw/Doxyfile Normal file

File diff suppressed because it is too large Load diff

105
fw/README.md Normal file
View file

@ -0,0 +1,105 @@
[![Build Status](https://travis-ci.org/libusbhost/libusbhost.svg?branch=master)](https://travis-ci.org/libusbhost/libusbhost)
##General Information
[Link to the official repository](http://github.com/libusbhost/libusbhost)
###Objectives
- provide open-source(Lesser GPL3) usb host library for embedded devices
- execution speed. This library doesn't use blocking sleep,
making low overhead on runtime performance
- use static allocation for all of its buffers.
This means no allocation and reallocation is affecting performance
(possibility of memory fragmentation. execution time indeterminism). No malloc(), realloc(), free()
- do not depend on any operating system
### Supported hardware
- stm32f4discovery
### Supported device drivers
- HUB
- Gamepad - XBox compatible Controller
- Generic Human Interface driver: mouse, keyboard (raw data)
- USB MIDI devices (raw data + note on/off)
## Steps to compile library and demo
### Prerequisities
Make sure the following prerequisities are installed to be able to compile this library
- **git** for libopencm3 submodule fetch
- **gcc-arm-none-eabi** toolchain for cross compilation
- **cmake**
- **ccmake** (optional)
- **openocd** (optional)
### Basic setup
1. go to build directory located in the root of the project
> cd build
2. compile demo and the library with the default options set
> cmake .. && make
Executable demo is placed into `build/demo.hex`.
Library is placed into `build/src/libusbhost.a`.
### Advanced setup
*cmake* initial cache variables
<table>
<tr>
<th>Cache variable</th><th>Value</th><th>Description</th>
</tr>
<tr>
<td>USE_STM32F4_FS</td><td>TRUE</td><td>Enable STM32F4 Full Speed USB host peripheral</td>
</tr>
<tr>
<td>USE_STM32F4_HS</td><td>TRUE</td><td>Enable STM32F4 High Speed USB host peripheral</td>
</tr>
<tr>
<td>USE_USART_DEBUG</td><td>TRUE</td><td>Enable writing of the debug information to USART6</td>
</tr>
<tr>
<td>OOCD_INTERFACE</td><td>"stlink-v2"</td><td>Interface configuration file used by the openocd</td>
</tr>
<tr>
<td>OOCD_BOARD</td><td>"stm32f4discovery"</td><td>Board configuration file used by the openocd</td>
</tr>
</table>
You can alter these by issuing the following commands in the build directory
- Graphical user interface
> ccmake ..
- Command line interface
> cmake .. -D{VARIABLE}={VALUE}
### Flashing
If the *openocd* is installed, `make flash` executed in the build directory
flashes the `build/demo.hex` to the stm32f4discovery board.
### Reading debug output
The following table represents the configuration of the debug output
<table>
<tr>
<th>GPIO</th><td>GPIOC6</td>
</tr>
<tr>
<th>USART periphery</th><td>USART6</td>
</tr>
<tr>
<th>Function</th><td>UART TX</td>
</tr>
<tr>
<th>Baud rate</th><td>921600</td>
</tr>
<tr>
<th>Uart mode</th><td>8N1</td>
</tr>
</table>
## License
The libusbhost code is released under the terms of the GNU Lesser General
Public License (LGPL), version 3 or later.
See COPYING.GPL3 and COPYING.LGPL3 for details.

88
fw/USB_icon.svg Normal file
View file

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
width="24"
height="24"
viewBox="0 0 24.000001 24"
id="Layer_1"
xml:space="preserve"
sodipodi:docname="USB_icon.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
inkscape:export-filename="/home/user/ref/libusbhost/secureusb_icon.png"
inkscape:export-xdpi="1280"
inkscape:export-ydpi="1280"><metadata
id="metadata7"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1030"
id="namedview5"
showgrid="false"
units="px"
inkscape:zoom="22.627417"
inkscape:cx="6.8983878"
inkscape:cy="13.626763"
inkscape:window-x="0"
inkscape:window-y="50"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><defs
id="defs1337" />
<path
d="M 9.1909916,1.0365848 6.7543191,5.2559831 H 8.4925226 V 15.009993 L 4.0559868,10.810749 C 3.7695331,10.453379 3.568585,9.9857932 3.5574545,9.5048004 c 0,-1.9458109 -5.065e-4,-1.7888256 -0.00136,-2.2141627 0.8214146,-0.2883098 1.414308,-1.0627543 1.414308,-1.983421 0,-1.1647037 -0.9451206,-2.1099103 -2.1102466,-2.1099103 -1.1656325,0 -2.11041486,0.9451205 -2.11041486,2.1099103 0,0.9206667 0.59255516,1.6951112 1.41329516,1.983421 l -5.905e-4,2.1728424 c 0,0.9446159 0.5182659,1.9344279 1.1258318,2.5644249 -0.018039,-0.01719 -0.037271,-0.03508 3.365e-4,10e-4 0.014994,0.01332 4.7065449,4.455256 4.7065449,4.455256 0.2860334,0.356613 0.4857161,0.823945 0.4971849,1.304601 v 8.752206 c -1.6117146,0.323388 -2.8260028,1.746635 -2.8260028,3.45389 0,1.946487 1.5778153,3.524302 3.5237111,3.524302 1.9464859,0 3.5243859,-1.577815 3.5243859,-3.524302 0,-1.707592 -1.2153,-3.130838 -2.8283611,-3.454226 v -8.709455 c 0,-0.0061 3.365e-4,-0.01224 0,-0.01856 v -5.301631 c 0.012235,-0.479729 0.2121621,-0.94647 0.4985321,-1.302745 0,0 4.691197,-4.4409224 4.706376,-4.4546676 0.03771,-0.035583 0.01805,-0.017872 3.38e-4,-3.365e-4 0.60748,-0.6299968 1.125408,-1.6202312 1.125408,-2.565013 L 16.215975,3.454345 h 1.414729 V -0.76611257 H 13.410639 V 3.4543809 h 1.412874 c 0,0 -0.0015,-1.7403408 -0.0015,0.7750052 -0.01102,0.4810766 -0.211742,0.9491696 -0.498196,1.306372 L 9.8863699,9.7359362 V 5.2559831 h 1.7409841 z"
id="path1334"
inkscape:connector-curvature="0"
style="stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:transform-center-x="3.8484292"
inkscape:transform-center-y="16.5482"
sodipodi:nodetypes="cccccccssscccccccssscscccccccccccccccccc" />
<g
id="g4661"
transform="matrix(0.95637878,0,0,0.95637878,49.375726,8.0349764)"
inkscape:export-xdpi="1280"
inkscape:export-ydpi="1280"><rect
ry="1.6849748"
y="6.75"
x="-39.125"
height="9.8125"
width="12.375"
id="rect4608"
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.8904658;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><rect
ry="3.9323201"
y="0.68743151"
x="-36.86982"
height="9.4375687"
width="7.8646402"
id="rect4610"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.69644809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><g
transform="matrix(0.93546514,0,0,0.93546514,-1.0854549,1.4271834)"
id="g4654"><circle
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.72307682;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4627"
cx="-34.051041"
cy="8.8928328"
r="1.84375" /><path
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m -34.754167,8.8303324 h 1.40625 l 1.140625,4.8258756 c 0.125,0.616281 -0.508719,1.140625 -1.140625,1.140625 h -1.40625 c -0.631906,0 -1.25,-0.508719 -1.140625,-1.140625 z"
id="rect4629"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccsscc" /></g></g><path
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M -21.203125 -21.580078 L -21.203125 45.580078 L 45.203125 45.580078 L 45.203125 -21.580078 L -21.203125 -21.580078 z M 0 0 L 24 0 L 24 24 L 0 24 L 0 0 z "
id="rect816" /></svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

34
fw/adjectives.py Normal file
View file

@ -0,0 +1,34 @@
ADJECTIVES = '''
wrathful worthy weird warm volatile veiled vacuous useless
upset unsoiled unsightly unpronounceable unfriendly unfree unfit unfaithful
unchaste unbroken unbound unblessed unbefitting unaltered unabused unable
ugly tongued thorny thirsty thick terminal ten-sided teeming
tangerine taken substantial stupefying stringy strange stillborn sticky
stagnant spongy sour soul-destroying smoldering smitten slain six-sided
shifting shadowy severed seven-sided serene salty rust-red royal
rotten riddled resentful regrettable reeking rare rank rancid
quiescent putrid putrid putrescent prehistoric predatory predaceous porous
poisonous pierced phlegmatic petrifying pessimal pathetic odorless oddish
obsessed obscene numb nine-sided nasty mysterious mute musky
morose moribund moldy miasmic material many-lobed malodorous malign
maimed luminescent low-cut lousy live limp lifeless leering
leaky layered latent lackluster jagged irregular iridescent intangible
infinite inept incomprehensible in-between improper idle hunted hideous
heavy hairy guilty grotesque grey greedy gory gorgeous
gooey golden-brown golden ghastly frostbitten fresh-cut freakish frantic
fossilized formless formidable floccose five-lobed firstborn filthy fickle
fetid fertile fearful fatal familiar fallen fallacious faint
faceless extinct esoteric errant emergent elastic eight-sided eerie
ebon dysphoric dying dumb dull-purple dull dull dull
dormant doomed disfigured dirty defenseless deep-pink deep deconsecrated
deathlike deadly dead dark-blue dark curly curious cured
cunning crystalline cryptic crying crumbly crimson crested creepy
crazy corrupt corporeal contemptible contained concrete cloudy chopped
chained caustic catholic cathartic captive cancerous cabalistic burnt
buoyant bronze-red bronze broken bright-red breathless bound bound
bottomless bony bodiless blue-lilac blue bloody bloodthirsty bloodsucking
bloodstained bloodcurdling blonde blistered blank bitter bilgy bewitched
befouled beardless bastardly barbed baleful balding awkward awful
atrocious arcane appalling antic anonymous angry ample ambiguous
amber-green amber aghast activated acidic abused abstruse abject
'''.split()

1
fw/build/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*

6
fw/cmake/doc.cmake Normal file
View file

@ -0,0 +1,6 @@
add_custom_target (doc
COMMAND doxygen
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "output is generted in the ${CMAKE_SOURCE_DIR}/doc"
SOURCES Doxyfile
)

View file

@ -0,0 +1,18 @@
find_program (OOCD openocd DOC "openocd executable")
set (OOCD_INTERFACE stlink-v2 CACHE STRING "interface config file used for openocd flashing")
set (OOCD_BOARD stm32f4discovery CACHE STRING "board config file used for openocd flashing")
if (OOCD)
message (STATUS "OpenOCD found: ${OOCD}")
message (STATUS "... interface: ${OOCD_INTERFACE}")
message (STATUS "... board: ${OOCD_BOARD}")
add_custom_target (flash
COMMAND sh -c '${OOCD} -f interface/${OOCD_INTERFACE}.cfg
-f board/${OOCD_BOARD}.cfg
-c "init" -c "reset init"
-c "flash write_image erase $<TARGET_FILE:demo>"
-c "reset"
-c "shutdown" '
DEPENDS demo
)
endif (OOCD)

15
fw/cmake/toolchain.cmake Normal file
View file

@ -0,0 +1,15 @@
set (_CMAKE_TOOLCHAIN_PREFIX "arm-none-eabi-" CACHE STRING "toolchain prefix")
set (_CMAKE_TOOLCHAIN_LOCATION "" CACHE STRING "toolchain location hint")
set (CMAKE_SYSTEM_NAME Generic)
set (CMAKE_C_COMPILER_WORKS 1)
set (CMAKE_CXX_COMPILER_WORKS 1)
set (CMAKE_C_FLAGS "")
set (CMAKE_CXX_FLAGS "")
set (BUILD_SHARED_LIBS OFF)
find_program (CMAKE_C_COMPILER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}gcc HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
find_program (CMAKE_CXX_COMPILER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}g++ HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
find_program (CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
find_program (CMAKE_SIZE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}size HINTS ${_CMAKE_TOOLCHAIN_LOCATION})

54
fw/hexcom.py Executable file
View file

@ -0,0 +1,54 @@
#!/usr/bin/env python3
import time
import string
def _print_line(write, ts, line, width=16):
h,m,s,ms = int(ts//3600), int((ts//60)%60), int(ts%60), int((ts%1.0) * 1000)
timestamp = f'{h: 3d}:{m:02d}:{s:02d}:{ms:03d}'
line = list(line) + [None]*(width-len(line))
hexcol = '\033[94m'
col = lambda b, s: s if b != 0 else f'\033[91m{s}{hexcol}'
hexfmt = ' '.join(
' '.join(col(b, f'{b:02x}') if b is not None else ' ' for b in line[i*8:i*8+8])
for i in range(1 + (len(line)-1)//8))
asciifmt = ''.join(chr(c) if c is not None and chr(c) in string.printable else '.' for c in line)
write(f'\033[38;5;244m{timestamp} {hexcol}{hexfmt} \033[38;5;244m|\033[92m{asciifmt}\033[38;5;244m|\033[0m', flush=True, end='')
def hexcom(write, ser, width=16, split=False):
current_line = b''
start = time.time()
while ser.is_open:
data = ser.read_all() # non-blocking, flushes buffer
if not data:
data = ser.read(1) # blocking
ts = time.time()
write('\033[2K\r', end='')
current_line += data
foo = current_line.split(b'\0') if split else [current_line]
for i, packet in enumerate(foo):
if len(foo) > 1 and i < len(foo)-1:
packet += b'\0'
while len(packet) > width:
chunk, packet = packet[:width], packet[width:]
_print_line(write, ts-start, chunk, width=width)
write()
_print_line(write, ts-start, packet, width=width)
if i < len(foo)-1:
write()
current_line = packet
if __name__ == '__main__':
import argparse
import serial
parser = argparse.ArgumentParser()
parser.add_argument('serial')
parser.add_argument('baudrate')
parser.add_argument('-w', '--width', type=int, default=16, help='Number of bytes to display in one line')
parser.add_argument('-s', '--split', action='store_true', help='Split output on null bytes')
args = parser.parse_args()
ser = serial.Serial(args.serial, args.baudrate)
hexcom(print, ser, width=args.width, split=args.split)

27
fw/hexdump.py Normal file
View file

@ -0,0 +1,27 @@
import string
import time
startup = time.time()
def _print_line(write, ts, line, width=16):
h,m,s,ms = int(ts//3600), int((ts//60)%60), int(ts%60), int((ts%1.0) * 1000)
timestamp = f'{h: 3d}:{m:02d}:{s:02d}:{ms:03d}'
line = list(line) + [None]*(width-len(line))
hexcol = '\033[0m'
col = lambda b, s: s if b != 0 else f'\033[91m{s}{hexcol}'
hexfmt = ' '.join(
' '.join(col(b, f'{b:02x}') if b is not None else ' ' for b in line[i*8:i*8+8])
for i in range(1 + (len(line)-1)//8))
asciifmt = ''.join(chr(c) if c is not None and chr(c) in string.printable and c>=0x20 else '.' for c in line)
write(f'\033[38;5;244m{timestamp} {hexcol}{hexfmt} \033[38;5;244m|\033[92m{asciifmt}\033[38;5;244m|\033[0m', flush=True, end='')
def hexdump(write, packet, width=16):
ts = time.time()
while len(packet) > width:
chunk, packet = packet[:width], packet[width:]
_print_line(write, ts-startup, chunk, width=width)
write()
_print_line(write, ts-startup, packet, width=width)
write()

384
fw/hexnoise.py Executable file
View file

@ -0,0 +1,384 @@
#!/usr/bin/env python3
import time
import enum
import sys
from contextlib import contextmanager, suppress, wraps
import hashlib
import secrets
import serial
from cobs import cobs
import uinput
from noise.connection import NoiseConnection, Keypair
from noise.exceptions import NoiseInvalidMessage
import keymap
from hexdump import hexdump
class PacketType(enum.Enum):
_RESERVED = 0
INITIATE_HANDSHAKE = 1
HANDSHAKE = 2
DATA = 3
COMM_ERROR = 4
CRYPTO_ERROR = 5
TOO_MANY_FAILS = 6
class ReportType(enum.Enum):
_RESERVED = 0
KEYBOARD = 1
MOUSE = 2
PAIRING_INPUT = 3
PAIRING_SUCCESS = 4
PAIRING_ERROR = 5
PAIRING_START = 6
class ProtocolError(Exception):
pass
class Packetizer:
def __init__(self, serial, debug=False, width=16):
self.ser, self.debug, self.width = serial, debug, width
self.ser.write(b'\0') # COBS synchronization
def send_packet(self, pkt_type, data):
if self.debug:
print(f'\033[93mSending {len(data)} bytes, packet type {pkt_type.name} ({pkt_type.value})\033[0m')
hexdump(print, data, self.width)
data = bytes([pkt_type.value]) + data
encoded = cobs.encode(data) + b'\0'
self.ser.write(encoded)
self.ser.flushOutput()
def receive_packet(self):
packet = self.ser.read_until(b'\0')
data = cobs.decode(packet[:-1])
if self.debug:
print(f'\033[93mReceived {len(data)} bytes\033[0m')
hexdump(print, data, self.width)
pkt_type, data = PacketType(data[0]), data[1:]
if pkt_type is PacketType.COMM_ERROR:
raise ProtocolError('Device-side serial communication error')
elif pkt_type is PacketType.CRYPTO_ERROR:
raise ProtocolError('Device-side cryptographic error')
elif pkt_type is PacketType.TOO_MANY_FAILS:
raise ProtocolError('Device reports too many failed handshake attempts')
else:
return pkt_type, data
class KeyMapper:
Keycode = enum.Enum('Keycode', start=0, names='''
KEY_NONE _RESERVED_0x01 _RESERVED_0x02 _RESERVED_0x03 KEY_A KEY_B KEY_C KEY_D
KEY_E KEY_F KEY_G KEY_H KEY_I KEY_J KEY_K KEY_L
KEY_M KEY_N KEY_O KEY_P KEY_Q KEY_R KEY_S KEY_T
KEY_U KEY_V KEY_W KEY_X KEY_Y KEY_Z KEY_1 KEY_2
KEY_3 KEY_4 KEY_5 KEY_6 KEY_7 KEY_8 KEY_9 KEY_0
KEY_ENTER KEY_ESC KEY_BACKSPACE KEY_TAB KEY_SPACE KEY_MINUS KEY_EQUAL KEY_LEFTBRACE
KEY_RIGHTBRACE KEY_BACKSLASH KEY_HASH KEY_SEMICOLON KEY_APOSTROPHE KEY_GRAVE KEY_COMMA KEY_DOT
KEY_SLASH KEY_CAPSLOCK KEY_F1 KEY_F2 KEY_F3 KEY_F4 KEY_F5 KEY_F6
KEY_F7 KEY_F8 KEY_F9 KEY_F10 KEY_F11 KEY_F12 KEY_SYSRQ KEY_SCROLLLOCK
KEY_PAUSE KEY_INSERT KEY_HOME KEY_PAGEUP KEY_DELETE KEY_END KEY_PAGEDOWN KEY_RIGHT
KEY_LEFT KEY_DOWN KEY_UP KEY_NUMLOCK KEY_KPSLASH KEY_KPASTERISK KEY_KPMINUS KEY_KPPLUS
KEY_KPENTER KEY_KP1 KEY_KP2 KEY_KP3 KEY_KP4 KEY_KP5 KEY_KP6 KEY_KP7
KEY_KP8 KEY_KP9 KEY_KP0 KEY_KPDOT KEY_102ND KEY_COMPOSE KEY_POWER KEY_KPEQUAL
KEY_F13 KEY_F14 KEY_F15 KEY_F16 KEY_F17 KEY_F18 KEY_F19 KEY_F20
KEY_F21 KEY_F22 KEY_F23 KEY_F24 KEY_OPEN KEY_HELP KEY_PROPS KEY_FRONT
KEY_STOP KEY_AGAIN KEY_UNDO KEY_CUT KEY_COPY KEY_PASTE KEY_FIND KEY_MUTE
KEY_VOLUMEUP KEY_VOLUMEDOWN _RESERVED_0x82 _RESERVED_0x83 _RESERVED_0x84 KEY_KPCOMMA _RESERVED_0x86 KEY_RO
KEY_KATAKANAHIRAGANA KEY_YEN KEY_HENKAN KEY_MUHENKAN KEY_KPJPCOMMA _RESERVED_0x8D _RESERVED_0x8E _RESERVED_0x8F
KEY_HANGEUL KEY_HANJA KEY_KATAKANA KEY_HIRAGANA KEY_ZENKAKUHANKAKU _RESERVED_0x95 _RESERVED_0x96 _RESERVED_0x97
_RESERVED_0x98 _RESERVED_0x99 _RESERVED_0x9A _RESERVED_0x9B _RESERVED_0x9C _RESERVED_0x9D _RESERVED_0x9E _RESERVED_0x9F
_RESERVED_0xA0 _RESERVED_0xA1 _RESERVED_0xA2 _RESERVED_0xA3 _RESERVED_0xA4 _RESERVED_0xA5 _RESERVED_0xA6 _RESERVED_0xA7
_RESERVED_0xA8 _RESERVED_0xA9 _RESERVED_0xAA _RESERVED_0xAB _RESERVED_0xAC _RESERVED_0xAD _RESERVED_0xAE _RESERVED_0xAF
_RESERVED_0xB0 _RESERVED_0xB1 _RESERVED_0xB2 _RESERVED_0xB3 _RESERVED_0xB4 _RESERVED_0xB5 KEY_KPLEFTPAREN KEY_KPRIGHTPAREN
_RESERVED_0xB8 _RESERVED_0xB9 _RESERVED_0xBA _RESERVED_0xBB _RESERVED_0xBC _RESERVED_0xBD _RESERVED_0xBE _RESERVED_0xBF
_RESERVED_0xC0 _RESERVED_0xC1 _RESERVED_0xC2 _RESERVED_0xC3 _RESERVED_0xC4 _RESERVED_0xC5 _RESERVED_0xC6 _RESERVED_0xC7
_RESERVED_0xC8 _RESERVED_0xC9 _RESERVED_0xCA _RESERVED_0xCB _RESERVED_0xCC _RESERVED_0xCD _RESERVED_0xCE _RESERVED_0xCF
_RESERVED_0xD0 _RESERVED_0xD1 _RESERVED_0xD2 _RESERVED_0xD3 _RESERVED_0xD4 _RESERVED_0xD5 _RESERVED_0xD6 _RESERVED_0xD7
_RESERVED_0xD8 _RESERVED_0xD9 _RESERVED_0xDA _RESERVED_0xDB _RESERVED_0xDC _RESERVED_0xDD _RESERVED_0xDE _RESERVED_0xDF
_RESERVED_0xE0 _RESERVED_0xE1 _RESERVED_0xE2 _RESERVED_0xE3 _RESERVED_0xE4 _RESERVED_0xE5 _RESERVED_0xE6 _RESERVED_0xE7
_RESERVED_0xE8 _RESERVED_0xE9 _RESERVED_0xEA _RESERVED_0xEB _RESERVED_0xEC _RESERVED_0xED _RESERVED_0xEE _RESERVED_0xEF
_RESERVED_0xF0 _RESERVED_0xF1 _RESERVED_0xF2 _RESERVED_0xF3 _RESERVED_0xF4 _RESERVED_0xF5 _RESERVED_0xF6 _RESERVED_0xF7
_RESERVED_0xF8 _RESERVED_0xF9 _RESERVED_0xFA _RESERVED_0xFB _RESERVED_0xFC _RESERVED_0xFD _RESERVED_0xFE _RESERVED_0xFF
''')
MODIFIERS = [ uinput.ev.KEY_LEFTCTRL, uinput.ev.KEY_LEFTSHIFT, uinput.ev.KEY_LEFTALT, uinput.ev.KEY_LEFTMETA,
uinput.ev.KEY_RIGHTCTRL, uinput.ev.KEY_RIGHTSHIFT, uinput.ev.KEY_RIGHTALT, uinput.ev.KEY_RIGHTMETA ]
ALL_KEYS = [ v for k, v in uinput.ev.__dict__.items() if k.startswith('KEY_') ]
REGULAR_MAP = { kc.value: getattr(uinput.ev, kc.name) for kc in Keycode if hasattr(uinput.ev, kc.name) }
@classmethod
def map_modifiers(kls, val):
return [ mod for i, mod in enumerate(kls.MODIFIERS) if val & (1<<i) ]
@classmethod
def map_regulars(kls, keycodes):
return [ kls.REGULAR_MAP[kc] for kc in keycodes if kc != 0 and kc in kls.REGULAR_MAP ]
class Magic:
@classmethod
def map_bytes_to_incantation(kls, data):
elems = [ f'{kls.ADJECTIVES[a]} {kls.NOUNS[b]}' for a, b in zip(data[0::2], data[1::2]) ]
nfirst = ", ".join(elems[:-1])
return f'{nfirst} and {elems[-1]}'
EVEN = '''
aardvark absurd accrue acme adrift adult afflict ahead
aimless Algol allow alone ammo ancient apple artist
assume Athens atlas Aztec baboon backfield backward banjo
beaming bedlamp beehive beeswax befriend Belfast berserk billiard
bison blackjack blockade blowtorch bluebird bombast bookshelf brackish
breadline breakup brickyard briefcase Burbank button buzzard cement
chairlift chatter checkup chisel choking chopper Christmas clamshell
classic classroom cleanup clockwork cobra commence concert cowbell
crackdown cranky crowfoot crucial crumpled crusade cubic dashboard
deadbolt deckhand dogsled dragnet drainage dreadful drifter dropper
drumbeat drunken Dupont dwelling eating edict egghead eightball
endorse endow enlist erase escape exceed eyeglass eyetooth
facial fallout flagpole flatfoot flytrap fracture framework freedom
frighten gazelle Geiger glitter glucose goggles goldfish gremlin
guidance hamlet highchair hockey indoors indulge inverse involve
island jawbone keyboard kickoff kiwi klaxon locale lockup
merit minnow miser Mohawk mural music necklace Neptune
newborn nightbird Oakland obtuse offload optic orca payday
peachy pheasant physique playhouse Pluto preclude prefer preshrunk
printer prowler pupil puppy python quadrant quiver quota
ragtime ratchet rebirth reform regain reindeer rematch repay
retouch revenge reward rhythm ribcage ringbolt robust rocker
ruffled sailboat sawdust scallion scenic scorecard Scotland seabird
select sentence shadow shamrock showgirl skullcap skydive slingshot
slowdown snapline snapshot snowcap snowslide solo southward soybean
spaniel spearhead spellbind spheroid spigot spindle spyglass stagehand
stagnate stairway standard stapler steamship sterling stockman stopwatch
stormy sugar surmount suspense sweatband swelter tactics talon
tapeworm tempest tiger tissue tonic topmost tracker transit
trauma treadmill Trojan trouble tumor tunnel tycoon uncut
unearth unwind uproot upset upshot vapor village virus
Vulcan waffle wallet watchword wayside willow woodlark Zulu
'''.split()
ODD = '''
adroitness adviser aftermath aggregate alkali almighty amulet amusement
antenna applicant Apollo armistice article asteroid Atlantic atmosphere
autopsy Babylon backwater barbecue belowground bifocals bodyguard bookseller
borderline bottomless Bradbury bravado Brazilian breakaway Burlington businessman
butterfat Camelot candidate cannonball Capricorn caravan caretaker celebrate
cellulose certify chambermaid Cherokee Chicago clergyman coherence combustion
commando company component concurrent confidence conformist congregate consensus
consulting corporate corrosion councilman crossover crucifix cumbersome customer
Dakota decadence December decimal designing detector detergent determine
dictator dinosaur direction disable disbelief disruptive distortion document
embezzle enchanting enrollment enterprise equation equipment escapade Eskimo
everyday examine existence exodus fascinate filament finicky forever
fortitude frequency gadgetry Galveston getaway glossary gossamer graduate
gravity guitarist hamburger Hamilton handiwork hazardous headwaters hemisphere
hesitate hideaway holiness hurricane hydraulic impartial impetus inception
indigo inertia infancy inferno informant insincere insurgent integrate
intention inventive Istanbul Jamaica Jupiter leprosy letterhead liberty
maritime matchmaker maverick Medusa megaton microscope microwave midsummer
millionaire miracle misnomer molasses molecule Montana monument mosquito
narrative nebula newsletter Norwegian October Ohio onlooker opulent
Orlando outfielder Pacific pandemic Pandora paperweight paragon paragraph
paramount passenger pedigree Pegasus penetrate perceptive performance pharmacy
phonetic photograph pioneer pocketful politeness positive potato processor
provincial proximate puberty publisher pyramid quantity racketeer rebellion
recipe recover repellent replica reproduce resistor responsive retraction
retrieval retrospect revenue revival revolver sandalwood sardonic Saturday
savagery scavenger sensation sociable souvenir specialist speculate stethoscope
stupendous supportive surrender suspicious sympathy tambourine telephone therapist
tobacco tolerance tomorrow torpedo tradition travesty trombonist truncated
typewriter ultimate undaunted underfoot unicorn unify universe unravel
upcoming vacancy vagabond vertigo Virginia visitor vocalist voyager
warranty Waterloo whimsical Wichita Wilmington Wyoming yesteryear Yucatan
'''.split()
class NoiseEngine:
def __init__(self, host_key, packetizer, debug=False):
self.debug = debug
self.packetizer = packetizer
self.static_local = host_key
self.proto = NoiseConnection.from_name(b'Noise_XX_25519_ChaChaPoly_BLAKE2s')
self.proto.set_as_initiator()
self.proto.set_keypair_from_private_bytes(Keypair.STATIC, self.static_local)
self.proto.start_handshake()
self.handshake = self.proto.noise_protocol.handshake_state # save for later because someone didn't think
self.paired = False
self.connected = False
@property
def remote_fingerprint(self):
''' Return the SHA-256 hash of the remote static key (rs). This can be used to fingerprint the remote party. '''
return hashlib.sha256(self.handshake.rs.public_bytes).hexdigest()
@classmethod
def generate_private_key_x25519(kls):
# This is taken from noise-c's reference implementation. This would not be needed had not cryptography/hazmat
# decided noone would ever need serialized x25519 private keys and noiseprotocol stopped just short of implementing
# key generation (who'd need that anyway, amiright?) -.-
key = list(secrets.token_bytes(32))
key[0] &= 0xF8
key[31] = (key[31] & 0x7F) | 0x40
return bytes(key)
@wraps(print)
def debug_print(self, *args, **kwargs):
if self.debug:
print(*args, **kwargs)
def perform_handshake(self):
self.packetizer.send_packet(PacketType.INITIATE_HANDSHAKE, b'')
self.debug_print('Handshake started')
while True:
if self.proto.handshake_finished:
break
self.packetizer.send_packet(PacketType.HANDSHAKE, self.proto.write_message())
if self.proto.handshake_finished:
break
pkt_type, payload = self.packetizer.receive_packet()
if pkt_type is PacketType.HANDSHAKE:
self.proto.read_message(payload)
else:
raise ProtocolError(f'Incorrect packet type {pkt_type}. Ignoring since this is only test code.')
msg_type, payload = self.packetizer.receive_packet()
rtype, data = self._decrypt(payload)
if rtype is ReportType.PAIRING_SUCCESS:
self.connected, self.paired = True, True
elif rtype is ReportType.PAIRING_START:
self.connected, self.paired = True, False
else:
self.connected, self.paired = True, False
raise UserWarning(f'Unexpected record type {rtype} in {msg_type} packet. Ignoring.')
if self.debug:
print('Handshake finished, handshake hash:')
hexdump(print, self.proto.get_handshake_hash())
def channel_binding_incantation(self):
hhash = self.proto.get_handshake_hash()
return '\n'.join(Magic.map_bytes_to_incantation(hhash[i:i+8]) for i in range(0, 16, 8))
def receive_loop(self):
while True:
try:
pkt_type, received = self.packetizer.receive_packet()
except Exception as e:
self.debug_print('Invalid framing:', e)
if pkt_type is not PacketType.DATA:
raise UserWarning(f'Unexpected packet type {pkt_type}. Ignoring.')
continue
rtype, data = self._decrypt(received)
if self.debug:
print(f'Decrypted packet {rtype} ({rtype.value}):')
hexdump(print, data)
yield rtype, data
def _decrypt(self, received):
try:
data = self.proto.decrypt(received)
return ReportType(data[0]), data[1:]
except NoiseInvalidMessage as e:
self.debug_print('Invalid noise message', e)
for i in range(3):
with self._nonce_lookahead() as set_nonce:
set_nonce(i)
data = self.proto.decrypt(received)
return ReportType(data[0]), data[1:]
else:
self.debug_print(' Unrecoverable.')
raise e
self.debug_print(f' Recovered. n={n}')
@contextmanager
def _nonce_lookahead(self):
nold = self.proto.noise_protocol.cipher_state_decrypt.n
def setter(n):
self.proto.noise_protocol.cipher_state_decrypt.n = nold + n
with suppress(NoiseInvalidMessage):
yield setter
self.proto.noise_protocol.cipher_state_decrypt.n = nold
def pairing_messages(self):
user_input = ''
for msg_type, payload in self.receive_loop():
if msg_type is ReportType.PAIRING_INPUT:
ch = chr(payload[0])
if ch == '\b':
user_input = user_input[:-1]
else:
user_input += ch
yield user_input
elif msg_type is ReportType.PAIRING_SUCCESS:
self.paired = True
break
elif msg_type is ReportType.PAIRING_ERROR:
raise ProtocolError('Device-side pairing error') # FIXME find better exception subclass here
else:
raise ProtocolError('Invalid report type')
def uinput_passthrough(self):
with uinput.Device(KeyMapper.ALL_KEYS) as ui:
old_kcs = set()
for msg_type, payload in self.receive_loop():
report_len, *report = payload
if report_len != 8:
raise ValueError('Unsupported report length', report_len)
if msg_type is ReportType.KEYBOARD:
modbyte, _reserved, *keycodes = report
import binascii
keys = { *KeyMapper.map_modifiers(modbyte), *KeyMapper.map_regulars(keycodes) }
if self.debug:
print('Emitting:', keys)
for key in keys - old_kcs:
ui.emit(key, 1, syn=False)
for key in old_kcs - keys:
ui.emit(key, 0, syn=False)
ui.syn()
old_kcs = keys
elif msg_type is ReportType.MOUSE:
# FIXME unhandled
pass
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('serial')
parser.add_argument('baudrate')
parser.add_argument('-w', '--width', type=int, default=16, help='Number of bytes to display in one line')
parser.add_argument('-d', '--debug', action='store_true')
args = parser.parse_args()
ser = serial.Serial(args.serial, args.baudrate)
packetizer = Packetizer(ser, debug=args.debug, width=args.width)
noise = NoiseEngine(packetizer, debug=args.debug)
noise.perform_handshake()
print('Handshake channel binding incantation:')
print(noise.channel_binding_incantation())
for user_input in noise.pairing_messages():
if not args.debug:
print('\033[2K\r', end='')
print('Pairing input:', user_input, end='' if not args.debug else '\n', flush=True)
print()
print('Pairing success')
noise.uinput_passthrough()

27
fw/include/cobs.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef __COBS_H__
#define __COBS_H__
#include <stdint.h>
#include <unistd.h>
#include <string.h>
struct cobs_decode_state {
size_t p;
size_t c;
};
ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen);
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen);
int cobs_encode_incremental(void *f, int (*output)(void *f, unsigned char c), unsigned char *src, size_t srclen);
/*@ requires \valid(state);
ensures state->p == 0 && state->c == 0;
assigns *state;
@*/
void cobs_decode_incremental_initialize(struct cobs_decode_state *state);
int cobs_decode_incremental(struct cobs_decode_state *state, unsigned char *dst, size_t dstlen, unsigned char src);
#endif//__COBS_H__

View file

@ -0,0 +1,326 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DEVICE_DRIVER_
#define USBH_DEVICE_DRIVER_
#include "usbh_config.h"
#include "usbh_core.h"
#include <libopencm3/usb/usbstd.h>
#include <stdint.h>
BEGIN_DECLS
enum USBH_ENDPOINT_TYPE {
USBH_ENDPOINT_TYPE_CONTROL = 0,
USBH_ENDPOINT_TYPE_ISOCHRONOUS = 1,
USBH_ENDPOINT_TYPE_BULK = 2,
USBH_ENDPOINT_TYPE_INTERRUPT = 3,
};
enum USBH_SPEED {
USBH_SPEED_FULL = 0,
USBH_SPEED_LOW = 1,
USBH_SPEED_HIGH = 2,
};
enum USBH_PACKET_CALLBACK_STATUS {
USBH_PACKET_CALLBACK_STATUS_OK = 0,
USBH_PACKET_CALLBACK_STATUS_ERRSIZ = 1,
USBH_PACKET_CALLBACK_STATUS_EAGAIN = 2, // -- TODO: automatic handling of transmit errors 3xTXERR->FATAL
USBH_PACKET_CALLBACK_STATUS_EFATAL = 3
};
enum USBH_POLL_STATUS {
USBH_POLL_STATUS_NONE,
USBH_POLL_STATUS_DEVICE_CONNECTED,
USBH_POLL_STATUS_DEVICE_DISCONNECTED
};
enum USBH_CONTROL_TYPE {
USBH_CONTROL_TYPE_SETUP,
USBH_CONTROL_TYPE_DATA
};
enum USBH_ENUM_STATE {
USBH_ENUM_STATE_SET_ADDRESS,
USBH_ENUM_STATE_FIRST = USBH_ENUM_STATE_SET_ADDRESS,
USBH_ENUM_STATE_DEVICE_DT_READ_SETUP,
USBH_ENUM_STATE_DEVICE_DT_READ_COMPLETE,
USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_SETUP,
USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ,
USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_COMPLETE,
USBH_ENUM_STATE_CONFIGURATION_DT_READ_SETUP,
USBH_ENUM_STATE_CONFIGURATION_DT_READ,
USBH_ENUM_STATE_CONFIGURATION_DT_READ_COMPLETE,
USBH_ENUM_STATE_SET_CONFIGURATION_SETUP,
USBH_ENUM_STATE_SET_CONFIGURATION_COMPLETE,
USBH_ENUM_STATE_FIND_DRIVER,
};
enum USBH_CONTROL_STATE {
USBH_CONTROL_STATE_NONE,
USBH_CONTROL_STATE_SETUP,
USBH_CONTROL_STATE_DATA,
USBH_CONTROL_STATE_STATUS,
};
typedef struct _usbh_device usbh_device_t;
struct _usbh_packet_callback_data {
/// status - it is used for reporting of the errors
enum USBH_PACKET_CALLBACK_STATUS status;
/// count of bytes that has been actually transferred
uint32_t transferred_length;
};
typedef struct _usbh_packet_callback_data usbh_packet_callback_data_t;
typedef void (*usbh_packet_callback_t)(usbh_device_t *dev, usbh_packet_callback_data_t status);
struct _usbh_control {
enum USBH_CONTROL_STATE state;
usbh_packet_callback_t callback;
union {
const void *out;
void *in;
} data;
uint16_t data_length;
struct usb_setup_data setup_data;
};
typedef struct _usbh_control usbh_control_t;
/**
* @brief The _usbh_device struct
*
* This represents exactly one connected device.
*/
struct _usbh_device {
/// max packet size for control endpoint(0)
uint16_t packet_size_max0;
/// Device's address
int8_t address;
/// @see USBH_SPEED
enum USBH_SPEED speed;
/// state used for enumeration purposes
enum USBH_ENUM_STATE state;
usbh_control_t control;
/// toggle bit
uint8_t toggle0;
/**
* @brief drv - device driver used for this connected device
*/
const usbh_dev_driver_t *drv;
/**
* @brief drvdata - device driver's private data
*/
void *drvdata;
/**
* @brief lld - pointer to a low-level driver's instance
*/
const void *lld;
};
typedef struct _usbh_device usbh_device_t;
struct _usbh_packet {
/// pointer to data
union {
const void *out;
void *in;
} data;
/// length of the data (up to 1023)
uint16_t datalen;
/// Device's address
int8_t address;
/**
* @brief Endpoint type
* @see USBH_ENDPOINT_TYPE
*/
enum USBH_ENDPOINT_TYPE endpoint_type;
enum USBH_CONTROL_TYPE control_type;
/// Endpoint number 0..15
uint8_t endpoint_address;
/// Max packet size for an endpoint
uint16_t endpoint_size_max;
/// @see USBH_SPEED
enum USBH_SPEED speed;
uint8_t *toggle;
/**
* @brief callback this will be called when the packet is finished - either successfuly or not.
*/
usbh_packet_callback_t callback;
/**
* @brief callback_arg argument passed into callback
*
* Low level driver is not allowed to alter the data pointed by callback_arg
*/
void *callback_arg;
};
typedef struct _usbh_packet usbh_packet_t;
struct _usbh_low_level_driver {
/**
* @brief init initialization routine of the low-level driver
*
*
* This function is called during the initialization of the library
*
* @see usbh_init()
*/
void (*init)(void *drvdata);
/**
* write - perform a write to a device
* @see usbh_packet_t
*/
void (*write)(void *drvdata, const usbh_packet_t *packet);
/**
* @brief read - perform a read from a device
* @see usbh_packet_t
*/
void (*read)(void *drvdata, usbh_packet_t *packet);
/**
* @brief this is called as a part of @ref usbh_poll() routine
*/
enum USBH_POLL_STATUS (*poll)(void *drvdata, uint32_t time_curr_us);
/**
* @brief speed of the low-level bus
*/
enum USBH_SPEED (*root_speed)(void *drvdata);
/**
* @brief Pointer to Low-level driver data
*
* Data pointed by this pointer should not be altered by the logic other from low-level driver's logic
*/
void *driver_data;
};
typedef struct _usbh_low_level_driver usbh_low_level_driver_t;
struct _usbh_generic_data {
usbh_device_t usbh_device[USBH_MAX_DEVICES];
uint8_t usbh_buffer[BUFFER_ONE_BYTES];
};
typedef struct _usbh_generic_data usbh_generic_data_t;
/// set to -1 for unused items ("don't care" functionality) @see find_driver()
struct _usbh_dev_driver_info {
int32_t deviceClass;
int32_t deviceSubClass;
int32_t deviceProtocol;
int32_t idVendor;
int32_t idProduct;
int32_t ifaceClass;
int32_t ifaceSubClass;
int32_t ifaceProtocol;
};
typedef struct _usbh_dev_driver_info usbh_dev_driver_info_t;
struct _usbh_dev_driver {
/**
* @brief init is initialization routine of the device driver
*
* This function is called during the initialization of the device driver
*/
void *(*init)(usbh_device_t *usbh_dev);
/**
* @brief analyze descriptor
* @param[in/out] drvdata is the device driver's private data
* @param[in] descriptor is the pointer to the descriptor that should
* be parsed in order to prepare driver to be loaded
*
* @retval true when the enumeration is complete and the driver is ready to be used
* @retval false when the device driver is not ready to be used
*
* This should be used for getting correct endpoint numbers, getting maximum sizes of endpoints.
* Should return true, when no more data is needed.
*
*/
bool (*analyze_descriptor)(void *drvdata, void *descriptor);
/**
* @brief poll method is called periodically by the library core
* @param[in/out] drvdata is the device driver's private data
* @param[in] time_curr_us current timestamp in microseconds
* @see usbh_poll()
*/
void (*poll)(void *drvdata, uint32_t time_curr_us);
/**
* @brief unloads the device driver
* @param[in/out] drvdata is the device driver's private data
*
* This should free any data associated with this device
*/
void (*remove)(void *drvdata);
/**
* @brief info - compatibility information about the driver. It is used by the core during device enumeration
* @see find_driver()
*/
const usbh_dev_driver_info_t * const info;
};
typedef struct _usbh_dev_driver usbh_dev_driver_t;
#define ERROR(arg) LOG_PRINTF("UNHANDLED_ERROR %d: file: %s, line: %d",\
arg, __FILE__, __LINE__)
/* Hub related functions */
usbh_device_t *usbh_get_free_device(const usbh_device_t *dev);
bool usbh_enum_available(void);
void device_enumeration_start(usbh_device_t *dev);
/* All devices functions */
void usbh_read(usbh_device_t *dev, usbh_packet_t *packet);
void usbh_write(usbh_device_t *dev, const usbh_packet_t *packet);
/* Helper functions used by device drivers */
void device_control(usbh_device_t *dev, usbh_packet_callback_t callback, const struct usb_setup_data *setup_data, void *data);
void device_remove(usbh_device_t *dev);
END_DECLS
#endif

62
fw/include/usbh_config.h Normal file
View file

@ -0,0 +1,62 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_CONFIG_
#define USBH_CONFIG_
// Max devices per hub
#define USBH_HUB_MAX_DEVICES (8)
// Max number of hub instancies
#define USBH_MAX_HUBS (2)
// Max devices
#define USBH_MAX_DEVICES (15)
// Min: 128
// Set this wisely
#define BUFFER_ONE_BYTES (2048)
// HID class devices
#define USBH_HID_MAX_DEVICES (2)
#define USBH_HID_BUFFER (256)
#define USBH_HID_REPORT_BUFFER (4)
// MIDI
// Maximal number of midi devices connected to whatever hub
#define USBH_AC_MIDI_MAX_DEVICES (4)
#define USBH_AC_MIDI_BUFFER (64)
// Gamepad XBOX
#define USBH_GP_XBOX_MAX_DEVICES (2)
#define USBH_GP_XBOX_BUFFER (32)
/* Sanity checks */
#if (USBH_MAX_DEVICES > 127)
#error USBH_MAX_DEVICES > 127
#endif
#endif

65
fw/include/usbh_core.h Normal file
View file

@ -0,0 +1,65 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_CORE_
#define USBH_CORE_
#include "usbh_config.h"
#include <stdint.h>
#include <stdbool.h>
/* This must be placed around external function declaration for C++
* support. */
#ifdef __cplusplus
# define BEGIN_DECLS extern "C" {
# define END_DECLS }
#else
# define BEGIN_DECLS
# define END_DECLS
#endif
BEGIN_DECLS
typedef struct _usbh_dev_driver usbh_dev_driver_t;
typedef struct _usbh_low_level_driver usbh_low_level_driver_t;
/**
* @brief usbh_init
* @param low_level_drivers list of the low level drivers to be used by this library
* @param device_drivers list of the device drivers that could be used with attached devices
*/
void usbh_init(const usbh_low_level_driver_t * const low_level_drivers[], const usbh_dev_driver_t * const device_drivers[]);
/**
* @brief usbh_poll
* @param time_curr_us - use monotically rising time
*
* time_curr_us:
* * can overflow, in time of this writing, after 1s
* * unit is microseconds
*/
void usbh_poll(uint32_t time_curr_us);
END_DECLS
#endif // USBH_CORE_

View file

@ -0,0 +1,65 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_AC_MIDI_
#define USBH_DRIVER_AC_MIDI_
#include "usbh_core.h"
#include <stdint.h>
BEGIN_DECLS
struct _midi_config {
void (*read_callback)(int device_id, uint8_t *data);
void (*notify_connected)(int device_id);
void (*notify_disconnected)(int device_id);
};
typedef struct _midi_config midi_config_t;
/**
* @param bytes_written count of bytes that were actually written
*/
typedef void (*midi_write_callback_t)(uint8_t bytes_written);
/**
* @brief midi_driver_init initialization routine - this will initialize internal structures of this device driver
* @param config
*
* @see midi_config_t
*/
void midi_driver_init(const midi_config_t *config);
/**
* @brief usbh_midi_write
* @param device_id
* @param data
* @param length
* @param callback this is called when the write call finishes
*/
void usbh_midi_write(uint8_t device_id, const void *data, uint32_t length, midi_write_callback_t callback);
extern const usbh_dev_driver_t usbh_midi_driver;
END_DECLS
#endif

View file

@ -0,0 +1,78 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_GP_XBOX_
#define USBH_DRIVER_GP_XBOX_
#include "usbh_core.h"
#include <stdint.h>
BEGIN_DECLS
#define GP_XBOX_DPAD_TOP (1 << 0)
#define GP_XBOX_DPAD_LEFT (1 << 1)
#define GP_XBOX_DPAD_BOTTOM (1 << 2)
#define GP_XBOX_DPAD_RIGHT (1 << 3)
#define GP_XBOX_BUTTON_X (1 << 4)
#define GP_XBOX_BUTTON_Y (1 << 5)
#define GP_XBOX_BUTTON_A (1 << 6)
#define GP_XBOX_BUTTON_B (1 << 7)
#define GP_XBOX_BUTTON_SELECT (1 << 8)
#define GP_XBOX_BUTTON_START (1 << 9)
#define GP_XBOX_BUTTON_LT (1 << 10)
#define GP_XBOX_BUTTON_RT (1 << 11)
#define GP_XBOX_BUTTON_XBOX (1 << 12)
#define GP_XBOX_BUTTON_AXIS_LEFT (1 << 13)
#define GP_XBOX_BUTTON_AXIS_RIGHT (1 << 14)
struct _gp_xbox_packet {
uint32_t buttons;
int16_t axis_left_x;
int16_t axis_left_y;
int16_t axis_right_x;
int16_t axis_right_y;
uint8_t axis_rear_left;
uint8_t axis_rear_right;
};
typedef struct _gp_xbox_packet gp_xbox_packet_t;
struct _gp_xbox_config {
void (*update)(uint8_t device_id, gp_xbox_packet_t data);
void (*notify_connected)(uint8_t device_id);
void (*notify_disconnected)(uint8_t device_id);
};
typedef struct _gp_xbox_config gp_xbox_config_t;
/**
* @brief gp_xbox_driver_init initialization routine - this will initialize internal structures of this device driver
* @see gp_xbox_config_t
*/
void gp_xbox_driver_init(const gp_xbox_config_t *config);
extern const usbh_dev_driver_t usbh_gp_xbox_driver;
END_DECLS
#endif

View file

@ -0,0 +1,85 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_HID_
#define USBH_DRIVER_HID_
#include "usbh_core.h"
#include <stdint.h>
BEGIN_DECLS
struct _hid_mouse_config {
/**
* @brief this is called when some data is read when polling the device
* @param device_id handle of HID device
* @param data pointer to the data
* @param length count of bytes in the data
*
* TODO: make better interface that provides data contained in the report descriptor
*
*/
void (*hid_in_message_handler)(uint8_t device_id, const uint8_t *data, uint32_t length);
};
typedef struct _hid_mouse_config hid_config_t;
/**
* @brief hid_mouse_driver_init initialization routine - this will initialize internal structures of this device driver
* @param config
* @see hid_mouse_config_t
*/
void hid_driver_init(const hid_config_t *config);
/**
* @brief hid_set_report
* @param device_id handle of HID device
* @returns true on success, false otherwise
*/
bool hid_set_report(uint8_t device_id, uint8_t val);
enum HID_TYPE {
HID_TYPE_NONE,
HID_TYPE_MOUSE,
HID_TYPE_KEYBOARD,
};
/**
* @brief hid_get_type
* @param device_id handle of HID device
* @return type of attached HID
* @see enum HID_TYPE
*/
enum HID_TYPE hid_get_type(uint8_t device_id);
/**
* @brief hid_is_connected
* @param device_id handle of HID device
* @return true if the device with device_id is connected
*/
bool hid_is_connected(uint8_t device_id);
extern const usbh_dev_driver_t usbh_hid_driver;
END_DECLS
#endif

View file

@ -0,0 +1,39 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_HUB_
#define USBH_DRIVER_HUB_
#include "usbh_core.h"
BEGIN_DECLS
/**
* @brief hub_driver_init initialization routine - this will initialize internal structures of this device driver
*/
void hub_driver_init(void);
extern const usbh_dev_driver_t usbh_hub_driver;
END_DECLS
#endif

View file

@ -0,0 +1,44 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_LLD_STM32F4_H_
#define USBH_LLD_STM32F4_H_
#include "usbh_core.h"
#include <stdint.h>
BEGIN_DECLS
// pass this to usbh init
extern const usbh_low_level_driver_t usbh_lld_stm32f4_driver_fs;
extern const usbh_low_level_driver_t usbh_lld_stm32f4_driver_hs;
#ifdef USART_DEBUG
void print_channels(const void *drvdata);
#else
#define print_channels(arg) ((void)arg)
#endif
END_DECLS
#endif

4
fw/initRepo.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
git submodule init
git submodule update
make -C libopencm3 -j3 lib/stm32/f4

0
fw/keymap.py Normal file
View file

1
fw/libopencm3 Submodule

@ -0,0 +1 @@
Subproject commit 798c1edf4d11e8a40a7263dc465fa225a63fa7e9

52
fw/libusbhost_stm32f4.ld Normal file
View file

@ -0,0 +1,52 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
* Copyright (C) 2011 Stephen Caudle <scaudle@doceme.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/* Minimal linker script for chinese STM32F4 board (STM32F407VE, 512K flash, 128K RAM). */
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 512K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 112K /* Main SRAM */
sram2 (rwx) : ORIGIN = 0x2001c000, LENGTH = 16K /* SRAM2 with some security features (?) */
ccm (rwx) : ORIGIN = 0x10000000, LENGTH = 64K /* Fast core-coupled memory */
backup (rwx) : ORIGIN = 0x40024000, LENGTH = 4K /* Battery-backed backup SRAM */
}
/* Include the common ld script. */
INCLUDE libopencm3_stm32f4.ld
/* Extra stuff */
SECTIONS
{
.backup_sram : {
. = ALIGN(4);
__backup_sram_start = .;
*(.backup_sram)
__backup_sram_end = .;
} >backup
}
PROVIDE(_ram_start = ORIGIN(ram));
PROVIDE(_ram_end = ORIGIN(ram) + LENGTH(ram));
PROVIDE(_rom_start = ORIGIN(rom));
PROVIDE(_rom_end = ORIGIN(rom) + LENGTH(rom));

34
fw/nouns.py Normal file
View file

@ -0,0 +1,34 @@
NOUNS = '''
yolk writing wrath wound worm wings whistle watchdog
waste vomit vermin variation underachievement tusk troll trick
transplant transgression tooth tongue tickle tick thorn thistle
thing terror tentacle tease surrender surge sucker substance
storm stone stew stalk squid sprout sponge spill
spider sphere spectacle speck spawn soul solution snout
snake smell sloth slime slice sleeper slave sinew
shell shape seizure seed schism scam scale sainthood
root robe roach rinse remains relay rejuvenation realization
reaction ransom pupa pride prey predator potion pornography
polyp plum pleasure pitch pigeon phenomenon pest periwinkle
percolation parasite pair oyster orphan orgasm organism orchid
object nail mushroom murder mucus movement mother mold
mist mildew metal mesh meddling mayhem masterpiece masonry
mask manhood maggot lust loop living_thing liquor liquid
lining laceration knife kitten kiss jumper jest instrument
injustice injury influence indulgence incursion impulse imago hound
horn hook hoof heirloom heart hawk hare hair
gulp guardian grass goat gnat gluttony glowworm gasp
game fusion fungus frustration frog foul foot food
fog foal fluke fluff flower flicker flea flattery
flask flare firefly finger filtration female feeder feather
fart fang failure face fabrication extract exodus evil
envy enema embryo egress echo eater ear dwarf
dust drop draft domestication distortion dew depravity deity
death daughter dash dagger culture crutch crow critter
creeper creation crab corruption cocoon claw chip child
cell catch carving carrot carnival cancer butterfly burn
buildup brush brew bottle boot book bone blunder
blot blood blink bite bird benthos beak basket
bark ball baby axolotl ashes artifact arson armor
apparition antenna alms alienation advent adornment abomination abandonment
'''.split()

16
fw/openocd.cfg Normal file
View file

@ -0,0 +1,16 @@
telnet_port 4444
gdb_port 3333
source [find interface/stlink-v2.cfg]
#interface jlink
#interface stlink-v2
#adapter_khz 10000
#transport select swd
#source /usr/share/openocd/scripts/target/stm32f0x.cfg
source [find target/stm32f4x_stlink.cfg]
init
arm semihosting enable
#flash bank sysflash.alias stm32f0x 0x00000000 0 0 0 $_TARGETNAME

164
fw/pairing.py Executable file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env python3
import threading
import binascii
import re
import os
import time
import serial
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, Pango, GLib
import hexnoise
class PairingWindow(Gtk.Window):
def __init__(self, noise, debug=False):
Gtk.Window.__init__(self, title='SecureHID pairing')
self.noise = noise
self.debug = debug
self.trusted = False
self.set_border_width(10)
self.set_default_size(600, 200)
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
self.label = Gtk.Label()
self.label.set_line_wrap(True)
self.label.set_justify(Gtk.Justification.CENTER)
self.label.set_markup('<b>Step 1</b>\n\nContacting device...')
self.vbox.pack_start(self.label, True, True, 0)
self.entry = Gtk.Entry()
self.entry.set_editable(False)
self.vbox.pack_start(self.entry, True, True, 0)
self.confirm_button = Gtk.Button(label='Trust this device')
self.confirm_button.connect('clicked', self.confirm_trust)
self.confirm_button.set_sensitive(False)
self.abort_button = Gtk.Button(label='Abort')
self.abort_button.connect('clicked', lambda _foo: self.destroy())
self.bbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
self.bbox.pack_start(self.confirm_button, True, True, 0)
self.bbox.pack_start(self.abort_button, True, True, 0)
self.vbox.pack_start(self.bbox, True, True, 0)
self.add(self.vbox)
self.handshaker = threading.Thread(target=self.pair, daemon=True)
self.handshaker.start()
def pair(self):
for i in range(10):
try:
self.run_handshake()
break
except hexnoise.ProtocolError as e:
print(e)
def run_handshake(self):
binding_incantation = self.noise.channel_binding_incantation()
GLib.idle_add(self.label.set_markup,
f'<b>Step 2</b>\n\nPerform channel binding ritual.\n'
f'Enter the following incantation, then press enter.\n'
f'<b>{binding_incantation}</b>')
def update_text(text):
self.entry.set_text(text)
self.entry.set_position(len(text))
clean = lambda s: re.sub('[^a-z0-9-]', '', s.lower())
if clean(binding_incantation).startswith(clean(text)):
color = 0.9, 1.0, 0.9 # light red
else:
color = 1.0, 0.9, 0.9 # light green
self.entry.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(*color, 1.0))
try:
for user_input in self.noise.pairing_messages():
GLib.idle_add(update_text, user_input)
GLib.idle_add(self.finish_pairing)
except hexnoise.ProtocolError as e:
GLib.idle_add(self.label.set_markup, f'<b>Error: {e}</b>')
def finish_pairing(self):
self.label.set_markup(f'<b>Step 3</b>\n\nConfirm pairing.\n'
f'In case the device did not sound an alarm just now, confirm pairing now using the button below.')
self.confirm_button.set_sensitive(True)
def confirm_trust(self, _foo):
self.trusted = True
self.destroy()
class StatusIcon(Gtk.StatusIcon):
def __init__(self):
Gtk.StatusIcon.__init__(self)
self.set_tooltip_text('SecureHID connected')
self.set_from_file('secureusb_icon.png')
def run_pairing_gui(port, baudrate, debug=False):
XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME') or os.path.join(os.path.expandvars('$HOME'), '.config', 'secure_hid')
if not os.path.isdir(XDG_CONFIG_HOME):
os.mkdir(XDG_CONFIG_HOME)
private_key_file = os.path.join(XDG_CONFIG_HOME, 'host_key.pem')
if not os.path.isfile(private_key_file):
with open(private_key_file, 'w') as f:
f.write(binascii.hexlify(hexnoise.NoiseEngine.generate_private_key_x25519()).decode())
known_devices_file = os.path.join(XDG_CONFIG_HOME, 'known_devices')
if not os.path.isfile(known_devices_file):
with open(known_devices_file, 'w') as f:
f.write('# This file contains the hex-encoded SHA-256 fingerprints of the X25519 keys of all trusted SecureHID devices\n')
with open(private_key_file) as f:
host_key_private = binascii.unhexlify(f.read())
ser = serial.Serial(port, baudrate)
packetizer = hexnoise.Packetizer(ser, debug=debug)
noise = hexnoise.NoiseEngine(host_key_private, packetizer, debug=debug)
noise.perform_handshake()
print('Connected.')
print('Device fingerprint:', noise.remote_fingerprint)
if not noise.paired:
window = PairingWindow(noise, debug=debug)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()
if not window.trusted:
raise SystemError('User abort')
if not noise.paired:
raise SystemError('Unknown noise error')
with open(known_devices_file, 'a') as f:
f.write(f'{noise.remote_fingerprint} # added {time.ctime()}\n')
else:
with open(known_devices_file) as f:
known_devices = [ l.strip().partition('#')[0].strip() for l in f.readlines() if not l[0] == '#' ]
if noise.remote_fingerprint not in known_devices:
raise ValueError('Remote host is untrusted but seems to trust us.')
input_runner = threading.Thread(target=noise.uinput_passthrough, daemon=True)
input_runner.start()
status_icon = StatusIcon()
Gtk.main()
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('serial')
parser.add_argument('baudrate')
parser.add_argument('-d', '--debug', action='store_true')
args = parser.parse_args()
run_pairing_gui(args.serial, args.baudrate, args.debug)

BIN
fw/secureusb_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

69
fw/src/CMakeLists.txt Normal file
View file

@ -0,0 +1,69 @@
set (inc ${CMAKE_SOURCE_DIR}/include)
add_library (usbhost
${USART_HELPERS}
${inc}/usbh_core.h
${inc}/usbh_driver_ac_midi.h
${inc}/usbh_driver_gp_xbox.h
${inc}/usbh_driver_hid.h
${inc}/usbh_driver_hub.h
${inc}/usbh_lld_stm32f4.h
${inc}/driver/usbh_device_driver.h
${inc}/usbh_config.h
usbh_core.c
usbh_driver_ac_midi.c
usbh_driver_ac_midi_private.h
usbh_driver_gp_xbox.c
usbh_driver_hid.c
usbh_driver_hub.c
usbh_driver_hub_private.h
usbh_lld_stm32f4.c
usart_helpers.c
tinyprintf.c
cobs.c
noise.c
packet_interface.c
words.c
hid_keycodes.c
)
add_subdirectory (crypto)
add_definitions (
-DBLAKE2S_USE_VECTOR_MATH=0
)
target_link_libraries (usbhost
noise
${LIBOPENCM3_LIB}
)
add_executable (demo
rand_stm32.c
demo.c
)
target_link_libraries (demo
usbhost
noise
)
add_custom_command (TARGET demo
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:demo> ${CMAKE_BINARY_DIR}/demo.hex
COMMENT "Generating output files: ${CMAKE_BINARY_DIR}/demo.hex"
)
add_custom_command (TARGET demo
POST_BUILD
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:demo>
COMMENT "Calculating size of the binary"
)
add_custom_command (TARGET usbhost
POST_BUILD
COMMENT "Calculating size of the library"
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:usbhost>
)

390
fw/src/cobs.c Normal file
View file

@ -0,0 +1,390 @@
#include <cobs.h>
/*@ requires \valid(dst + (0..dstlen-1));
@ requires \valid_read(src + (0..srclen-1));
@ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@
@ behavior valid:
@ assumes 0 <= srclen <= 254;
@ assumes 0 <= dstlen <= 65535;
@ assumes dstlen >= srclen+2;
@ assigns dst[0..srclen+1];
@ ensures \forall integer i; (0 <= i < srclen && \old(src[i]) != 0) ==> dst[i+1] == src[i];
@ ensures \result == srclen+2;
@ ensures \forall integer i; 0 <= i <= srclen ==> dst[i] != 0;
@ ensures dst[srclen+1] == 0;
@
@ behavior invalid:
@ assumes srclen < 0 || srclen > 254
@ || dstlen < 0 || dstlen > 65535
@ || dstlen < srclen+2;
@ assigns \nothing;
@ ensures \result == -1;
@
@ complete behaviors;
@ disjoint behaviors;
@*/
ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen) {
if (dstlen > 65535 || srclen > 254)
return -1;
//@ assert 0 <= dstlen <= 65535 && 0 <= srclen <= 254;
if (dstlen < srclen+2)
return -1;
//@ assert 0 <= srclen < srclen+2 <= dstlen;
size_t p = 0;
/*@ loop invariant 0 <= p <= srclen+1;
@ loop invariant \forall integer i; 0 <= i < p ==> dst[i] != 0;
@ loop invariant \forall integer i; 0 < i < p ==> (src[i-1] != 0 ==> dst[i] == src[i-1]);
@ loop assigns p, dst[0..srclen+1];
@ loop variant srclen-p+1;
@*/
while (p <= srclen) {
char val;
if (p != 0 && src[p-1] != 0) {
val = src[p-1];
} else {
size_t q = p;
/*@ loop invariant 0 <= p <= q <= srclen;
@ loop invariant \forall integer i; p <= i < q ==> src[i] != 0;
@ loop assigns q;
@ loop variant srclen-q;
@*/
while (q < srclen && src[q] != 0)
q++;
//@ assert q == srclen || src[q] == 0;
//@ assert q <= srclen <= 254;
val = (char)q-p+1;
//@ assert val != 0;
}
dst[p] = val;
p++;
}
dst[p] = 0;
//@ assert p == srclen+1;
return srclen+2;
}
/*@ requires \valid_read(src + (0..srclen-1));
@*/
#ifndef VERIFICATION
int cobs_encode_incremental(void *f, int (*output)(void *f, unsigned char c), unsigned char *src, size_t srclen) {
#else
int fc_verification_cobs_encode_incremental(unsigned char *src, size_t srclen) {
#endif
//@ ghost unsigned char output_buf[1000000];
if (srclen > 254)
return -1;
//@ assert 0 <= srclen <= 254;
size_t p = 0;
/*@ loop invariant 0 <= p <= srclen+1;
@ loop invariant \forall integer i; 0 <= i < p ==> output_buf[i] != 0;
@ loop assigns p, output_buf[0..srclen+1];
@ loop variant srclen-p+1;
@*/
while (p <= srclen) {
unsigned char val;
if (p != 0 && src[p-1] != 0) {
val = src[p-1];
} else {
size_t q = p;
/*@ loop invariant 0 <= p <= q <= srclen;
@ loop invariant \forall integer i; p <= i < q ==> src[i] != 0;
@ loop assigns q;
@ loop variant srclen-q;
@*/
while (q < srclen && src[q] != 0)
q++;
val = (unsigned char)q-p+1;
}
//@ ghost output_buf[p] = val;
#ifndef VERIFICATION
int rv = output(f, val);
if (rv)
return rv;
#endif
p++;
}
//@ assert frame_size: p == srclen+1;
//@ assert synchronization_robustness: \forall integer i; 0 <= i < p ==> output_buf[i] != 0;
#ifndef VERIFICATION
int rv = output(f, 0);
if (rv)
return rv;
#endif
return 0;
}
/*@ requires \valid(dst + (0..dstlen-1));
@ requires \valid_read(src + (0..srclen-1));
@ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@
@ behavior maybe_valid_frame:
@ assumes 1 <= srclen <= dstlen <= 65535;
@ assumes \exists integer j; j > 0 && \forall integer i; 0 <= i < j ==> src[i] != 0;
@ assumes \exists integer i; 0 <= i < srclen && src[i] == 0;
@ assigns dst[0..dstlen-1];
@ ensures \result >= 0 || \result == -3;
@ ensures \result >= 0 ==> src[\result+1] == 0;
@ ensures \result >= 0 ==> (\forall integer i; 0 <= i < \result ==> src[i] != 0);
@
@ behavior invalid_frame:
@ assumes 1 <= srclen <= dstlen <= 65535;
@ assumes src[0] == 0 || \forall integer i; 0 <= i < srclen ==> src[i] != 0;
@ assigns dst[0..dstlen-1];
@ ensures \result == -2;
@
@ behavior invalid_buffers:
@ assumes dstlen < 0 || dstlen > 65535
@ || srclen < 1 || srclen > 65535
@ || dstlen < srclen;
@ assigns \nothing;
@ ensures \result == -1;
@
@ complete behaviors;
@ disjoint behaviors;
@*/
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) {
if (dstlen > 65535 || srclen > 65535)
return -1;
if (srclen < 1)
return -1;
if (dstlen < srclen)
return -1;
size_t p = 1;
size_t c = (unsigned char)src[0];
//@ assert 0 <= c < 256;
//@ assert 0 <= c;
//@ assert c < 256;
if (c == 0)
return -2; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
//@ assert c >= 0;
//@ assert c != 0;
//@ assert c <= 257;
//@ assert c > 0;
//@ assert c >= 0 && c != 0 ==> c > 0;
/*@ //loop invariant \forall integer i; 0 <= i <= p ==> (i == srclen || src[i] != 0);
@ loop invariant \forall integer i; 1 <= i < p ==> src[i] != 0;
@ loop invariant c > 0;
@ loop invariant 1 <= p <= srclen <= dstlen <= 65535;
@ loop invariant \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@ loop invariant \valid_read(src + (0..srclen-1));
@ loop invariant \forall integer i; 1 <= i <= srclen ==> \valid(dst + i - 1);
@ loop assigns dst[0..dstlen-1], p, c;
@ loop variant srclen-p;
@*/
while (p < srclen && src[p]) {
char val;
c--;
//@ assert src[p] != 0;
if (c == 0) {
c = (unsigned char)src[p];
val = 0;
} else {
val = src[p];
}
//@ assert 0 <= p-1 <= dstlen-1;
dst[p-1] = val;
p++;
}
if (p == srclen)
return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */
if (c != 1)
return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */
//@ assert 0 < p <= srclen <= 65535;
//@ assert src[p] == 0;
//@ assert \forall integer i; 1 <= i < p ==> src[i] != 0;
return p-1;
}
void cobs_decode_incremental_initialize(struct cobs_decode_state *state) {
state->p = 0;
state->c = 0;
}
/*@ requires separation: \separated(state, dst + (0..dstlen-1));
requires state_valid: \valid(state);
requires dst_valid: \valid(dst + (0..dstlen-1));
requires dstlen_bounds: 0 < dstlen <= INT_MAX;
requires p_valid: 0 <= state->p <= dstlen+1;
requires c_valid: state->p != 0 ==> 1 <= state->c <= 255;
// Sanity properties
ensures c_valid: state->p != 0 ==> 1 <= state->c <= 255;
ensures buffer_bounds: 0 <= state->p <= dstlen+1;
ensures return_value: 0 <= \result <= dstlen || \result \in {-1, -2, -3, -4};
ensures reset_on_error: \result < -1 ==> state->p == 0 && state->c == 0;
// Synchronization properties
ensures self_synchronization: src == 0 ==> \result >= 0 || \result \in {-2, -3};
ensures synchronization_robustness: src != 0 ==> \result \in {-1, -4};
// Basic functional sanity
ensures legal_advance: \result != -1 <==> state->p \in {0, \old(state->p)};
ensures incremental_advance: \result == -1 <==> state->p == \old(state->p) + 1;
ensures nonzero_unmodified: \result == -1 && state->p > 1 ==> dst[state->p-2] \in {0, src};
assigns *state, dst[0..dstlen-1];
behavior eof_bad_empty_frame:
assumes state->p == 0 && src == 0;
ensures \result == -3;
assigns *state;
behavior eof_maybe_good_frame:
assumes state->p != 0 && src == 0;
ensures \result >= 0 || \result == -2;
assigns *state;
behavior decoding_no_overflow:
assumes src != 0 && state->p <= dstlen;
ensures \result == -1;
assigns *state, dst[0..dstlen-1];
behavior decoding_overflow:
assumes src != 0 && state->p > dstlen;
ensures \result == -4;
assigns *state;
complete behaviors;
disjoint behaviors;
@*/
int cobs_decode_incremental(struct cobs_decode_state *state, unsigned char *dst, size_t dstlen, unsigned char src) {
if (state->p == 0) {
if (src == 0) {
/* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
cobs_decode_incremental_initialize(state);
return -3;
}
state->c = src;
state->p++;
return -1;
}
if (!src) {
if (state->c != 1) {
/* invalid framing. The skip counter does not hit the end of the frame. */
cobs_decode_incremental_initialize(state);
return -2;
}
size_t rv = state->p-1;
cobs_decode_incremental_initialize(state);
return rv;
}
unsigned char val;
state->c--;
if (state->c == 0) {
state->c = src;
val = 0;
} else {
val = src;
}
size_t pos = state->p-1;
if (pos >= dstlen) {
cobs_decode_incremental_initialize(state);
return -4; /* output buffer too small */
}
dst[pos] = val;
state->p++;
return -1;
}
/*@ requires \valid_read(buf + (0..len-1));
assigns \nothing;
@*/
void handle_packet(unsigned char *buf, size_t len);
/*@ requires \valid(dst + (0..dstlen-1));
requires \valid_read(src + (0..srclen-1));
requires 0 < dstlen <= 65535;
assigns dst[0..dstlen-1];
@*/
void cobs_decode_test(unsigned char *src, size_t srclen, unsigned char *dst, size_t dstlen) {
struct cobs_decode_state state;
cobs_decode_incremental_initialize(&state);
//@ assert state.p == 0;
//@ assert state.c == 0;
//@ assert state.p != 0 ==> 1 <= state.c <= 255;
/*@ loop invariant \separated(&state, dst + (0..dstlen-1), &i);
loop invariant \valid(&state);
loop invariant \valid(dst + (0..dstlen-1));
loop invariant 0 < dstlen <= 65535;
loop invariant 0 <= state.p <= dstlen+1;
loop invariant state.p != 0 ==> 1 <= state.c <= 255;
loop assigns dst[0..dstlen-1], state, i;
loop variant srclen-i;
@*/
for (size_t i=0; i<srclen; i++) {
int rv = cobs_decode_incremental(&state, dst, dstlen, src[i]);
if (rv >= 0)
handle_packet(dst, rv);
if (rv == -1)
continue;
if (rv < -1)
continue;
}
}
#ifdef VALIDATION
/*@
@ requires 0 <= d < 256;
@ assigns \nothing;
@*/
size_t test(char foo, unsigned int d) {
unsigned int c = (unsigned char)foo;
if (c != 0) {
//@ assert c < 256;
//@ assert c >= 0;
//@ assert c != 0;
//@ assert c > 0;
}
if (d != 0) {
//@ assert d >= 0;
//@ assert d != 0;
//@ assert d > 0;
}
return c + d;
}
#include <__fc_builtin.h>
void main(void) {
char inbuf[254];
char cobsbuf[256];
char outbuf[256];
size_t range = Frama_C_interval(0, sizeof(inbuf));
Frama_C_make_unknown((char *)inbuf, range);
cobs_encode(cobsbuf, sizeof(cobsbuf), inbuf, sizeof(inbuf));
cobs_decode(outbuf, sizeof(outbuf), cobsbuf, sizeof(cobsbuf));
//@ assert \forall integer i; 0 <= i < sizeof(inbuf) ==> outbuf[i] == inbuf[i];
}
#endif//VALIDATION

View file

@ -0,0 +1,93 @@
include_directories (
noise-c/include
noise-c/include/noise/keys
noise-c/src
noise-c/src/crypto/goldilocks/include
noise-c/src/crypto/goldilocks/src/include
noise-c/src/crypto/goldilocks/src/p448/arch_arm_32
noise-c/src/crypto/goldilocks/src/p448
noise-c/src/protocol
)
add_library (noise
noise-c/src/protocol/util.c
noise-c/src/protocol/patterns.c
noise-c/src/protocol/signstate.c
noise-c/src/protocol/randstate.c
noise-c/src/protocol/symmetricstate.c
noise-c/src/protocol/internal.c
noise-c/src/protocol/names.c
noise-c/src/protocol/hashstate.c
noise-c/src/protocol/errors.c
noise-c/src/protocol/cipherstate.c
noise-c/src/protocol/handshakestate.c
noise-c/src/protocol/dhstate.c
noise-c/src/keys/certificate.c
noise-c/src/keys/loader.c
noise-c/src/crypto/sha2/sha256.c
noise-c/src/crypto/sha2/sha512.c
noise-c/src/crypto/ghash/ghash.c
noise-c/src/crypto/ed25519/ed25519.c
noise-c/src/crypto/blake2/blake2s.c
noise-c/src/crypto/blake2/blake2b.c
noise-c/src/crypto/chacha/chacha.c
noise-c/src/crypto/goldilocks/src/ec_point.c
noise-c/src/crypto/goldilocks/src/sha512.c
noise-c/src/crypto/goldilocks/src/p448/arch_32/p448.c
noise-c/src/crypto/goldilocks/src/p448/f_arithmetic.c
noise-c/src/crypto/goldilocks/src/p448/arch_arm_32/p448.c
noise-c/src/crypto/goldilocks/src/p448/magic.c
noise-c/src/crypto/goldilocks/src/barrett_field.c
noise-c/src/crypto/goldilocks/src/goldilocks.c
noise-c/src/crypto/goldilocks/src/arithmetic.c
noise-c/src/crypto/goldilocks/src/crandom.c
noise-c/src/crypto/goldilocks/src/scalarmul.c
noise-c/src/crypto/newhope/poly.c
noise-c/src/crypto/newhope/randombytes.c
noise-c/src/crypto/newhope/reduce.c
noise-c/src/crypto/newhope/ntt.c
noise-c/src/crypto/newhope/crypto_stream_chacha20.c
noise-c/src/crypto/newhope/error_correction.c
noise-c/src/crypto/newhope/batcher.c
noise-c/src/crypto/newhope/fips202.c
noise-c/src/crypto/newhope/newhope.c
noise-c/src/crypto/newhope/precomp.c
noise-c/src/crypto/aes/rijndael-alg-fst.c
noise-c/src/crypto/curve448/curve448.c
noise-c/src/crypto/donna/poly1305-donna.c
noise-c/src/crypto/donna/curve25519-donna.c
noise-c/src/protobufs/protobufs.c
noise-c/src/backend/ref/sign-ed25519.c
noise-c/src/backend/ref/hash-blake2b.c
noise-c/src/backend/ref/hash-sha512.c
noise-c/src/backend/ref/hash-sha256.c
noise-c/src/backend/ref/cipher-aesgcm.c
noise-c/src/backend/ref/cipher-chachapoly.c
noise-c/src/backend/ref/dh-curve25519.c
noise-c/src/backend/ref/dh-newhope.c
noise-c/src/backend/ref/dh-curve448.c
noise-c/src/backend/ref/hash-blake2s.c
)
add_definitions (
-DUSE_LIBSODIUM=0
-DUSE_SODIUM=0
-DHAVE_PTHREAD=0
-DUSE_OPENSSL=0
-D__WORDSIZE=32
-D__BIG_ENDIAN=4321
-D__LITTLE_ENDIAN=1234
-D__BYTE_ORDER=__LITTLE_ENDIAN
-DED25519_CUSTOMRANDOM=1
-DED25519_CUSTOMHASH=1
-DED25519_REFHASH=1
-DBLAKE2S_USE_VECTOR_MATH=0
-DEXPERIMENT_CRANDOM_CUTOFF_BYTES=0
-D__clang__=0
)
set (CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -Wno-implicit-fallthrough -Wno-shadow -Wno-unused-parameter"
)

684
fw/src/demo.c Normal file
View file

@ -0,0 +1,684 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/otg_hs.h>
#include <libopencm3/stm32/otg_fs.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencmsis/core_cm3.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "usart_helpers.h"
#include "usbh_core.h"
#include "usbh_lld_stm32f4.h"
#include "usbh_driver_hid.h"
#include "usbh_driver_hub.h"
#include "rand_stm32.h"
#include "packet_interface.h"
#include "noise.h"
#include "hid_keycodes.h"
#include "words.h"
#include "tracing.h"
#include "crypto/noise-c/src/protocol/internal.h"
#ifndef USE_STM32F4_USBH_DRIVER_FS
#error The full-speed USB driver must be enabled with USE_STM32F4_USBH_DRIVER_FS in usbh_config.h!
#endif
#ifndef MAX_FAILED_HANDSHAKES
#define MAX_FAILED_HANDSHAKES 5
#endif
static struct NoiseState noise_state;
static struct {
union {
struct {
uint8_t local_key[CURVE25519_KEY_LEN];
uint8_t remote_key_reference[BLAKE2S_HASH_SIZE];
};
uint32_t all_keys[0];
} keys;
struct {
uint8_t identity_key_valid;
uint8_t scrub_backup;
uint8_t scrubber_armed;
uint32_t old_scrub_pattern;
uint32_t new_scrub_pattern;
int scrub_idx_read;
int scrub_idx_done;
} mgmt __attribute__((aligned(4)));
} keystore __attribute__((section(".backup_sram"))) = {0};
void _fini(void);
static inline void delay(uint32_t n) {
for (volatile uint32_t i = 0; i < 1490*n; i++);
}
/* Set STM32 to 168 MHz. */
static void clock_setup(void) {
rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOD);
rcc_periph_clock_enable(RCC_GPIOE);
rcc_periph_clock_enable(RCC_USART1);
rcc_periph_clock_enable(RCC_USART2);
rcc_periph_clock_enable(RCC_OTGFS);
rcc_periph_clock_enable(RCC_TIM6);
rcc_periph_clock_enable(RCC_DMA2);
rcc_periph_clock_enable(RCC_DMA1);
rcc_periph_clock_enable(RCC_PWR);
rcc_periph_clock_enable(RCC_BKPSRAM);
rcc_periph_clock_enable(RCC_RNG);
}
void arm_key_scrubber() {
keystore.mgmt.scrubber_armed = 1;
}
static void finish_scrub(int start_index, uint32_t pattern);
static void finish_interrupted_scrub(void);
void disarm_key_scrubber() {
keystore.mgmt.scrubber_armed = 0;
keystore.mgmt.old_scrub_pattern = keystore.mgmt.new_scrub_pattern;
keystore.mgmt.new_scrub_pattern = 0x00000000;
finish_scrub(0, keystore.mgmt.old_scrub_pattern);
}
static void finish_scrub(int start_index, uint32_t pattern) {
for (size_t i=start_index; i<sizeof(keystore.keys)/sizeof(keystore.keys.all_keys[0]); i++) {
keystore.mgmt.scrub_backup = keystore.keys.all_keys[i];
keystore.mgmt.scrub_idx_read = i;
keystore.keys.all_keys[i] ^= pattern;
keystore.mgmt.scrub_idx_done = i;
}
}
static void finish_interrupted_scrub(void) {
if (keystore.mgmt.scrub_idx_read != keystore.mgmt.scrub_idx_done)
keystore.keys.all_keys[keystore.mgmt.scrub_idx_read] = keystore.mgmt.scrub_backup;
finish_scrub(keystore.mgmt.scrub_idx_done, keystore.mgmt.old_scrub_pattern ^ keystore.mgmt.new_scrub_pattern);
}
/* setup 10kHz timer */
static void tim6_setup(void) {
timer_reset(TIM6);
timer_set_prescaler(TIM6, 8400 - 1); // 84Mhz/10kHz - 1
timer_set_period(TIM6, 65535); // Overflow in ~6.5 seconds
timer_enable_irq(TIM6, TIM_DIER_UIE);
nvic_enable_irq(NVIC_TIM6_DAC_IRQ);
nvic_set_priority(NVIC_TIM6_DAC_IRQ, 15<<4); /* really low priority */
timer_enable_counter(TIM6);
}
void tim6_dac_isr(void) {
/* Runs every ~6.5s on timer overrun */
timer_clear_flag(TIM6, TIM_SR_UIF);
if (!keystore.mgmt.scrubber_armed)
return;
keystore.mgmt.old_scrub_pattern = keystore.mgmt.new_scrub_pattern;
noise_rand_bytes(&keystore.mgmt.new_scrub_pattern, sizeof(keystore.mgmt.new_scrub_pattern));
LOG_PRINTF("Scrubbing keys using pattern %08x\n", keystore.mgmt.new_scrub_pattern);
finish_scrub(0, keystore.mgmt.old_scrub_pattern ^ keystore.mgmt.new_scrub_pattern);
}
static uint32_t tim6_get_time_us(void)
{
uint32_t cnt = timer_get_counter(TIM6);
// convert to 1MHz less precise timer value -> units: microseconds
uint32_t time_us = cnt * 100;
return time_us;
}
static void gpio_setup(void)
{
/* Tracing */
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, 0xffff);
/* D2, D3 LEDs */
//gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO6 | GPIO7);
//gpio_set(GPIOA, GPIO6 | GPIO7);
/* Status LEDs (PE4-15) */
gpio_mode_setup(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, 0xfff0);
/* Alarm LEDs (PA6,7) */
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO6 | GPIO7);
gpio_set(GPIOA, GPIO6 | GPIO7);
/* Speaker */
gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO10);
gpio_set(GPIOB, GPIO10);
/* USB OTG FS phy outputs */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12);
gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12);
/* USART1 (debug) */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO10);
gpio_set_af(GPIOA, GPIO_AF7, GPIO9 | GPIO10);
/* USART2 (host link) */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3);
gpio_set_af(GPIOA, GPIO_AF7, GPIO2 | GPIO3);
/* K0 (PE4)/K1 (PE3) buttons */
//gpio_mode_setup(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO3 | GPIO4);
}
struct hid_report {
uint8_t modifiers;
uint8_t _reserved;
uint8_t keycodes[6];
} __attribute__((__packed__));
static char pairing_buf[512];
static size_t pairing_buf_pos = 0;
int pairing_check(struct NoiseState *st, const char *buf);
void pairing_input(uint8_t modbyte, uint8_t keycode);
void pairing_parse_report(struct hid_report *buf, uint8_t len);
/* Minimum number of bytes of handshake hash to confirm during pairing */
#define MIN_PAIRING_SEQUENCE_LENGTH 8
int pairing_check(struct NoiseState *st, const char *buf) {
//LOG_PRINTF("Checking pairing\n");
const char *p = buf;
int idx = 0;
do {
/* Skip over most special chars */
while (*p) {
char c = *p;
if ('0' <= c && c <= '9') break;
if ('a' <= c && c <= 'z') break;
if ('A' <= c && c <= 'Z') break;
if (c == '-') break;
p++;
}
const char *found = strchr(p, ' ');
size_t plen = found ? (size_t)(found - p) : strlen(p); /* p >= found */
while (plen > 0) {
char c = p[plen];
if ('0' <= c && c <= '9') break;
if ('a' <= c && c <= 'z') break;
if ('A' <= c && c <= 'Z') break;
if (c == '-') break;
plen--;
}
plen++;
//LOG_PRINTF("matching: \"%s\" - \"%s\" %d\n", p, p+plen, plen);
if (strncasecmp(p, "and", plen)) { /* ignore "and" */
int num = -1;
for (int i=0; i<256; i++) {
if ((!strncasecmp(p, even[i], plen) && plen == strlen(even[i]))
|| (!strncasecmp(p, odd[i], plen) && plen == strlen(odd[i] ))) {
//LOG_PRINTF(" idx=%02d h=%02x i=%02x adj=%s n=%s plen=%d s=%s\n", idx, st->handshake_hash[idx], i, adjectives[i], nouns[i], plen, p);
num = i;
break;
}
}
if (num == -1) {
LOG_PRINTF("Pairing word \"%s\" not found in dictionary\n", p);
return -1;
}
if (st->handshake_hash[idx] != num) {
LOG_PRINTF("Pairing data does not match hash\n");
return -1;
}
idx++;
}
p = strchr(p, ' ');
if (!p)
break; /* end of string */
p++; /* skip space */
} while (idx < BLAKE2S_HASH_SIZE);
if (idx < MIN_PAIRING_SEQUENCE_LENGTH) {
LOG_PRINTF("Pairing sequence too short, only %d bytes of hash checked\n", idx);
return -1;
}
LOG_PRINTF("Pairing sequence match\n");
return 0;
}
void pairing_input(uint8_t modbyte, uint8_t keycode) {
char ch = 0;
uint8_t level = modbyte & MOD_XSHIFT ? LEVEL_SHIFT : LEVEL_NONE;
switch (keycode) {
case KEY_ENTER:
pairing_buf[pairing_buf_pos++] = '\0';
if (!pairing_check(&noise_state, pairing_buf)) {
persist_remote_key(&noise_state);
/* FIXME write key to backup memory */
uint8_t response = REPORT_PAIRING_SUCCESS;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
} else {
/* FIXME sound alarm */
pairing_buf_pos = 0; /* Reset input buffer */
uint8_t response = REPORT_PAIRING_ERROR;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
}
break;
case KEY_BACKSPACE:
if (pairing_buf_pos > 0)
pairing_buf_pos--;
pairing_buf[pairing_buf_pos] = '\0';
ch = '\b';
break;
default:
for (size_t i=0; keycode_mapping[i].kc != KEY_NONE; i++) {
if (keycode_mapping[i].kc == keycode) {
ch = keycode_mapping[i].ch[level];
if (!ch)
break;
if (pairing_buf_pos < sizeof(pairing_buf)-1) /* allow for terminating null byte */ {
pairing_buf[pairing_buf_pos++] = ch;
pairing_buf[pairing_buf_pos] = '\0';
} else {
LOG_PRINTF("Pairing confirmation user input buffer full\n");
uint8_t response = REPORT_PAIRING_ERROR;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
}
break;
}
}
break;
}
if (ch) {
//LOG_PRINTF("Input: %s\n", pairing_buf);
struct hid_report_packet pkt = {
.type = REPORT_PAIRING_INPUT,
.pairing_input = { .c = ch }
};
if (send_encrypted_message(&noise_state, (uint8_t *)&pkt, sizeof(pkt))) {
LOG_PRINTF("Error sending pairing input packet\n");
return;
}
}
}
void pairing_parse_report(struct hid_report *buf, uint8_t len) {
static uint8_t old_keycodes[6] = {0};
for (int i=0; i<len-2; i++) {
if (!buf->keycodes[i])
break; /* keycodes are always populated from low to high */
int found = 0;
for (int j=0; j<6; j++) {
if (old_keycodes[j] == buf->keycodes[i]) {
found = 1;
break;
}
}
if (!found) /* key pressed */
pairing_input(buf->modifiers, buf->keycodes[i]);
}
memcpy(old_keycodes, buf->keycodes, 6);
}
static void hid_in_message_handler(uint8_t device_id, const uint8_t *data, uint32_t length) {
TRACING_SET(TR_HID_MESSAGE_HANDLER);
if (length < 4 || length > 8) {
LOG_PRINTF("HID report length must be 4 < len < 8, is %d bytes\n", length);
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
//LOG_PRINTF("Sending event %02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]);
int type = hid_get_type(device_id);
if (type != HID_TYPE_KEYBOARD && type != HID_TYPE_MOUSE) {
LOG_PRINTF("Unsupported HID report type %x\n", type);
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
if (noise_state.handshake_state == HANDSHAKE_DONE_UNKNOWN_HOST) {
if (type == HID_TYPE_KEYBOARD)
pairing_parse_report((struct hid_report *)data, length);
else
LOG_PRINTF("Not sending HID mouse report during pairing\n");
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
struct hid_report_packet pkt = {
.type = type == HID_TYPE_KEYBOARD ? REPORT_KEYBOARD : REPORT_MOUSE,
.report = {
.len = length,
.report = {0}
}
};
memcpy(pkt.report.report, data, length);
if (send_encrypted_message(&noise_state, (uint8_t *)&pkt, sizeof(pkt))) {
LOG_PRINTF("Error sending HID report packet\n");
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
}
volatile struct {
struct dma_buf dma;
uint8_t data[256];
} debug_buf = { .dma = { .len = sizeof(debug_buf.data) } };
struct dma_usart_file debug_out_s = {
.usart = DEBUG_USART,
.baudrate = DEBUG_USART_BAUDRATE,
.dma = DMA(DEBUG_USART_DMA_NUM),
.stream = DEBUG_USART_DMA_STREAM_NUM,
.channel = DEBUG_USART_DMA_CHANNEL_NUM,
.irqn = NVIC_DMA_IRQ(DEBUG_USART_DMA_NUM, DEBUG_USART_DMA_STREAM_NUM),
.buf = &debug_buf.dma
};
struct dma_usart_file *debug_out = &debug_out_s;
/* FIXME start unsafe debug code */
void usart1_isr(void) {
if (USART1_SR & USART_SR_ORE) { /* Overrun handling */
LOG_PRINTF("USART1 data register overrun\n");
/* Clear interrupt flag */
int dummy = USART1_DR;
return;
}
uint8_t data = USART1_DR; /* This automatically acknowledges the IRQ */
for (size_t i=0; keycode_mapping[i].kc != KEY_NONE; i++) {
struct hid_report report = {0};
if (keycode_mapping[i].ch[0] == data)
report.modifiers = 0;
else if (keycode_mapping[i].ch[1] == data)
report.modifiers = MOD_LSHIFT;
else continue;
report.keycodes[0] = keycode_mapping[i].kc;
pairing_parse_report(&report, 8);
break;
}
LOG_PRINTF(" %02x ", data);
if (data == 0x7f) {
struct hid_report report = {.modifiers=0, .keycodes={KEY_BACKSPACE, 0}};
pairing_parse_report(&report, 8);
} else if (data == '\r') {
struct hid_report report = {.modifiers=0, .keycodes={KEY_ENTER, 0}};
pairing_parse_report(&report, 8);
LOG_PRINTF("\n");
}
struct hid_report report = {0};
pairing_parse_report(&report, 8);
}
/* end unsafe debug code */
void DMA_ISR(DEBUG_USART_DMA_NUM, DEBUG_USART_DMA_STREAM_NUM)(void) {
TRACING_SET(TR_DEBUG_OUT_DMA_IRQ);
if (dma_get_interrupt_flag(debug_out->dma, debug_out->stream, DMA_FEIF)) {
/* Ignore FIFO errors as they're 100% non-critical for UART applications */
dma_clear_interrupt_flags(debug_out->dma, debug_out->stream, DMA_FEIF);
TRACING_CLEAR(TR_DEBUG_OUT_DMA_IRQ);
return;
}
/* Transfer complete */
dma_clear_interrupt_flags(debug_out->dma, debug_out->stream, DMA_TCIF);
if (debug_out->buf->wr_pos != debug_out->buf->xfr_end) /* buffer not empty */
schedule_dma(debug_out);
TRACING_CLEAR(TR_DEBUG_OUT_DMA_IRQ);
}
/*@ requires \valid_read(&pkt->type) && \valid_read(pkt->payload + (0..payload_length-1));
requires \valid(st);
requires \valid(st->handshake);
requires \separated(st, st->rx_cipher, st->tx_cipher, st->handshake, (uint8_t *)pkt->payload, &usart2_out, &st->handshake_hash);
requires \valid(usart2_out);
assigns pairing_buf_pos, *usart2_out, *st;
assigns st->handshake, st->handshake_state, st->rx_cipher, st->tx_cipher;
@*/
void handle_host_packet(struct NoiseState *st, const struct control_packet *pkt, size_t payload_length) {
TRACING_SET(TR_HOST_PKT_HANDLER);
if (pkt->type == HOST_INITIATE_HANDSHAKE) {
/* It is important that we acknowledge this command right away. Starting the handshake involves key
* generation which takes a few milliseconds. If we'd acknowledge this later, we might run into an
* overrun here since we would be blocking the buffer during key generation. */
if (payload_length > 0) {
LOG_PRINTF("Extraneous data in INITIATE_HANDSHAKE message\n");
} else if (st->failed_handshakes < MAX_FAILED_HANDSHAKES) {
LOG_PRINTF("Starting noise protocol handshake...\n");
if (reset_protocol_handshake(st))
LOG_PRINTF("Error starting protocol handshake.\n");
pairing_buf_pos = 0; /* Reset channel binding keyboard input buffer */
} else {
LOG_PRINTF("Too many failed handshake attempts, not starting another one\n");
struct control_packet out = { .type=HOST_TOO_MANY_FAILS };
send_packet(usart2_out, (uint8_t *)&out, sizeof(out));
}
} else if (pkt->type == HOST_HANDSHAKE) {
LOG_PRINTF("Handling handshake packet of length %d\n", payload_length);
TRACING_SET(TR_NOISE_HANDSHAKE);
if (try_continue_noise_handshake(st, pkt->payload, payload_length)) {
TRACING_CLEAR(TR_NOISE_HANDSHAKE);
LOG_PRINTF("Reporting handshake error to host\n");
struct control_packet out = { .type=HOST_CRYPTO_ERROR };
send_packet(usart2_out, (uint8_t *)&out, sizeof(out));
} else TRACING_CLEAR(TR_NOISE_HANDSHAKE);
} else {
LOG_PRINTF("Unhandled packet of type %d\n", pkt->type);
}
TRACING_CLEAR(TR_HOST_PKT_HANDLER);
}
int main(void)
{
clock_setup();
gpio_setup();
pwr_disable_backup_domain_write_protect();
PWR_CSR |= PWR_CSR_BRE; /* Enable backup SRAM battery power regulator */
finish_interrupted_scrub();
disarm_key_scrubber();
tim6_setup();
#ifdef USART_DEBUG
usart_dma_init(debug_out);
/* FIXME start unsafe debug code */
usart_enable_rx_interrupt(debug_out->usart);
nvic_enable_irq(NVIC_USART1_IRQ);
nvic_set_priority(NVIC_USART1_IRQ, 3<<4);
/* end unsafe debug code */
#endif
usart_dma_init(usart2_out);
usart_enable_rx_interrupt(USART2);
nvic_enable_irq(NVIC_USART2_IRQ);
nvic_set_priority(NVIC_USART2_IRQ, 3<<4);
nvic_set_priority(debug_out_s.irqn, 1<<4);
LOG_PRINTF("\n==================================\n");
LOG_PRINTF("SecureHID device side initializing\n");
LOG_PRINTF("==================================\n");
LOG_PRINTF("Initializing USB...\n");
const hid_config_t hid_config = { .hid_in_message_handler = &hid_in_message_handler };
hid_driver_init(&hid_config);
hub_driver_init();
const usbh_dev_driver_t *device_drivers[] = { &usbh_hub_driver, &usbh_hid_driver, NULL };
const usbh_low_level_driver_t * const lld_drivers[] = { &usbh_lld_stm32f4_driver_fs, NULL };
usbh_init(lld_drivers, device_drivers);
LOG_PRINTF("Initializing RNG...\n");
rand_init();
//@ assert \valid(&noise_state);
//@ assert \valid((uint8_t *)keystore.keys.remote_key_reference + (0..31)) && \valid((uint8_t *)keystore.keys.local_key + (0..31));
noise_state_init(&noise_state, keystore.keys.remote_key_reference, keystore.keys.local_key);
//@ assert \valid(noise_state.local_key + (0..31));
/* FIXME load remote key from backup memory */
/* FIXME only run this on first boot and persist key in backup sram. Allow reset via jumper-triggered factory reset function. */
if (!keystore.mgmt.identity_key_valid) {
LOG_PRINTF("Generating identity key...\n");
if (generate_identity_key(&noise_state)) {
LOG_PRINTF("Error generating identiy key\n");
} else {
keystore.mgmt.identity_key_valid = 1;
}
}
int poll_ctr = 0;
int led_ctr = 0;
int led_idx = 0;
int spk_ctr = 0;
int spk_ctr2 = 0;
int spk_adv = 0;
int spk_inc = 1;
gpio_clear(GPIOA, GPIO6);
gpio_clear(GPIOA, GPIO7);
gpio_clear(GPIOB, GPIO10);
while (23) {
delay(1);
led_ctr++;
if (led_ctr == 10) {
gpio_clear(GPIOA, GPIO6);
gpio_clear(GPIOA, GPIO7);
} else if (led_ctr == 300) {
gpio_mode_setup(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, 0xfff0);
} else if (led_ctr == 400) {
if (++led_idx == 12)
led_idx = 0;
gpio_mode_setup(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, 1<<(4+led_idx));
gpio_clear(GPIOE, 0xfff0);
if (led_idx & 1)
gpio_set(GPIOA, GPIO6);
else
gpio_set(GPIOA, GPIO7);
led_ctr = 0;
}
spk_ctr++;
spk_ctr2++;
if (spk_ctr2 == 100) {
spk_adv += spk_inc;
if (spk_adv > 31)
spk_inc = -3;
if (spk_adv < 1)
spk_inc = 1;
spk_ctr2 = 0;
}
if (spk_ctr%spk_adv == 0) {
gpio_set(GPIOB, GPIO10);
} else {
gpio_clear(GPIOB, GPIO10);
}
continue;
if (++poll_ctr == 10) {
poll_ctr = 0;
TRACING_SET(TR_USBH_POLL);
usbh_poll(tim6_get_time_us());
TRACING_CLEAR(TR_USBH_POLL);
}
if (host_packet_length > 0) {
handle_host_packet(&noise_state, (struct control_packet *)host_packet_buf, host_packet_length - 1);
host_packet_length = 0; /* Acknowledge to USART ISR the buffer has been handled */
} else if (host_packet_length < 0) { /* USART error */
host_packet_length = 0; /* Acknowledge to USART ISR the error has been handled */
if (noise_state.handshake_state < HANDSHAKE_DONE_UNKNOWN_HOST) {
LOG_PRINTF("USART error, aborting handshake\n");
struct control_packet pkt = { .type=HOST_COMM_ERROR };
send_packet(usart2_out, (uint8_t *)&pkt, sizeof(pkt));
if (reset_protocol_handshake(&noise_state))
LOG_PRINTF("Error starting protocol handshake.\n");
pairing_buf_pos = 0; /* Reset channel binding keyboard input buffer */
}
}
if (noise_state.handshake_state == HANDSHAKE_IN_PROGRESS) {
TRACING_SET(TR_NOISE_HANDSHAKE);
if (try_continue_noise_handshake(&noise_state, NULL, 0)) { /* handle outgoing messages */
TRACING_CLEAR(TR_NOISE_HANDSHAKE);
LOG_PRINTF("Reporting handshake error to host\n");
struct control_packet pkt = { .type=HOST_CRYPTO_ERROR };
send_packet(usart2_out, (uint8_t *)&pkt, sizeof(pkt));
} else TRACING_CLEAR(TR_NOISE_HANDSHAKE);
}
}
}
void _fini() {
while (1);
}

1
fw/src/frama_c_cmdline Normal file
View file

@ -0,0 +1 @@
frama-c-gui -c11 -cpp-extra-args="-DVERIFICATION -DSTM32F4 -DUSE_STM32F4_USBH_DRIVER_FS -DDEBUG_USART=USART1 -DDEBUG_USART_BAUDRATE=115200 -DDEBUG_USART_DMA_CHANNEL_NUM=4 -DDEBUG_USART_DMA_NUM=2 -DDEBUG_USART_DMA_STREAM_NUM=7 -I. -I"(frama-c -print-path)"/libc -I crypto/noise-c/include -I../include -I../libopencm3/include" -wp -wp-rte -wp-model +cint -wp-verbose 2 -slevel 8 -wp-out goals noise.c demo.c -wp-fct try_continue_noise_handshake

48
fw/src/hid_keycodes.c Normal file
View file

@ -0,0 +1,48 @@
#include "hid_keycodes.h"
struct keymap_entry keycode_mapping[] = {
{ KEY_A, {'a', 'A'}},
{ KEY_B, {'b', 'B'}},
{ KEY_C, {'c', 'C'}},
{ KEY_D, {'d', 'D'}},
{ KEY_E, {'e', 'E'}},
{ KEY_F, {'f', 'F'}},
{ KEY_G, {'g', 'G'}},
{ KEY_H, {'h', 'H'}},
{ KEY_I, {'i', 'I'}},
{ KEY_J, {'j', 'J'}},
{ KEY_K, {'k', 'K'}},
{ KEY_L, {'l', 'L'}},
{ KEY_M, {'m', 'M'}},
{ KEY_N, {'n', 'N'}},
{ KEY_O, {'o', 'O'}},
{ KEY_P, {'p', 'P'}},
{ KEY_Q, {'q', 'Q'}},
{ KEY_R, {'r', 'R'}},
{ KEY_S, {'s', 'S'}},
{ KEY_T, {'t', 'T'}},
{ KEY_U, {'u', 'U'}},
{ KEY_V, {'v', 'V'}},
{ KEY_W, {'w', 'W'}},
{ KEY_X, {'x', 'X'}},
{ KEY_Y, {'y', 'Y'}},
{ KEY_Z, {'z', 'Z'}},
{ KEY_1, {'1', '!'}},
{ KEY_2, {'2', '@'}},
{ KEY_3, {'3', '#'}},
{ KEY_4, {'4', '$'}},
{ KEY_5, {'5', '%'}},
{ KEY_6, {'6', '^'}},
{ KEY_7, {'7', '&'}},
{ KEY_8, {'8', '*'}},
{ KEY_9, {'9', '('}},
{ KEY_0, {'0', ')'}},
{ KEY_MINUS, {'-', '_'}},
{ KEY_SPACE, {' ', ' '}},
{ KEY_COMMA, {',', '<'}},
{ KEY_DOT, {'.', '>'}},
{ KEY_SEMICOLON, {';', ':'}},
{ KEY_NONE, {0, 0}}, /* end marker */
};

218
fw/src/hid_keycodes.h Normal file
View file

@ -0,0 +1,218 @@
#ifndef __HID_KEYCODES_H__
#define __HID_KEYCODES_H__
enum mod_levels {
LEVEL_NONE,
LEVEL_SHIFT,
LEVEL_NLEVELS
};
enum mod_bits {
MOD_LCTRL,
MOD_LSHIFT,
MOD_LALT,
MOD_LMETA,
MOD_RCTRL,
MOD_RSHIFT,
MOD_RALT,
MOD_RMETA,
};
enum mod_bitmaps {
MOD_XCTRL = MOD_LCTRL | MOD_RCTRL,
MOD_XSHIFT = MOD_LSHIFT | MOD_RSHIFT,
MOD_XALT = MOD_LALT | MOD_RALT,
MOD_XMETA = MOD_LMETA | MOD_RMETA,
};
struct keymap_entry {
unsigned char kc;
char ch[LEVEL_NLEVELS];
};
extern struct keymap_entry keycode_mapping[];
enum hid_keycode {
KEY_NONE = 0x00, // No key pressed
KEY_ERR_OVF = 0x01, // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key")
KEY_A = 0x04, // Keyboard a and A
KEY_B = 0x05, // Keyboard b and B
KEY_C = 0x06, // Keyboard c and C
KEY_D = 0x07, // Keyboard d and D
KEY_E = 0x08, // Keyboard e and E
KEY_F = 0x09, // Keyboard f and F
KEY_G = 0x0a, // Keyboard g and G
KEY_H = 0x0b, // Keyboard h and H
KEY_I = 0x0c, // Keyboard i and I
KEY_J = 0x0d, // Keyboard j and J
KEY_K = 0x0e, // Keyboard k and K
KEY_L = 0x0f, // Keyboard l and L
KEY_M = 0x10, // Keyboard m and M
KEY_N = 0x11, // Keyboard n and N
KEY_O = 0x12, // Keyboard o and O
KEY_P = 0x13, // Keyboard p and P
KEY_Q = 0x14, // Keyboard q and Q
KEY_R = 0x15, // Keyboard r and R
KEY_S = 0x16, // Keyboard s and S
KEY_T = 0x17, // Keyboard t and T
KEY_U = 0x18, // Keyboard u and U
KEY_V = 0x19, // Keyboard v and V
KEY_W = 0x1a, // Keyboard w and W
KEY_X = 0x1b, // Keyboard x and X
KEY_Y = 0x1c, // Keyboard y and Y
KEY_Z = 0x1d, // Keyboard z and Z
KEY_1 = 0x1e, // Keyboard 1 and !
KEY_2 = 0x1f, // Keyboard 2 and @
KEY_3 = 0x20, // Keyboard 3 and #
KEY_4 = 0x21, // Keyboard 4 and $
KEY_5 = 0x22, // Keyboard 5 and %
KEY_6 = 0x23, // Keyboard 6 and ^
KEY_7 = 0x24, // Keyboard 7 and &
KEY_8 = 0x25, // Keyboard 8 and *
KEY_9 = 0x26, // Keyboard 9 and (
KEY_0 = 0x27, // Keyboard 0 and )
KEY_ENTER = 0x28, // Keyboard Return (ENTER)
KEY_ESC = 0x29, // Keyboard ESCAPE
KEY_BACKSPACE = 0x2a, // Keyboard DELETE (Backspace)
KEY_TAB = 0x2b, // Keyboard Tab
KEY_SPACE = 0x2c, // Keyboard Spacebar
KEY_MINUS = 0x2d, // Keyboard - and _
KEY_EQUAL = 0x2e, // Keyboard = and +
KEY_LEFTBRACE = 0x2f, // Keyboard [ and {
KEY_RIGHTBRACE = 0x30, // Keyboard ] and }
KEY_BACKSLASH = 0x31, // Keyboard \ and |
KEY_HASHTILDE = 0x32, // Keyboard Non-US # and ~
KEY_SEMICOLON = 0x33, // Keyboard ; and :
KEY_APOSTROPHE = 0x34, // Keyboard ' and "
KEY_GRAVE = 0x35, // Keyboard ` and ~
KEY_COMMA = 0x36, // Keyboard , and <
KEY_DOT = 0x37, // Keyboard . and >
KEY_SLASH = 0x38, // Keyboard / and ?
KEY_CAPSLOCK = 0x39, // Keyboard Caps Lock
KEY_F1 = 0x3a, // Keyboard F1
KEY_F2 = 0x3b, // Keyboard F2
KEY_F3 = 0x3c, // Keyboard F3
KEY_F4 = 0x3d, // Keyboard F4
KEY_F5 = 0x3e, // Keyboard F5
KEY_F6 = 0x3f, // Keyboard F6
KEY_F7 = 0x40, // Keyboard F7
KEY_F8 = 0x41, // Keyboard F8
KEY_F9 = 0x42, // Keyboard F9
KEY_F10 = 0x43, // Keyboard F10
KEY_F11 = 0x44, // Keyboard F11
KEY_F12 = 0x45, // Keyboard F12
KEY_SYSRQ = 0x46, // Keyboard Print Screen
KEY_SCROLLLOCK = 0x47, // Keyboard Scroll Lock
KEY_PAUSE = 0x48, // Keyboard Pause
KEY_INSERT = 0x49, // Keyboard Insert
KEY_HOME = 0x4a, // Keyboard Home
KEY_PAGEUP = 0x4b, // Keyboard Page Up
KEY_DELETE = 0x4c, // Keyboard Delete Forward
KEY_END = 0x4d, // Keyboard End
KEY_PAGEDOWN = 0x4e, // Keyboard Page Down
KEY_RIGHT = 0x4f, // Keyboard Right Arrow
KEY_LEFT = 0x50, // Keyboard Left Arrow
KEY_DOWN = 0x51, // Keyboard Down Arrow
KEY_UP = 0x52, // Keyboard Up Arrow
KEY_NUMLOCK = 0x53, // Keyboard Num Lock and Clear
KEY_KPSLASH = 0x54, // Keypad /
KEY_KPASTERISK = 0x55, // Keypad *
KEY_KPMINUS = 0x56, // Keypad -
KEY_KPPLUS = 0x57, // Keypad +
KEY_KPENTER = 0x58, // Keypad ENTER
KEY_KP1 = 0x59, // Keypad 1 and End
KEY_KP2 = 0x5a, // Keypad 2 and Down Arrow
KEY_KP3 = 0x5b, // Keypad 3 and PageDn
KEY_KP4 = 0x5c, // Keypad 4 and Left Arrow
KEY_KP5 = 0x5d, // Keypad 5
KEY_KP6 = 0x5e, // Keypad 6 and Right Arrow
KEY_KP7 = 0x5f, // Keypad 7 and Home
KEY_KP8 = 0x60, // Keypad 8 and Up Arrow
KEY_KP9 = 0x61, // Keypad 9 and Page Up
KEY_KP0 = 0x62, // Keypad 0 and Insert
KEY_KPDOT = 0x63, // Keypad . and Delete
KEY_102ND = 0x64, // Keyboard Non-US \ and |
KEY_COMPOSE = 0x65, // Keyboard Application
KEY_POWER = 0x66, // Keyboard Power
KEY_KPEQUAL = 0x67, // Keypad =
KEY_F13 = 0x68, // Keyboard F13
KEY_F14 = 0x69, // Keyboard F14
KEY_F15 = 0x6a, // Keyboard F15
KEY_F16 = 0x6b, // Keyboard F16
KEY_F17 = 0x6c, // Keyboard F17
KEY_F18 = 0x6d, // Keyboard F18
KEY_F19 = 0x6e, // Keyboard F19
KEY_F20 = 0x6f, // Keyboard F20
KEY_F21 = 0x70, // Keyboard F21
KEY_F22 = 0x71, // Keyboard F22
KEY_F23 = 0x72, // Keyboard F23
KEY_F24 = 0x73, // Keyboard F24
KEY_OPEN = 0x74, // Keyboard Execute
KEY_HELP = 0x75, // Keyboard Help
KEY_PROPS = 0x76, // Keyboard Menu
KEY_FRONT = 0x77, // Keyboard Select
KEY_STOP = 0x78, // Keyboard Stop
KEY_AGAIN = 0x79, // Keyboard Again
KEY_UNDO = 0x7a, // Keyboard Undo
KEY_CUT = 0x7b, // Keyboard Cut
KEY_COPY = 0x7c, // Keyboard Copy
KEY_PASTE = 0x7d, // Keyboard Paste
KEY_FIND = 0x7e, // Keyboard Find
KEY_MUTE = 0x7f, // Keyboard Mute
KEY_VOLUMEUP = 0x80, // Keyboard Volume Up
KEY_VOLUMEDOWN = 0x81, // Keyboard Volume Down
KEY_KPCOMMA = 0x85, // Keypad Comma
KEY_RO = 0x87, // Keyboard International1
KEY_KATAKANAHIRAGANA = 0x88, // Keyboard International2
KEY_YEN = 0x89, // Keyboard International3
KEY_HENKAN = 0x8a, // Keyboard International4
KEY_MUHENKAN = 0x8b, // Keyboard International5
KEY_KPJPCOMMA = 0x8c, // Keyboard International6
KEY_HANGEUL = 0x90, // Keyboard LANG1
KEY_HANJA = 0x91, // Keyboard LANG2
KEY_KATAKANA = 0x92, // Keyboard LANG3
KEY_HIRAGANA = 0x93, // Keyboard LANG4
KEY_ZENKAKUHANKAKU = 0x94, // Keyboard LANG5
KEY_KPLEFTPAREN = 0xb6, // Keypad (
KEY_KPRIGHTPAREN = 0xb7, // Keypad )
KEY_LEFTCTRL = 0xe0, // Keyboard Left Control
KEY_LEFTSHIFT = 0xe1, // Keyboard Left Shift
KEY_LEFTALT = 0xe2, // Keyboard Left Alt
KEY_LEFTMETA = 0xe3, // Keyboard Left GUI
KEY_RIGHTCTRL = 0xe4, // Keyboard Right Control
KEY_RIGHTSHIFT = 0xe5, // Keyboard Right Shift
KEY_RIGHTALT = 0xe6, // Keyboard Right Alt
KEY_RIGHTMETA = 0xe7, // Keyboard Right GUI
KEY_MEDIA_PLAYPAUSE = 0xe8,
KEY_MEDIA_STOPCD = 0xe9,
KEY_MEDIA_PREVIOUSSONG = 0xea,
KEY_MEDIA_NEXTSONG = 0xeb,
KEY_MEDIA_EJECTCD = 0xec,
KEY_MEDIA_VOLUMEUP = 0xed,
KEY_MEDIA_VOLUMEDOWN = 0xee,
KEY_MEDIA_MUTE = 0xef,
KEY_MEDIA_WWW = 0xf0,
KEY_MEDIA_BACK = 0xf1,
KEY_MEDIA_FORWARD = 0xf2,
KEY_MEDIA_STOP = 0xf3,
KEY_MEDIA_FIND = 0xf4,
KEY_MEDIA_SCROLLUP = 0xf5,
KEY_MEDIA_SCROLLDOWN = 0xf6,
KEY_MEDIA_EDIT = 0xf7,
KEY_MEDIA_SLEEP = 0xf8,
KEY_MEDIA_COFFEE = 0xf9,
KEY_MEDIA_REFRESH = 0xfa,
KEY_MEDIA_CALC = 0xfb,
};
#endif

445
fw/src/noise.c Normal file
View file

@ -0,0 +1,445 @@
#include <string.h>
#include "noise.h"
#include "packet_interface.h"
#include "rand_stm32.h"
#include "crypto/noise-c/src/crypto/blake2/blake2s.h"
#ifdef VERIFICATION
#define HANDLE_NOISE_ERROR(x, msg) if (x) { goto errout; }
#else
#define HANDLE_NOISE_ERROR(x, msg) do { \
err = x; \
if (err != NOISE_ERROR_NONE) { \
char errbuf[256]; \
noise_strerror(err, errbuf, sizeof(errbuf)); \
LOG_PRINTF("Error " msg ": %s\n", errbuf); \
goto errout; \
} \
} while(0);
#endif
#ifdef VERIFICATION
/*@ requires \valid(s + (0..n-1));
@ assigns s[0..n-1] \from c;
@ assigns \result \from s;
@ ensures result_ptr: \result == s;
@*/
uint8_t *fc_memset_uint8(uint8_t *s, int c, size_t n);
/*@ requires \valid(dest + (0..n-1));
@ requires \valid_read(src + (0..n-1));
@ assigns dest[0..n-1] \from src[0..n-1];
@ assigns \result \from dest;
@ ensures result_ptr: \result == dest;
@ ensures equals: \forall integer i; 0 <= i <= n-1 ==> dest[i] == src[i];
@*/
uint8_t *fc_memcpy_uint8(uint8_t *dest, const uint8_t *src, size_t n);
/*@ requires valid_s1: \valid_read(s1 + (0..n-1));
@ requires valid_s2: \valid_read(s2 + (0..n-1));
@ assigns \result \from
@ indirect:s1[0.. n-1], indirect:s2[0.. n-1];
@ ensures logic_spec: \result == 0 <==> (
\forall integer i; 0 <= i <= n-1 ==> s1[i] == s2[i]);
@*/
int fc_memcmp_uint8(const uint8_t *s1, const uint8_t *s2, size_t n);
#else
#define fc_memset_uint8 memset
#define fc_memcpy_uint8 memcpy
#define fc_memcmp_uint8 memcmp
#endif
volatile uint8_t host_packet_buf[MAX_HOST_PACKET_SIZE];
volatile int host_packet_length = 0;
/*@
requires validity: \valid(st);
ensures equal: st->remote_key_reference == remote_key_reference && st->local_key == local_key;
ensures equal: st->handshake_state == HANDSHAKE_UNINITIALIZED;
ensures equal: st->failed_handshakes == 0;
ensures equal: st->tx_cipher == NULL && st->rx_cipher == NULL && st->handshake == NULL;
assigns *st;
*/
void noise_state_init(struct NoiseState *st, uint8_t *remote_key_reference, uint8_t *local_key) {
st->handshake_state = HANDSHAKE_UNINITIALIZED;
st->handshake = NULL;
st->tx_cipher = NULL;
st->rx_cipher = NULL;
fc_memset_uint8(st->handshake_hash, 0, sizeof(st->handshake_hash));
st->remote_key_reference = remote_key_reference;
st->local_key = local_key;
st->failed_handshakes = 0;
}
/*@
requires validity: \valid(st) && \valid(st->handshake_hash + (0..31)) && \valid_read(st->local_key + (0..31));
requires separation: \separated(st, st->rx_cipher, st->tx_cipher, st->handshake);
ensures result: \result \in {0, -1};
ensures success: \result == 0 ==> (
\valid(st->handshake) &&
(st->handshake_state == HANDSHAKE_PHASE1));
ensures failure: \result != 0 ==> (
(st->handshake == NULL) &&
(st->handshake_state == HANDSHAKE_UNINITIALIZED));
ensures unmodified: \old(st->failed_handshakes) == st->failed_handshakes;
assigns *st, *st->rx_cipher, *st->tx_cipher;
*/
int reset_protocol_handshake(struct NoiseState *st) {
uninit_handshake(st, HANDSHAKE_UNINITIALIZED);
disarm_key_scrubber();
noise_cipherstate_free(st->tx_cipher);
noise_cipherstate_free(st->rx_cipher);
st->tx_cipher = NULL;
st->rx_cipher = NULL;
st->handshake = NULL;
fc_memset_uint8(st->handshake_hash, 0, sizeof(st->handshake_hash));
return start_protocol_handshake(st);
}
/*@ requires validity: \valid(st) && \valid_read(st->local_key + (0..31));
ensures result: \result \in {0, -1};
ensures success: \result == 0 ==> (
\valid(st->handshake) &&
st->handshake_state == HANDSHAKE_PHASE1);
ensures failure: \result != 0 ==> (
st->handshake == \old(st->handshake) &&
st->handshake_state == \old(st->handshake_state));
assigns st->handshake, st->handshake_state;
*/
int start_protocol_handshake(struct NoiseState *st) {
/* TODO Noise-C is nice for prototyping, but we should really get rid of it for mostly three reasons:
* * We don't need cipher/protocol agility, and by baking the final protocol into the firmware we can save a lot
* of flash space by not including all the primitives we don't need as well as noise's dynamic protocol
* abstraction layer.
* * Noise-c is not very embedded-friendly, in particular it uses malloc and free. We should be able to run
* everything with statically allocated buffers instead.
* * Parts of it are not written that well
*/
NoiseHandshakeState *handshake;
int err;
HANDLE_NOISE_ERROR(noise_init(), "initializing noise");
HANDLE_NOISE_ERROR(noise_handshakestate_new_by_name(&handshake, "Noise_XX_25519_ChaChaPoly_BLAKE2s", NOISE_ROLE_RESPONDER), "instantiating handshake pattern");
NoiseDHState *dh = noise_handshakestate_get_local_keypair_dh(handshake);
HANDLE_NOISE_ERROR(noise_dhstate_set_keypair_private(dh, st->local_key, CURVE25519_KEY_LEN), "loading local private keys");
HANDLE_NOISE_ERROR(noise_handshakestate_start(handshake), "starting handshake");
st->handshake = handshake;
st->handshake_state = HANDSHAKE_PHASE1;
return 0;
errout:
noise_handshakestate_free(handshake);
return -1;
}
/*@ requires validity: \valid(st) && \valid(st->local_key + (0..31));
assigns st->local_key[0..31];
*/
int generate_identity_key(struct NoiseState *st) {
NoiseDHState *dh;
int err;
HANDLE_NOISE_ERROR(noise_dhstate_new_by_name(&dh, "25519"), "creating dhstate for key generation");
HANDLE_NOISE_ERROR(noise_dhstate_generate_keypair(dh), "generating key pair");
uint8_t unused[CURVE25519_KEY_LEN]; /* the noise api is a bit bad here. */
fc_memset_uint8(st->local_key, 0, CURVE25519_KEY_LEN);
HANDLE_NOISE_ERROR(noise_dhstate_get_keypair(dh, st->local_key, CURVE25519_KEY_LEN, unused, sizeof(unused)), "saving key pair");
noise_dhstate_free(dh);
return 0;
errout:
if (dh)
noise_dhstate_free(dh);
return -1;
}
/*@requires validity: \valid(st);
requires state_valid: new_state \in
{HANDSHAKE_UNINITIALIZED, HANDSHAKE_DONE_UNKNOWN_HOST, HANDSHAKE_DONE_KNOWN_HOST};
ensures state: st->handshake_state == new_state;
ensures handshake: st->handshake == NULL;
assigns st->handshake, st->handshake_state;
@*/
void uninit_handshake(struct NoiseState *st, enum handshake_state new_state) {
if (st->handshake)
noise_handshakestate_free(st->handshake);
st->handshake_state = new_state;
st->handshake = NULL;
arm_key_scrubber();
}
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->handshake);
requires validity: \valid(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->handshake_hash + (0..sizeof(st->handshake_hash)-1));
requires separation: \separated(usart2_out, st, buf, st->handshake);
ensures \result \in {-1, 0};
assigns *usart2_out, *st->handshake;
@*/
int handshake_phase1(struct NoiseState * const st, uint8_t *buf, size_t len) {
int err;
struct {
struct control_packet header;
uint8_t payload[MAX_HOST_PACKET_SIZE];
} pkt;
NoiseBuffer noise_msg;
/* Read the next handshake message and discard the payload */
noise_buffer_set_input(noise_msg, buf, len);
HANDLE_NOISE_ERROR(noise_handshakestate_read_message(st->handshake, &noise_msg, NULL), "reading handshake message");
/* Write the next handshake message with a zero-length noise payload */
pkt.header.type = HOST_HANDSHAKE;
noise_buffer_set_output(noise_msg, &pkt.payload, sizeof(pkt.payload));
HANDLE_NOISE_ERROR(noise_handshakestate_write_message(st->handshake, &noise_msg, NULL), "writing handshake message");
send_packet(usart2_out, (uint8_t *)&pkt, noise_msg.size + sizeof(pkt.header));
return 0;
errout: /* for HANDLE_NOISE_ERROR macro */
return -1;
}
//@ ghost int key_checked_trace;
//@ ghost int key_match_trace;
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->handshake);
requires validity: \valid(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid_read(st->remote_key_reference + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->handshake_hash + (0..sizeof(st->handshake_hash)-1));
requires separation: \separated(usart2_out, st);
requires sanity: 0 <= st->failed_handshakes < 100;
requires separation: \separated(&usart2_out, st, buf, st->handshake);
ensures result: \result \in {-1, 0, 1};
ensures sanity: 0 <= st->failed_handshakes <= 102;
ensures sanity: \result != 1 ==> st->failed_handshakes >= \old(st->failed_handshakes);
ensures permission_valid: \result != -1 ==> key_checked_trace == 1;
ensures permission_valid: \result == 1 ==> key_match_trace == 1;
//
assigns *usart2_out, *st, *st->handshake, key_checked_trace, key_match_trace;
@*/
int handshake_phase2(struct NoiseState * const st, uint8_t *buf, size_t len) {
//@ ghost int old_failed_handshakes = st->failed_handshakes;
int err;
struct {
struct control_packet header;
uint8_t payload[MAX_HOST_PACKET_SIZE];
} pkt;
NoiseBuffer noise_msg;
//@ ghost key_checked_trace = 0;
// ghost key_match_trace = 0;
/* Read the next handshake message and discard the payload */
noise_buffer_set_input(noise_msg, buf, len);
HANDLE_NOISE_ERROR(noise_handshakestate_read_message(st->handshake, &noise_msg, NULL), "reading handshake message");
HANDLE_NOISE_ERROR(noise_handshakestate_split(st->handshake, &st->tx_cipher, &st->rx_cipher), "splitting handshake state");
LOG_PRINTF("Noise protocol handshake completed successfully, handshake hash:\n");
if (noise_handshakestate_get_handshake_hash(st->handshake, st->handshake_hash, sizeof(st->handshake_hash)) != NOISE_ERROR_NONE) {
LOG_PRINTF("Error fetching noise handshake state\n");
goto errout;
} else {
LOG_PRINTF(" ");
/*@ loop assigns i; @*/
for (size_t i=0; i<sizeof(st->handshake_hash); i++)
LOG_PRINTF("%02x ", st->handshake_hash[i]);
LOG_PRINTF("\n");
}
NoiseDHState *remote_dh = noise_handshakestate_get_remote_public_key_dh(st->handshake);
if (!remote_dh) {
LOG_PRINTF("Error: Host has not identified itself\n");
goto errout;
}
HANDLE_NOISE_ERROR(noise_dhstate_get_public_key(remote_dh, st->remote_key, sizeof(st->remote_key)), "getting remote pubkey");
/* TODO support list of known remote hosts here instead of just one */
uint8_t remote_fp[BLAKE2S_HASH_SIZE];
BLAKE2s_context_t bc;
BLAKE2s_reset(&bc);
fc_BLAKE2s_update_uint8(&bc, st->remote_key, sizeof(st->remote_key));
BLAKE2s_finish(&bc, remote_fp);
//@ ghost key_checked_trace = 1;
if (!fc_memcmp_uint8(remote_fp, st->remote_key_reference, sizeof(remote_fp))) { /* keys match */
//@ ghost key_match_trace = 1;
uint8_t response = REPORT_PAIRING_SUCCESS;
if (send_encrypted_message(st, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
st->failed_handshakes = 0;
//@ assert 0 <= st->failed_handshakes <= 102;
return 1;
} else { /* keys don't match */
uint8_t response = REPORT_PAIRING_START;
if (send_encrypted_message(st, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
st->failed_handshakes++;
//@ assert 0 <= st->failed_handshakes <= 102;
//@ assert st->failed_handshakes >= old_failed_handshakes;
return 0;
}
errout:
//@ assert 0 <= st->failed_handshakes <= 102;
//@ assert st->failed_handshakes >= old_failed_handshakes;
return -1;
}
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->handshake);
requires validity: \valid(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->remote_key_reference + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->handshake_hash + (0..sizeof(st->handshake_hash)-1));
requires sanity: 0 <= st->failed_handshakes < 100;
requires separation: \separated(usart2_out, st, buf, st->handshake);
ensures result: \result \in {0, -1};
ensures state_legal: st->handshake_state \in
{HANDSHAKE_UNINITIALIZED, HANDSHAKE_PHASE1, HANDSHAKE_PHASE2,
HANDSHAKE_DONE_KNOWN_HOST, HANDSHAKE_DONE_UNKNOWN_HOST};
ensures transition_legal:
\result != -1 ==> \old(st->handshake_state) \in {HANDSHAKE_PHASE1, HANDSHAKE_PHASE2};
ensures transition_legal: (\old(st->handshake_state) == HANDSHAKE_PHASE1)
==> st->handshake_state \in {HANDSHAKE_PHASE2, HANDSHAKE_UNINITIALIZED};
ensures transition_legal: (\old(st->handshake_state) == HANDSHAKE_PHASE2)
==> st->handshake_state \in {HANDSHAKE_DONE_KNOWN_HOST, HANDSHAKE_DONE_UNKNOWN_HOST, HANDSHAKE_UNINITIALIZED};
ensures failure_handling: \result == -1 <==> st->handshake_state == HANDSHAKE_UNINITIALIZED;
ensures failure_handling: \result == -1 ==> st->failed_handshakes > \old(st->failed_handshakes);
ensures state_advance_condition: (st->handshake_state == HANDSHAKE_DONE_KNOWN_HOST) ==> key_match_trace == 1;
//assigns *usart2_out, *st, *st->rx_cipher, *st->tx_cipher, *st->handshake;
//assigns key_checked_trace, key_match_trace;
@*/
int try_continue_noise_handshake(struct NoiseState * const st, uint8_t *buf, size_t len) {
//@ ghost key_checked_trace = 0;
//@ ghost key_match_trace = 0;
int rc;
if (!st->handshake) {
LOG_PRINTF("Error: Invalid handshake state\n");
goto errout;
}
/* Run the protocol handshake */
switch (st->handshake_state) {
case HANDSHAKE_PHASE1:
if (handshake_phase1(st, buf, len))
goto errout;
st->handshake_state = HANDSHAKE_PHASE2;
return 0;
case HANDSHAKE_PHASE2:
rc = handshake_phase2(st, buf, len);
if (rc < 0)
goto errout;
if (rc == 1)
uninit_handshake(st, HANDSHAKE_DONE_KNOWN_HOST);
else
uninit_handshake(st, HANDSHAKE_DONE_UNKNOWN_HOST);
break;
default:
LOG_PRINTF("Invalid handshake state\n");
goto errout;
}
return 0;
errout:
uninit_handshake(st, HANDSHAKE_UNINITIALIZED);
st->failed_handshakes++;
LOG_PRINTF("Noise protocol handshake failed, %d failed attempts\n", st->failed_handshakes);
return -1;
}
/*@
requires validity: \valid(st);
requires validity: \valid_read(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->remote_key_reference + (0..31));
ensures state: st->handshake_state == HANDSHAKE_DONE_KNOWN_HOST;
assigns st->remote_key_reference[0..31], st->handshake_state;
*/
void persist_remote_key(struct NoiseState *st) {
BLAKE2s_context_t bc;
BLAKE2s_reset(&bc);
fc_BLAKE2s_update_uint8(&bc, st->remote_key, sizeof(st->remote_key));
BLAKE2s_finish(&bc, st->remote_key_reference);
st->handshake_state = HANDSHAKE_DONE_KNOWN_HOST;
}
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->tx_cipher) && \valid_read(msg + (0..len-1));
requires separation: \separated(usart2_out, st);
ensures length: !(0 <= len <= MAX_HOST_PACKET_SIZE) <==> \result == -3;
ensures \result \in {0, -1, -2, -3};
assigns *st->tx_cipher, *usart2_out;
*/
int send_encrypted_message(struct NoiseState *st, const uint8_t *msg, size_t len) {
int err;
NoiseBuffer noise_buf;
struct {
struct control_packet header;
uint8_t payload[MAX_HOST_PACKET_SIZE];
} pkt;
if (len > sizeof(pkt.payload)) {
LOG_PRINTF("Packet too long\n");
return -3;
}
if (!st->tx_cipher) {
LOG_PRINTF("Cannot send encrypted packet: Data ciphers not yet initialized\n");
return -1;
}
pkt.header.type = HOST_DATA;
fc_memcpy_uint8(pkt.payload, msg, len); /* This is necessary because noises API doesn't support separate in and out buffers. D'oh! */
noise_buffer_set_inout(noise_buf, pkt.payload, len, sizeof(pkt.payload));
HANDLE_NOISE_ERROR(noise_cipherstate_encrypt(st->tx_cipher, &noise_buf), "encrypting data");
send_packet(usart2_out, (uint8_t *)&pkt, noise_buf.size + sizeof(pkt.header));
return 0;
errout:
return -2;
}

56
fw/src/noise.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef __NOISE_H__
#define __NOISE_H__
#include <stdint.h>
#include <noise/protocol.h>
#include "usart_helpers.h"
#include "rand_stm32.h"
#define CURVE25519_KEY_LEN 32
#define MAX_HOST_PACKET_SIZE 128
extern volatile uint8_t host_packet_buf[MAX_HOST_PACKET_SIZE];
extern volatile int host_packet_length;
enum handshake_state {
HANDSHAKE_UNINITIALIZED,
HANDSHAKE_PHASE1,
HANDSHAKE_PHASE2,
HANDSHAKE_DONE_UNKNOWN_HOST,
HANDSHAKE_DONE_KNOWN_HOST,
};
extern volatile enum handshake_state handshake_state;
struct NoiseState {
NoiseHandshakeState *handshake;
enum handshake_state handshake_state;
NoiseCipherState *tx_cipher, *rx_cipher;
uint8_t *local_key;
uint8_t remote_key[CURVE25519_KEY_LEN];
uint8_t *remote_key_reference;
uint8_t handshake_hash[BLAKE2S_HASH_SIZE];
int failed_handshakes;
};
void uninit_handshake(struct NoiseState *st, enum handshake_state new_state);
void noise_state_init(struct NoiseState *st, uint8_t *remote_key_reference, uint8_t *local_key);
void persist_remote_key(struct NoiseState *st);
int start_protocol_handshake(struct NoiseState *st);
int reset_protocol_handshake(struct NoiseState *st);
int generate_identity_key(struct NoiseState *st);
int try_continue_noise_handshake(struct NoiseState * const st, uint8_t *buf, size_t len);
int send_encrypted_message(struct NoiseState *st, const uint8_t *msg, size_t len);
/*@ assigns \nothing; */
void arm_key_scrubber(void);
/*@ assigns \nothing; */
void disarm_key_scrubber(void);
#endif

98
fw/src/packet_interface.c Normal file
View file

@ -0,0 +1,98 @@
#include "packet_interface.h"
#include "noise.h"
#include "cobs.h"
#include "tracing.h"
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencmsis/core_cm3.h>
volatile struct {
struct dma_buf dma;
uint8_t data[256];
} usart2_buf = { .dma = { .len = sizeof(usart2_buf.data) } };
struct dma_usart_file usart2_out_s = {
.usart = USART2,
.baudrate = 115200,
.dma = DMA1,
.stream = 6,
.channel = 4,
.irqn = NVIC_DMA_IRQ(1, 6),
.buf = &usart2_buf.dma
};
struct dma_usart_file *usart2_out = &usart2_out_s;
void dma1_stream6_isr(void) {
TRACING_SET(TR_HOST_IF_DMA_IRQ);
static unsigned int fifo_errors = 0; /* debug */
if (dma_get_interrupt_flag(usart2_out->dma, usart2_out->stream, DMA_FEIF)) {
/* Ignore FIFO errors as they're 100% non-critical for UART applications */
dma_clear_interrupt_flags(usart2_out->dma, usart2_out->stream, DMA_FEIF);
fifo_errors++;
TRACING_CLEAR(TR_HOST_IF_DMA_IRQ);
return;
}
/* Transfer complete interrupt */
dma_clear_interrupt_flags(usart2_out->dma, usart2_out->stream, DMA_TCIF);
if (usart2_out->buf->wr_pos != usart2_out->buf->xfr_end) /* buffer not empty */
schedule_dma(usart2_out);
TRACING_CLEAR(TR_HOST_IF_DMA_IRQ);
}
void usart2_isr(void) {
TRACING_SET(TR_HOST_IF_USART_IRQ);
static struct cobs_decode_state host_cobs_state = {0};
if (USART2_SR & USART_SR_ORE) { /* Overrun handling */
LOG_PRINTF("USART2 data register overrun\n");
/* Clear interrupt flag */
(void)USART2_DR; /* FIXME make sure this read is not optimized out */
host_packet_length = -1;
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
return;
}
uint8_t data = USART2_DR; /* This automatically acknowledges the IRQ */
if (host_packet_length) {
LOG_PRINTF("USART2 COBS buffer overrun\n");
host_packet_length = -1;
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
return;
}
ssize_t rv = cobs_decode_incremental(&host_cobs_state, (char *)host_packet_buf, sizeof(host_packet_buf), data);
if (rv == 0) {
/* good, empty frame */
LOG_PRINTF("Got empty frame from host\n");
host_packet_length = -1;
} else if (rv == -1) {
/* Decoding frame, wait for next byte */
} else if (rv == -2) {
LOG_PRINTF("Host interface COBS framing error\n");
host_packet_length = -1;
} else if (rv == -3) {
/* invalid empty frame */
LOG_PRINTF("Got double null byte from host\n");
host_packet_length = -1;
} else if (rv == -4) {
/* frame too large */
LOG_PRINTF("Got too large frame from host\n");
host_packet_length = -1;
} else if (rv > 0) {
/* Good, non-empty frame */
host_packet_length = rv;
}
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
}
void send_packet(struct dma_usart_file *f, const uint8_t *data, size_t len) {
/* ignore return value as putf is blocking and always succeeds */
(void)cobs_encode_incremental(f, putf, (char *)data, len);
flush(f);
}

58
fw/src/packet_interface.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef __PACKET_INTERFACE_H__
#define __PACKET_INTERFACE_H__
#include "usart_helpers.h"
extern struct dma_usart_file *usart2_out;
enum control_packet_types {
_HOST_RESERVED = 0,
HOST_INITIATE_HANDSHAKE = 1,
HOST_HANDSHAKE = 2,
HOST_DATA = 3,
HOST_COMM_ERROR = 4,
HOST_CRYPTO_ERROR = 5,
HOST_TOO_MANY_FAILS = 6,
};
enum packet_types {
_REPORT_RESERVED = 0,
REPORT_KEYBOARD= 1,
REPORT_MOUSE= 2,
REPORT_PAIRING_INPUT = 3,
REPORT_PAIRING_SUCCESS = 4,
REPORT_PAIRING_ERROR = 5,
REPORT_PAIRING_START = 6,
};
struct hid_report_packet {
uint8_t type;
union {
struct {
uint8_t len;
uint8_t report[8];
} report;
struct {
char c;
} pairing_input;
};
} __attribute__((__packed__));
struct control_packet {
uint8_t type;
uint8_t payload[0];
} __attribute__((__packed__));
/*@
requires \valid(f);
requires \valid_read(data + (0..len-1));
requires len > 0;
assigns *f;
*/
void send_packet(struct dma_usart_file *f, const uint8_t *data, size_t len);
#endif

257
fw/src/pgp_wordlist Normal file
View file

@ -0,0 +1,257 @@
Hex Even Word Odd Word
00 aardvark adroitness
01 absurd adviser
02 accrue aftermath
03 acme aggregate
04 adrift alkali
05 adult almighty
06 afflict amulet
07 ahead amusement
08 aimless antenna
09 Algol applicant
0A allow Apollo
0B alone armistice
0C ammo article
0D ancient asteroid
0E apple Atlantic
0F artist atmosphere
10 assume autopsy
11 Athens Babylon
12 atlas backwater
13 Aztec barbecue
14 baboon belowground
15 backfield bifocals
16 backward bodyguard
17 banjo bookseller
18 beaming borderline
19 bedlamp bottomless
1A beehive Bradbury
1B beeswax bravado
1C befriend Brazilian
1D Belfast breakaway
1E berserk Burlington
1F billiard businessman
20 bison butterfat
21 blackjack Camelot
22 blockade candidate
23 blowtorch cannonball
24 bluebird Capricorn
25 bombast caravan
26 bookshelf caretaker
27 brackish celebrate
28 breadline cellulose
29 breakup certify
2A brickyard chambermaid
2B briefcase Cherokee
2C Burbank Chicago
2D button clergyman
2E buzzard coherence
2F cement combustion
30 chairlift commando
31 chatter company
32 checkup component
33 chisel concurrent
34 choking confidence
35 chopper conformist
36 Christmas congregate
37 clamshell consensus
38 classic consulting
39 classroom corporate
3A cleanup corrosion
3B clockwork councilman
3C cobra crossover
3D commence crucifix
3E concert cumbersome
3F cowbell customer
40 crackdown Dakota
41 cranky decadence
42 crowfoot December
43 crucial decimal
44 crumpled designing
45 crusade detector
46 cubic detergent
47 dashboard determine
48 deadbolt dictator
49 deckhand dinosaur
4A dogsled direction
4B dragnet disable
4C drainage disbelief
4D dreadful disruptive
4E drifter distortion
4F dropper document
50 drumbeat embezzle
51 drunken enchanting
52 Dupont enrollment
53 dwelling enterprise
54 eating equation
55 edict equipment
56 egghead escapade
57 eightball Eskimo
58 endorse everyday
59 endow examine
5A enlist existence
5B erase exodus
5C escape fascinate
5D exceed filament
5E eyeglass finicky
5F eyetooth forever
60 facial fortitude
61 fallout frequency
62 flagpole gadgetry
63 flatfoot Galveston
64 flytrap getaway
65 fracture glossary
66 framework gossamer
67 freedom graduate
68 frighten gravity
69 gazelle guitarist
6A Geiger hamburger
6B glitter Hamilton
6C glucose handiwork
6D goggles hazardous
6E goldfish headwaters
6F gremlin hemisphere
70 guidance hesitate
71 hamlet hideaway
72 highchair holiness
73 hockey hurricane
74 indoors hydraulic
75 indulge impartial
76 inverse impetus
77 involve inception
78 island indigo
79 jawbone inertia
7A keyboard infancy
7B kickoff inferno
7C kiwi informant
7D klaxon insincere
7E locale insurgent
7F lockup integrate
80 merit intention
81 minnow inventive
82 miser Istanbul
83 Mohawk Jamaica
84 mural Jupiter
85 music leprosy
86 necklace letterhead
87 Neptune liberty
88 newborn maritime
89 nightbird matchmaker
8A Oakland maverick
8B obtuse Medusa
8C offload megaton
8D optic microscope
8E orca microwave
8F payday midsummer
90 peachy millionaire
91 pheasant miracle
92 physique misnomer
93 playhouse molasses
94 Pluto molecule
95 preclude Montana
96 prefer monument
97 preshrunk mosquito
98 printer narrative
99 prowler nebula
9A pupil newsletter
9B puppy Norwegian
9C python October
9D quadrant Ohio
9E quiver onlooker
9F quota opulent
A0 ragtime Orlando
A1 ratchet outfielder
A2 rebirth Pacific
A3 reform pandemic
A4 regain Pandora
A5 reindeer paperweight
A6 rematch paragon
A7 repay paragraph
A8 retouch paramount
A9 revenge passenger
AA reward pedigree
AB rhythm Pegasus
AC ribcage penetrate
AD ringbolt perceptive
AE robust performance
AF rocker pharmacy
B0 ruffled phonetic
B1 sailboat photograph
B2 sawdust pioneer
B3 scallion pocketful
B4 scenic politeness
B5 scorecard positive
B6 Scotland potato
B7 seabird processor
B8 select provincial
B9 sentence proximate
BA shadow puberty
BB shamrock publisher
BC showgirl pyramid
BD skullcap quantity
BE skydive racketeer
BF slingshot rebellion
C0 slowdown recipe
C1 snapline recover
C2 snapshot repellent
C3 snowcap replica
C4 snowslide reproduce
C5 solo resistor
C6 southward responsive
C7 soybean retraction
C8 spaniel retrieval
C9 spearhead retrospect
CA spellbind revenue
CB spheroid revival
CC spigot revolver
CD spindle sandalwood
CE spyglass sardonic
CF stagehand Saturday
D0 stagnate savagery
D1 stairway scavenger
D2 standard sensation
D3 stapler sociable
D4 steamship souvenir
D5 sterling specialist
D6 stockman speculate
D7 stopwatch stethoscope
D8 stormy stupendous
D9 sugar supportive
DA surmount surrender
DB suspense suspicious
DC sweatband sympathy
DD swelter tambourine
DE tactics telephone
DF talon therapist
E0 tapeworm tobacco
E1 tempest tolerance
E2 tiger tomorrow
E3 tissue torpedo
E4 tonic tradition
E5 topmost travesty
E6 tracker trombonist
E7 transit truncated
E8 trauma typewriter
E9 treadmill ultimate
EA Trojan undaunted
EB trouble underfoot
EC tumor unicorn
ED tunnel unify
EE tycoon universe
EF uncut unravel
F0 unearth upcoming
F1 unwind vacancy
F2 uproot vagabond
F3 upset vertigo
F4 upshot Virginia
F5 vapor visitor
F6 village vocalist
F7 virus voyager
F8 Vulcan warranty
F9 waffle Waterloo
FA wallet whimsical
FB watchword Wichita
FC wayside Wilmington
FD willow Wyoming
FE woodlark yesteryear
FF Zulu Yucatan

135
fw/src/rand_stm32.c Normal file
View file

@ -0,0 +1,135 @@
/* Quick-and-dirty cryptographic RNG based on BLAKE2s
*
* This system uses a 32-byte BLAKE2s hash as internal state seeded by somewhat random post-powerup SRAM content, the
* unique device ID and the program flash contents. This seed state is mixed with values from the hardware RNG for each
* 32-byte block of output data.
*
* The RNG's chaining looks like the following, with H(...) being the BLAKE2s hash function, | being binary
* concatenation and hw_rng(...) being the hardware RNG. c and e are the fixed extraction and chain string constants
* defined below.
*
* Seed: state = H(SRAM | FLASH | hw_rng(64 byte))
*
* Extract: state = H(state | c | hw_rng(64 byte)) block[0] = H(state | e)
* state = H(state | c | hw_rng(64 byte)) block[1] = H(state | e)
* [...]
* state = H(state | c | hw_rng(64 byte)) block[n] = H(state | e)
* state = H(state | c | hw_rng(64 byte))
*
*
* Graphically, with C = H( state | c | rng ) being the chaining function
* and X = H( state | e ) being the extraction function
* this becomes:
*
* rng rng rng rng
* | | | |
* v v v v
* state ---> [C] ---> [C] -- . . . --> [C] ---> [C] ---> new state
* | | |
* v v v
* [X] [X] [X]
* | | |
* v v v
* out[0] out[1] . . . out[n]
*
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libopencm3/stm32/f4/rng.h>
#include "usart_helpers.h"
#include "rand_stm32.h"
#include "tracing.h"
#include "crypto/noise-c/src/protocol/internal.h"
#include "crypto/noise-c/src/crypto/blake2/blake2s.h"
/* FIXME persist state in backup sram */
extern unsigned _ram_start, _ram_end, _rom_start, _rom_end;
static uint8_t global_stm_rand_state[BLAKE2S_HASH_SIZE];
static uint32_t stm32_read_rng_raw(void) {
if ((RNG_SR & (RNG_SR_SEIS | RNG_SR_CEIS)) || !(RNG_CR & RNG_CR_RNGEN)) {
LOG_PRINTF("RNG error detected, bailing out.\n");
exit(1);
}
while (!(RNG_SR & RNG_SR_DRDY))
;
return RNG_DR;
}
static void rng_seed_blake(BLAKE2s_context_t *bc) {
/* This pulls out 64 bytes. Even though the resulting BLAKE2s hash only is 32 bytes large, the internal state of
* BLAKE2s is larger. Also I don't quite trust the STM32F4's hardware RNG. */
for (int i=0; i<16; i++) {
uint32_t val = stm32_read_rng_raw();
BLAKE2s_update(bc, &val, sizeof(val));
}
}
void rand_init() {
RNG_CR |= RNG_CR_RNGEN;
BLAKE2s_context_t bc;
BLAKE2s_reset(&bc);
/* Seed with entire SRAM area */
BLAKE2s_update(&bc, &_ram_start, &_ram_end - &_ram_start);
/* Seed with entire flash area. This includes the device unique ID if it has not been overwritten. */
BLAKE2s_update(&bc, &_rom_start, &_rom_end - &_rom_start);
/* Seed with 64 bytes of handware RNG input */
rng_seed_blake(&bc);
/* FIXME use ADC to seeed */
BLAKE2s_finish(&bc, global_stm_rand_state);
/* FIXME make sure this is not optimized out */
memset(&bc, 0, sizeof(bc));
}
const char *extraction_constant = "Blake2 RNG extraction constant";
const char *chain_constant = "Blake2 RNG chaining constant";
void noise_rand_bytes(void *bytes, size_t size) {
TRACING_SET(TR_RNG);
BLAKE2s_context_t out_ctx, chain_ctx;
uint8_t *out = (uint8_t *)bytes;
uint8_t hash_buf[BLAKE2S_HASH_SIZE];
for (size_t wr_pos = 0; wr_pos<size; wr_pos += BLAKE2S_HASH_SIZE) {
BLAKE2s_reset(&chain_ctx);
BLAKE2s_update(&chain_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&chain_ctx, chain_constant, strlen(chain_constant));
rng_seed_blake(&chain_ctx);
BLAKE2s_finish(&chain_ctx, global_stm_rand_state);
BLAKE2s_reset(&out_ctx);
BLAKE2s_update(&out_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&out_ctx, extraction_constant, strlen(extraction_constant));
BLAKE2s_finish(&out_ctx, hash_buf);
size_t rem = size-wr_pos;
memcpy(&out[wr_pos], hash_buf, rem < BLAKE2S_HASH_SIZE ? rem : BLAKE2S_HASH_SIZE);
}
BLAKE2s_reset(&chain_ctx);
BLAKE2s_update(&chain_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&chain_ctx, chain_constant, strlen(chain_constant));
rng_seed_blake(&chain_ctx);
BLAKE2s_finish(&chain_ctx, global_stm_rand_state);
/* FIXME make sure this is not optimized out */
memset(&out_ctx, 0, sizeof(out_ctx));
memset(&chain_ctx, 0, sizeof(chain_ctx));
memset(hash_buf, 0, sizeof(hash_buf));
TRACING_CLEAR(TR_RNG);
}
#ifdef ED25519_CUSTOMRANDOM /* We are building against ed25519-donna, which needs a random function */
void ed25519_randombytes_unsafe(void *p, size_t len) {
noise_rand_bytes(p, len);
}
#endif

11
fw/src/rand_stm32.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef __RAND_STM32_H__
#define __RAND_STM32_H__
#include <stdint.h>
#include <unistd.h>
#define BLAKE2S_HASH_SIZE 32
void rand_init(void);
#endif

523
fw/src/tinyprintf.c Normal file
View file

@ -0,0 +1,523 @@
/*
File: tinyprintf.c
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tinyprintf.h"
/*
* Configuration
*/
/* Enable long int support */
#define PRINTF_LONG_SUPPORT
/* Enable long long int support (implies long int support) */
#define PRINTF_LONG_LONG_SUPPORT
/* Enable %z (size_t) support */
#define PRINTF_SIZE_T_SUPPORT
/*
* Configuration adjustments
*/
#ifdef PRINTF_SIZE_T_SUPPORT
#include <sys/types.h>
#endif
#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
/* __SIZEOF_<type>__ defined at least by gcc */
#ifdef __SIZEOF_POINTER__
# define SIZEOF_POINTER __SIZEOF_POINTER__
#endif
#ifdef __SIZEOF_LONG_LONG__
# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__
#endif
#ifdef __SIZEOF_LONG__
# define SIZEOF_LONG __SIZEOF_LONG__
#endif
#ifdef __SIZEOF_INT__
# define SIZEOF_INT __SIZEOF_INT__
#endif
#ifdef __GNUC__
# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline))
#else
# define _TFP_GCC_NO_INLINE_
#endif
/*
* Implementation
*/
struct param {
char lz:1; /**< Leading zeros */
char alt:1; /**< alternate form */
char uc:1; /**< Upper case (for base16 only) */
char align_left:1; /**< 0 == align right (default), 1 == align left */
unsigned int width; /**< field width */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
};
#ifdef PRINTF_LONG_LONG_SUPPORT
static void _TFP_GCC_NO_INLINE_ ulli2a(
unsigned long long int num, struct param *p)
{
int n = 0;
unsigned long long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void lli2a(long long int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ulli2a(num, p);
}
#endif
#ifdef PRINTF_LONG_SUPPORT
static void uli2a(unsigned long int num, struct param *p)
{
int n = 0;
unsigned long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void li2a(long num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
uli2a(num, p);
}
#endif
static void ui2a(unsigned int num, struct param *p)
{
int n = 0;
unsigned int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void i2a(int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ui2a(num, p);
}
static int a2d(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else
return -1;
}
static char a2u(char ch, const char **src, int base, unsigned int *nump)
{
const char *p = *src;
unsigned int num = 0;
int digit;
while ((digit = a2d(ch)) >= 0) {
if (digit > base)
break;
num = num * base + digit;
ch = *p++;
}
*src = p;
*nump = num;
return ch;
}
static void putchw(void *putp, putcf putf, struct param *p)
{
char ch;
int n = p->width;
char *bf = p->bf;
/* Number of filling characters */
while (*bf++ && n > 0)
n--;
if (p->sign)
n--;
if (p->alt && p->base == 16)
n -= 2;
else if (p->alt && p->base == 8)
n--;
/* Fill with space to align to the right, before alternate or sign */
if (!p->lz && !p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
/* print sign */
if (p->sign)
putf(putp, p->sign);
/* Alternate */
if (p->alt && p->base == 16) {
putf(putp, '0');
putf(putp, (p->uc ? 'X' : 'x'));
} else if (p->alt && p->base == 8) {
putf(putp, '0');
}
/* Fill with zeros, after alternate or sign */
if (p->lz) {
while (n-- > 0)
putf(putp, '0');
}
/* Put actual buffer */
bf = p->bf;
while ((ch = *bf++))
putf(putp, ch);
/* Fill with space to align to the left, after string */
if (!p->lz && p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
}
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
{
struct param p;
#ifdef PRINTF_LONG_SUPPORT
char bf[23]; /* long = 64b on some architectures */
#else
char bf[12]; /* int = 32b on some architectures */
#endif
char ch;
p.bf = bf;
while ((ch = *(fmt++))) {
if (ch != '%') {
putf(putp, ch);
} else {
#ifdef PRINTF_LONG_SUPPORT
char lng = 0; /* 1 for long, 2 for long long */
#endif
/* Init parameter struct */
p.lz = 0;
p.alt = 0;
p.width = 0;
p.align_left = 0;
p.sign = 0;
/* Flags */
while ((ch = *(fmt++))) {
switch (ch) {
case '-':
p.align_left = 1;
continue;
case '0':
p.lz = 1;
continue;
case '#':
p.alt = 1;
continue;
default:
break;
}
break;
}
/* Width */
if (ch >= '0' && ch <= '9') {
ch = a2u(ch, &fmt, 10, &(p.width));
}
/* We accept 'x.y' format but don't support it completely:
* we ignore the 'y' digit => this ignores 0-fill
* size and makes it == width (ie. 'x') */
if (ch == '.') {
p.lz = 1; /* zero-padding */
/* ignore actual 0-fill size: */
do {
ch = *(fmt++);
} while ((ch >= '0') && (ch <= '9'));
}
#ifdef PRINTF_SIZE_T_SUPPORT
# ifdef PRINTF_LONG_SUPPORT
if (ch == 'z') {
ch = *(fmt++);
if (sizeof(size_t) == sizeof(unsigned long int))
lng = 1;
# ifdef PRINTF_LONG_LONG_SUPPORT
else if (sizeof(size_t) == sizeof(unsigned long long int))
lng = 2;
# endif
} else
# endif
#endif
#ifdef PRINTF_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 1;
#ifdef PRINTF_LONG_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 2;
}
#endif
}
#endif
switch (ch) {
case 0:
goto abort;
case 'u':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'd':
case 'i':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
lli2a(va_arg(va, long long int), &p);
else
#endif
if (1 == lng)
li2a(va_arg(va, long int), &p);
else
#endif
i2a(va_arg(va, int), &p);
putchw(putp, putf, &p);
break;
#ifdef SIZEOF_POINTER
case 'p':
p.alt = 1;
# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT
lng = 0;
# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG
lng = 1;
# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG
lng = 2;
# endif
#endif
__attribute__((fallthrough));
case 'x':
__attribute__((fallthrough));
case 'X':
p.base = 16;
p.uc = (ch == 'X')?1:0;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'o':
p.base = 8;
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'c':
putf(putp, (char)(va_arg(va, int)));
break;
case 's':
p.bf = va_arg(va, char *);
putchw(putp, putf, &p);
p.bf = bf;
break;
case '%':
putf(putp, ch);
default:
break;
}
}
}
abort:;
}
#if TINYPRINTF_DEFINE_TFP_PRINTF
static putcf stdout_putf;
static void *stdout_putp;
void init_printf(void *putp, putcf putf)
{
stdout_putf = putf;
stdout_putp = putp;
}
void tfp_printf(char *fmt, ...)
{
va_list va;
va_start(va, fmt);
tfp_format(stdout_putp, stdout_putf, fmt, va);
va_end(va);
}
#endif
#if TINYPRINTF_DEFINE_TFP_SPRINTF
struct _vsnprintf_putcf_data
{
size_t dest_capacity;
char *dest;
size_t num_chars;
};
static void _vsnprintf_putcf(void *p, char c)
{
struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p;
if (data->num_chars < data->dest_capacity)
data->dest[data->num_chars] = c;
data->num_chars ++;
}
int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
struct _vsnprintf_putcf_data data;
if (size < 1)
return 0;
data.dest = str;
data.dest_capacity = size-1;
data.num_chars = 0;
tfp_format(&data, _vsnprintf_putcf, format, ap);
if (data.num_chars < data.dest_capacity)
data.dest[data.num_chars] = '\0';
else
data.dest[data.dest_capacity] = '\0';
return data.num_chars;
}
int tfp_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsnprintf(str, size, format, ap);
va_end(ap);
return retval;
}
struct _vsprintf_putcf_data
{
char *dest;
size_t num_chars;
};
static void _vsprintf_putcf(void *p, char c)
{
struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p;
data->dest[data->num_chars++] = c;
}
int tfp_vsprintf(char *str, const char *format, va_list ap)
{
struct _vsprintf_putcf_data data;
data.dest = str;
data.num_chars = 0;
tfp_format(&data, _vsprintf_putcf, format, ap);
data.dest[data.num_chars] = '\0';
return data.num_chars;
}
int tfp_sprintf(char *str, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsprintf(str, format, ap);
va_end(ap);
return retval;
}
#endif

186
fw/src/tinyprintf.h Normal file
View file

@ -0,0 +1,186 @@
/*
File: tinyprintf.h
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'.
They provide a simple and small (+400 loc) printf functionality to
be used in embedded systems.
I've found them so useful in debugging that I do not bother with a
debugger at all.
They are distributed in source form, so to use them, just compile them
into your project.
Two printf variants are provided: printf and the 'sprintf' family of
functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf').
The formats supported by this implementation are:
'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'.
Zero padding and field width are also supported.
If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then
the long specifier is also supported. Note that this will pull in some
long math routines (pun intended!) and thus make your executable
noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the
long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t
specifier.
The memory footprint of course depends on the target CPU, compiler and
compiler options, but a rough guesstimate (based on a H8S target) is about
1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space.
Not too bad. Your mileage may vary. By hacking the source code you can
get rid of some hundred bytes, I'm sure, but personally I feel the balance of
functionality and flexibility versus code size is close to optimal for
many embedded systems.
To use the printf, you need to supply your own character output function,
something like :
void putc ( void* p, char c)
{
while (!SERIAL_PORT_EMPTY) ;
SERIAL_PORT_TX_REGISTER = c;
}
Before you can call printf, you need to initialize it to use your
character output function with something like:
init_printf(NULL,putc);
Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc',
the NULL (or any pointer) you pass into the 'init_printf' will eventually be
passed to your 'putc' routine. This allows you to pass some storage space (or
anything really) to the character output function, if necessary.
This is not often needed but it was implemented like that because it made
implementing the sprintf function so neat (look at the source code).
The code is re-entrant, except for the 'init_printf' function, so it is safe
to call it from interrupts too, although this may result in mixed output.
If you rely on re-entrancy, take care that your 'putc' function is re-entrant!
The printf and sprintf functions are actually macros that translate to
'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set
(default). Setting it to 0 makes it possible to use them along with
'stdio.h' printf's in a single source file. When
'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are
not function-like macros, so if you have variables or struct members
with these names, things will explode in your face. Without variadic
macros this is the best we can do to wrap these function. If it is a
problem, just give up the macros and use the functions directly, or
rename them.
It is also possible to avoid defining tfp_printf and/or tfp_sprintf by
clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or
'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to
export only tfp_format, which is at the core of all the other
functions.
For further details see source code.
regs Kusti, 23.10.2004
*/
#ifndef __TFP_PRINTF__
#define __TFP_PRINTF__
#include <stdarg.h>
/* Global configuration */
/* Set this to 0 if you do not want to provide tfp_printf */
#ifndef TINYPRINTF_DEFINE_TFP_PRINTF
# define TINYPRINTF_DEFINE_TFP_PRINTF 1
#endif
/* Set this to 0 if you do not want to provide
tfp_sprintf/snprintf/vsprintf/vsnprintf */
#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF
# define TINYPRINTF_DEFINE_TFP_SPRINTF 1
#endif
/* Set this to 0 if you do not want tfp_printf and
tfp_{vsn,sn,vs,s}printf to be also available as
printf/{vsn,sn,vs,s}printf */
#ifndef TINYPRINTF_OVERRIDE_LIBC
# define TINYPRINTF_OVERRIDE_LIBC 1
#endif
/* Optional external types dependencies */
#if TINYPRINTF_DEFINE_TFP_SPRINTF
# include <sys/types.h> /* size_t */
#endif
/* Declarations */
#ifdef __GNUC__
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \
__attribute__((format (printf, fmt_idx, arg1_idx)))
#else
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*putcf) (void *, char);
/*
'tfp_format' really is the central function for all tinyprintf. For
each output character after formatting, the 'putf' callback is
called with 2 args:
- an arbitrary void* 'putp' param defined by the user and
passed unmodified from 'tfp_format',
- the character.
The 'tfp_printf' and 'tfp_sprintf' functions simply define their own
callback and pass to it the right 'putp' it is expecting.
*/
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va);
#if TINYPRINTF_DEFINE_TFP_SPRINTF
int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(3, 4);
int tfp_vsprintf(char *str, const char *fmt, va_list ap);
int tfp_sprintf(char *str, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(2, 3);
# if TINYPRINTF_OVERRIDE_LIBC
# define vsnprintf tfp_vsnprintf
# define snprintf tfp_snprintf
# define vsprintf tfp_vsprintf
# define sprintf tfp_sprintf
# endif
#endif
#if TINYPRINTF_DEFINE_TFP_PRINTF
void init_printf(void *putp, putcf putf);
void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
# if TINYPRINTF_OVERRIDE_LIBC
# define printf tfp_printf
# endif
#endif
#ifdef __cplusplus
}
#endif
#endif

25
fw/src/tracing.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef __TRACING_H__
#define __TRACING_H__
#include <libopencm3/stm32/gpio.h>
#ifndef VERIFICATION
#define TRACING_SET(i) gpio_set(GPIOD, (1<<i))
#define TRACING_CLEAR(i) gpio_clear(GPIOD, (1<<i))
#else
#define TRACING_SET(i) ((void)0)
#define TRACING_CLEAR(i) ((void)0)
#endif
enum tracing_channels {
TR_HID_MESSAGE_HANDLER = 0,
TR_DEBUG_OUT_DMA_IRQ = 1,
TR_HOST_IF_DMA_IRQ = 2,
TR_HOST_IF_USART_IRQ = 3,
TR_USBH_POLL = 4,
TR_HOST_PKT_HANDLER = 5,
TR_NOISE_HANDSHAKE = 6,
TR_RNG = 7,
};
#endif

149
fw/src/usart_helpers.c Normal file
View file

@ -0,0 +1,149 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usart_helpers.h"
#define TINYPRINTF_OVERRIDE_LIBC 0
#define TINYPRINTF_DEFINE_TFP_SPRINTF 0
#include "tinyprintf.h"
#include "cobs.h"
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencmsis/core_cm3.h>
void usart_fprintf(struct dma_usart_file *f, const char *str, ...) {
va_list va;
va_start(va, str);
tfp_format(f, (void (*)(void *, char c))putf, str, va);
va_end(va);
flush(f);
}
void usart_init(uint32_t arg_usart, uint32_t baudrate) {
usart_set_baudrate(arg_usart, baudrate);
usart_set_databits(arg_usart, 8);
usart_set_flow_control(arg_usart, USART_FLOWCONTROL_NONE);
usart_set_mode(arg_usart, USART_MODE_TX | USART_MODE_RX);
usart_set_parity(arg_usart, USART_PARITY_NONE);
usart_set_stopbits(arg_usart, USART_STOPBITS_1);
usart_enable(arg_usart);
}
void usart_dma_init(struct dma_usart_file *f) {
usart_init(f->usart, f->baudrate);
f->buf->xfr_start = -1,
f->buf->xfr_end = 0,
f->buf->wr_pos = 0,
dma_stream_reset(f->dma, f->stream);
dma_channel_select(f->dma, f->stream, DMA_SxCR_CHSEL(f->channel));
dma_set_peripheral_address(f->dma, f->stream, (uint32_t)&USART_DR(f->usart));
dma_set_transfer_mode(f->dma, f->stream, DMA_SxCR_DIR_MEM_TO_PERIPHERAL);
dma_enable_memory_increment_mode(f->dma, f->stream);
dma_set_peripheral_size(f->dma, f->stream, DMA_SxCR_PSIZE_8BIT);
dma_set_memory_size(f->dma, f->stream, DMA_SxCR_MSIZE_8BIT);
dma_set_priority(f->dma, f->stream, DMA_SxCR_PL_VERY_HIGH);
dma_enable_transfer_complete_interrupt(f->dma, f->stream);
dma_enable_fifo_error_interrupt(f->dma, f->stream);
usart_enable_tx_dma(f->usart);
}
void usart_kickoff_dma(uint32_t dma, uint8_t stream, volatile uint8_t *buf, size_t len) {
/* initiate transmission of new buffer */
dma_set_memory_address(dma, stream, (uint32_t)buf); /* select active buffer address */
dma_set_number_of_data(dma, stream, len);
dma_enable_stream(dma, stream);
}
void schedule_dma(volatile struct dma_usart_file *f) {
volatile struct dma_buf *buf = f->buf;
uint32_t xfr_len, xfr_start = buf->xfr_end;
if (buf->wr_pos > xfr_start) /* no wraparound */
xfr_len = buf->wr_pos - xfr_start;
else /* wraparound */
xfr_len = buf->len - xfr_start; /* schedule transfer until end of buffer */
buf->xfr_start = xfr_start;
buf->xfr_end = (xfr_start + xfr_len) % buf->len; /* handle wraparound */
usart_kickoff_dma(f->dma, f->stream, buf->data + xfr_start, xfr_len);
}
int dma_fifo_push(volatile struct dma_buf *buf, char c) {
if (buf->wr_pos == buf->xfr_start)
return -EBUSY;
buf->data[buf->wr_pos] = c;
buf->wr_pos = (buf->wr_pos + 1) % buf->len;
return 0;
}
int putf(void *file, char c) {
volatile struct dma_usart_file *f = (struct dma_usart_file *)file;
nvic_disable_irq(f->irqn);
/* push char to fifo, busy-loop if stalled to wait for USART to empty fifo via DMA */
while (dma_fifo_push(f->buf, c) == -EBUSY) {
nvic_enable_irq(f->irqn);
flush(f);
nvic_disable_irq(f->irqn);
}
nvic_enable_irq(f->irqn);
return 0;
}
int putb(void *file, const uint8_t *buf, size_t len) {
volatile struct dma_usart_file *f = (struct dma_usart_file *)file;
nvic_disable_irq(f->irqn);
for (size_t i=0; i<len; i++) {
/* push char to fifo, busy-loop if stalled to wait for USART to empty fifo via DMA */
while (dma_fifo_push(f->buf, buf[i]) == -EBUSY) {
nvic_enable_irq(f->irqn);
nvic_disable_irq(f->irqn);
}
}
nvic_enable_irq(f->irqn);
return 0;
}
void flush(void *file) {
volatile struct dma_usart_file *f = (struct dma_usart_file *)file;
nvic_disable_irq(f->irqn);
/* If the DMA stream is idle right now, schedule a transfer */
if (!(DMA_SCR(f->dma, f->stream) & DMA_SxCR_EN)) { /* DMA is not running */
//&& !dma_get_interrupt_flag(f->dma, f->stream, DMA_TCIF)/* DMA interrupt is clear */) {
dma_clear_interrupt_flags(f->dma, f->stream, DMA_TCIF);
schedule_dma(f);
}
nvic_enable_irq(f->irqn);
}

88
fw/src/usart_helpers.h Normal file
View file

@ -0,0 +1,88 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_USART_HELPERS_H
#define USBH_USART_HELPERS_H
#include "usbh_core.h"
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>
BEGIN_DECLS
struct dma_buf {
uint32_t xfr_start; /* Start index of running DMA transfer */
uint32_t xfr_end; /* End index of running DMA transfer plus one */
uint32_t wr_pos; /* Next index to be written */
uint32_t len;
uint8_t data[0];
};
struct dma_usart_file {
uint32_t usart;
uint32_t baudrate;
uint32_t dma;
uint8_t stream;
uint8_t channel;
uint8_t irqn;
volatile struct dma_buf *buf;
};
extern struct dma_usart_file *debug_out;
void usart_init(uint32_t usart, uint32_t baudrate);
void usart_fprintf(struct dma_usart_file *f, const char *str, ...);
void usart_fifo_push(uint8_t c);
void usart_dma_init(struct dma_usart_file *f);
void usart_kickoff_dma(uint32_t dma, uint8_t stream, volatile uint8_t *buf, size_t len);
void schedule_dma(volatile struct dma_usart_file *f);
int dma_fifo_push(volatile struct dma_buf *buf, char c);
int putf(void *file, char c);
int putb(void *file, const uint8_t *buf, size_t len);
void flush(void *file);
/* This macro abomination templates a bunch of dma-specific register/constant names from preprocessor macros passed in
* from cmake. */
#define DMA_PASTE(num) DMA ## num
#define DMA(num) DMA_PASTE(num)
#define NVIC_DMA_IRQ_PASTE(dma, stream) NVIC_ ## DMA ## dma ## _ ## STREAM ## stream ## _IRQ
#define NVIC_DMA_IRQ(dma, stream) NVIC_DMA_IRQ_PASTE(dma, stream)
#define DMA_ISR_PASTE(dma, stream) DMA ## dma ## _ ## STREAM ## stream ## _IRQHandler
#define DMA_ISR(dma, stream) DMA_ISR_PASTE(dma, stream)
#ifdef USART_DEBUG
#define LOG_PRINTF(format, ...) usart_fprintf(debug_out, format, ##__VA_ARGS__);
#else
#define LOG_PRINTF(dummy, ...) ((void)dummy)
#endif
#define UNUSED(var) ((void)var)
END_DECLS
#endif

726
fw/src/usbh_core.c Normal file
View file

@ -0,0 +1,726 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usbh_config.h"
#include "usbh_lld_stm32f4.h"
#include "driver/usbh_device_driver.h"
#include "usart_helpers.h"
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbstd.h>
#include <stddef.h>
static struct {
bool enumeration_run;
const usbh_low_level_driver_t * const *lld_drivers;
const usbh_dev_driver_t * const *dev_drivers;
int8_t address_temporary;
} usbh_data = {};
static void set_enumeration(void)
{
usbh_data.enumeration_run = true;
}
static void reset_enumeration(void)
{
usbh_data.enumeration_run = false;
}
static bool enumeration(void)
{
return usbh_data.enumeration_run;
}
void device_remove(usbh_device_t *dev)
{
if (dev->drv && dev->drvdata) {
dev->drv->remove(dev->drvdata);
}
dev->address = -1;
dev->drv = NULL;
dev->drvdata = NULL;
}
/**
*
*/
static bool find_driver(usbh_device_t *dev, const usbh_dev_driver_info_t * device_info)
{
#define CHECK_PARTIAL_COMPATIBILITY(what) \
if (usbh_data.dev_drivers[i]->info->what != -1\
&& device_info->what != usbh_data.dev_drivers[i]->info->what) {\
i++;\
continue;\
}
int i = 0;
while (usbh_data.dev_drivers[i]) {
CHECK_PARTIAL_COMPATIBILITY(ifaceClass);
CHECK_PARTIAL_COMPATIBILITY(ifaceSubClass);
CHECK_PARTIAL_COMPATIBILITY(ifaceProtocol);
CHECK_PARTIAL_COMPATIBILITY(deviceClass);
CHECK_PARTIAL_COMPATIBILITY(deviceSubClass);
CHECK_PARTIAL_COMPATIBILITY(deviceProtocol);
CHECK_PARTIAL_COMPATIBILITY(idVendor);
CHECK_PARTIAL_COMPATIBILITY(idProduct);
dev->drv = usbh_data.dev_drivers[i];
dev->drvdata = dev->drv->init(dev);
if (!dev->drvdata) {
LOG_PRINTF("Unable to initialize device driver at index %d\n", i);
i++;
continue;
}
return true;
}
return false;
#undef CHECK_PARTIAL_COMPATIBILITY
}
static void device_register(void *descriptors, uint16_t descriptors_len, usbh_device_t *dev)
{
uint32_t i = 0;
uint8_t *buf = (uint8_t *)descriptors;
dev->drv = NULL;
dev->drvdata = NULL;
uint8_t desc_len = buf[i];
uint8_t desc_type = buf[i + 1];
usbh_dev_driver_info_t device_info;
if (desc_type == USB_DT_DEVICE) {
struct usb_device_descriptor *device_desc = (void*)&buf[i];
LOG_PRINTF("DEVICE DESCRIPTOR\n");
device_info.deviceClass = device_desc->bDeviceClass;
device_info.deviceSubClass = device_desc->bDeviceSubClass;
device_info.deviceProtocol = device_desc->bDeviceProtocol;
device_info.idVendor = device_desc->idVendor;
device_info.idProduct = device_desc->idProduct;
} else {
LOG_PRINTF("INVALID descriptors pointer - fatal error");
return;
}
while (i < descriptors_len) {
desc_len = buf[i];
desc_type = buf[i + 1];
switch (desc_type) {
case USB_DT_INTERFACE:
{
LOG_PRINTF("INTERFACE_DESCRIPTOR\n");
struct usb_interface_descriptor *iface = (void*)&buf[i];
device_info.ifaceClass = iface->bInterfaceClass;
device_info.ifaceSubClass = iface->bInterfaceSubClass;
device_info.ifaceProtocol = iface->bInterfaceProtocol;
if (find_driver(dev, &device_info)) {
int k = 0;
while (k < descriptors_len) {
desc_len = buf[k];
void *drvdata = dev->drvdata;
LOG_PRINTF("[%d]", buf[k+1]);
if (dev->drv->analyze_descriptor(drvdata, &buf[k])) {
LOG_PRINTF("Device Initialized\n");
return;
}
if (desc_len == 0) {
LOG_PRINTF("Problem occured while parsing complete configuration descriptor");
return;
}
k += desc_len;
}
LOG_PRINTF("Device driver isn't compatible with this device\n");
device_remove(dev);
} else {
LOG_PRINTF("No compatible driver has been found for interface #%d\n", iface->bInterfaceNumber);
}
}
break;
default:
break;
}
if (desc_len == 0) {
LOG_PRINTF("PROBLEM WITH PARSE %d\n",i);
return;
}
i += desc_len;
}
LOG_PRINTF("Device NOT Initialized\n");
}
void usbh_init(const usbh_low_level_driver_t * const low_level_drivers[], const usbh_dev_driver_t * const device_drivers[])
{
if (!low_level_drivers) {
return;
}
usbh_data.lld_drivers = (const usbh_low_level_driver_t **)low_level_drivers;
usbh_data.dev_drivers = device_drivers;
uint32_t k = 0;
while (usbh_data.lld_drivers[k]) {
LOG_PRINTF("Initialization low-level driver with index=%d\n", k);
usbh_device_t * usbh_device =
((usbh_generic_data_t *)(usbh_data.lld_drivers[k])->driver_data)->usbh_device;
uint32_t i;
for (i = 0; i < USBH_MAX_DEVICES; i++) {
//~ LOG_PRINTF("%p ", &usbh_device[i]);
usbh_device[i].address = -1;
usbh_device[i].drv = 0;
usbh_device[i].drvdata = 0;
}
usbh_data.lld_drivers[k]->init(usbh_data.lld_drivers[k]->driver_data);
k++;
}
}
static void device_xfer_control_write_setup(const void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data.out = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_ENDPOINT_TYPE_CONTROL;
packet.control_type = USBH_CONTROL_TYPE_SETUP;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_write(dev, &packet);
LOG_PRINTF("WR-setup@device...%d \n", dev->address);
}
static void device_xfer_control_write_data(const void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data.out = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_ENDPOINT_TYPE_CONTROL;
packet.control_type = USBH_CONTROL_TYPE_DATA;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_write(dev, &packet);
LOG_PRINTF("WR-data@device...%d \n", dev->address);
}
static void device_xfer_control_read(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data.in = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_ENDPOINT_TYPE_CONTROL;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_read(dev, &packet);
LOG_PRINTF("RD@device...%d | \n", dev->address);
}
static void control_state_machine(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
switch (dev->control.state) {
case USBH_CONTROL_STATE_SETUP:
if (cb_data.status != USBH_PACKET_CALLBACK_STATUS_OK) {
dev->control.state = USBH_CONTROL_STATE_NONE;
// Unable to deliver setup control packet - this is a fatal error
usbh_packet_callback_data_t ret_data;
ret_data.status = USBH_PACKET_CALLBACK_STATUS_EFATAL;
ret_data.transferred_length = 0;
dev->control.callback(dev, ret_data);
break;
}
if (dev->control.setup_data.bmRequestType & USB_REQ_TYPE_IN) {
dev->control.state = USBH_CONTROL_STATE_DATA;
device_xfer_control_read(dev->control.data.in, dev->control.data_length, control_state_machine, dev);
} else {
if (dev->control.data_length == 0) {
dev->control.state = USBH_CONTROL_STATE_STATUS;
device_xfer_control_read(NULL, 0, control_state_machine, dev);
} else {
dev->control.state = USBH_CONTROL_STATE_DATA;
device_xfer_control_write_data(dev->control.data.out, dev->control.data_length, control_state_machine, dev);
}
}
break;
case USBH_CONTROL_STATE_DATA:
if (dev->control.setup_data.bmRequestType & USB_REQ_TYPE_IN) {
dev->control.state = USBH_CONTROL_STATE_NONE;
dev->control.callback(dev, cb_data);
} else {
if (cb_data.status != USBH_PACKET_CALLBACK_STATUS_OK) {
dev->control.state = USBH_CONTROL_STATE_NONE;
// Unable to deliver data control packet - this is a fatal error
usbh_packet_callback_data_t ret_data;
ret_data.status = USBH_PACKET_CALLBACK_STATUS_EFATAL;
ret_data.transferred_length = 0;
dev->control.callback(dev, ret_data);
break;
}
if (dev->control.data_length == 0) {
// we should be in status state when the length of data is zero
LOG_PRINTF("Control logic error\n");
dev->control.state = USBH_CONTROL_STATE_NONE;
dev->control.callback(dev, cb_data);
} else {
dev->control.state = USBH_CONTROL_STATE_STATUS;
device_xfer_control_read(NULL, 0, control_state_machine, dev);
}
}
break;
case USBH_CONTROL_STATE_STATUS:
dev->control.state = USBH_CONTROL_STATE_NONE;
dev->control.callback(dev, cb_data);
break;
default:
break;
}
}
void device_control(usbh_device_t *dev, usbh_packet_callback_t callback, const struct usb_setup_data *setup_data, void *data)
{
if (dev->control.state != USBH_CONTROL_STATE_NONE) {
LOG_PRINTF("ERROR: Use of control state machine while not idle\n");
return;
}
dev->control.state = USBH_CONTROL_STATE_SETUP;
dev->control.callback = callback;
dev->control.data.out = data;
dev->control.data_length = setup_data->wLength;
dev->control.setup_data = *setup_data;
device_xfer_control_write_setup(&dev->control.setup_data, sizeof(dev->control.setup_data), control_state_machine, dev);
}
bool usbh_enum_available(void)
{
return !enumeration();
}
/**
* Returns 0 on error
* device otherwise
*/
usbh_device_t *usbh_get_free_device(const usbh_device_t *dev)
{
const usbh_low_level_driver_t *lld = dev->lld;
usbh_generic_data_t *lld_data = lld->driver_data;
usbh_device_t *usbh_device = lld_data->usbh_device;
uint8_t i;
LOG_PRINTF("DEV ADDRESS%d\n", dev->address);
for (i = 0; i < USBH_MAX_DEVICES; i++) {
if (usbh_device[i].address < 0) {
LOG_PRINTF("\t\t\t\t\tFOUND: %d", i);
usbh_device[i].address = i+1;
return &usbh_device[i];
} else {
LOG_PRINTF("address: %d\n\n\n", usbh_device[i].address);
}
}
return 0;
}
static void device_enumeration_finish(usbh_device_t *dev)
{
reset_enumeration();
dev->state = USBH_ENUM_STATE_FIRST;
}
static void device_enumeration_terminate(usbh_device_t *dev)
{
dev->address = -1;
device_enumeration_finish(dev);
}
#define CONTINUE_WITH(en) \
dev->state = en;\
device_enumerate(dev, cb_data);
static void device_enumerate(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
const usbh_low_level_driver_t *lld = dev->lld;
usbh_generic_data_t *lld_data = lld->driver_data;
uint8_t *usbh_buffer = lld_data->usbh_buffer;
// LOG_PRINTF("\nSTATE: %d\n", state);
switch (dev->state) {
case USBH_ENUM_STATE_SET_ADDRESS:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
if (dev->address == 0) {
dev->address = usbh_data.address_temporary;
LOG_PRINTF("Assigned address: %d\n", dev->address);
}
CONTINUE_WITH(USBH_ENUM_STATE_DEVICE_DT_READ_SETUP);
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
break;
case USBH_ENUM_STATE_DEVICE_DT_READ_SETUP:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_DEVICE << 8;
setup_data.wIndex = 0;
setup_data.wLength = USB_DT_DEVICE_SIZE;
dev->state = USBH_ENUM_STATE_DEVICE_DT_READ_COMPLETE;
device_control(dev, device_enumerate, &setup_data, &usbh_buffer[0]);
}
break;
case USBH_ENUM_STATE_DEVICE_DT_READ_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_device_descriptor *ddt =
(struct usb_device_descriptor *)&usbh_buffer[0];
dev->packet_size_max0 = ddt->bMaxPacketSize0;
LOG_PRINTF("Found device with vid=0x%04x pid=0x%04x\n", ddt->idVendor, ddt->idProduct);
LOG_PRINTF("class=0x%02x subclass=0x%02x protocol=0x%02x\n", ddt->bDeviceClass, ddt->bDeviceSubClass, ddt->bDeviceProtocol);
CONTINUE_WITH(USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_SETUP)
}
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length >= 8) {
struct usb_device_descriptor *ddt =
(struct usb_device_descriptor *)&usbh_buffer[0];
dev->packet_size_max0 = ddt->bMaxPacketSize0;
CONTINUE_WITH(USBH_ENUM_STATE_DEVICE_DT_READ_SETUP);
} else {
device_enumeration_terminate(dev);
ERROR(cb_data.status);
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_SETUP:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_CONFIGURATION << 8;
setup_data.wIndex = 0;
setup_data.wLength = dev->packet_size_max0;
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_COMPLETE;
device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE],
dev->packet_size_max0, device_enumerate, dev);
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
CONTINUE_WITH(USBH_ENUM_STATE_CONFIGURATION_DT_READ_SETUP);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length >= USB_DT_CONFIGURATION_SIZE) {
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
if (cb_data.transferred_length == cdt->wTotalLength) {
LOG_PRINTF("Configuration descriptor read complete. length: %d\n", cdt->wTotalLength);
CONTINUE_WITH(USBH_ENUM_STATE_SET_CONFIGURATION_SETUP);
}
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_READ_SETUP:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
struct usb_setup_data setup_data;
LOG_PRINTF("Getting complete configuration descriptor of length: %d bytes\n", cdt->wTotalLength);
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_CONFIGURATION << 8;
setup_data.wIndex = 0;
setup_data.wLength = cdt->wTotalLength;
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_READ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_READ_COMPLETE;
device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE],
cdt->wTotalLength, device_enumerate, dev);
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_READ_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
LOG_PRINTF("Configuration descriptor read complete. length: %d\n", cdt->wTotalLength);
CONTINUE_WITH(USBH_ENUM_STATE_SET_CONFIGURATION_SETUP);
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_SET_CONFIGURATION_SETUP:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
setup_data.wValue = cdt->bConfigurationValue;
setup_data.wIndex = 0;
setup_data.wLength = 0;
dev->state = USBH_ENUM_STATE_SET_CONFIGURATION_COMPLETE;
device_control(dev, device_enumerate, &setup_data, 0);
}
break;
case USBH_ENUM_STATE_SET_CONFIGURATION_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
CONTINUE_WITH(USBH_ENUM_STATE_FIND_DRIVER);
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_FIND_DRIVER:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
device_register(usbh_buffer, cdt->wTotalLength + USB_DT_DEVICE_SIZE, dev);
device_enumeration_finish(dev);
}
break;
default:
LOG_PRINTF("Error: Unknown state "__FILE__"/%d\n", __LINE__);
break;
}
}
void device_enumeration_start(usbh_device_t *dev)
{
set_enumeration();
// save address
uint8_t address = dev->address;
dev->address = 0;
if (dev->speed == USBH_SPEED_LOW) {
dev->packet_size_max0 = 8;
} else {
dev->packet_size_max0 = 64;
}
usbh_data.address_temporary = address;
LOG_PRINTF("\n\n\n ENUMERATION OF DEVICE@%d STARTED \n\n", address);
dev->state = USBH_ENUM_STATE_SET_ADDRESS;
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_SET_ADDRESS;
setup_data.wValue = address;
setup_data.wIndex = 0;
setup_data.wLength = 0;
device_control(dev, device_enumerate, &setup_data, 0);
}
/**
* Should be called with at least 1kHz frequency
*
*/
void usbh_poll(uint32_t time_curr_us)
{
uint32_t k = 0;
while (usbh_data.lld_drivers[k]) {
usbh_device_t *usbh_device =
((usbh_generic_data_t *)(usbh_data.lld_drivers[k]->driver_data))->usbh_device;
usbh_generic_data_t *lld_data = usbh_data.lld_drivers[k]->driver_data;
enum USBH_POLL_STATUS poll_status =
usbh_data.lld_drivers[k]->poll(lld_data, time_curr_us);
switch (poll_status) {
case USBH_POLL_STATUS_DEVICE_CONNECTED:
// New device found
LOG_PRINTF("\nDEVICE FOUND\n");
usbh_device[0].lld = usbh_data.lld_drivers[k];
usbh_device[0].speed = usbh_data.lld_drivers[k]->root_speed(lld_data);
usbh_device[0].address = 1;
usbh_device[0].control.state = USBH_CONTROL_STATE_NONE;
device_enumeration_start(&usbh_device[0]);
break;
case USBH_POLL_STATUS_DEVICE_DISCONNECTED:
{
usbh_device[0].control.state = USBH_CONTROL_STATE_NONE;
uint32_t i;
for (i = 0; i < USBH_MAX_DEVICES; i++) {
device_remove(&usbh_device[i]);
}
}
break;
default:
break;
}
if (lld_data->usbh_device[0].drv && usbh_device[0].drvdata) {
usbh_device[0].drv->poll(usbh_device[0].drvdata, time_curr_us);
}
k++;
}
}
void usbh_read(usbh_device_t *dev, usbh_packet_t *packet)
{
const usbh_low_level_driver_t *lld = dev->lld;
lld->read(lld->driver_data, packet);
}
void usbh_write(usbh_device_t *dev, const usbh_packet_t *packet)
{
const usbh_low_level_driver_t *lld = dev->lld;
lld->write(lld->driver_data, packet);
}

View file

@ -0,0 +1,364 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "driver/usbh_device_driver.h"
#include "usbh_driver_ac_midi_private.h"
#include "usart_helpers.h"
#include <stddef.h>
#include <libopencm3/usb/midi.h>
#include <libopencm3/usb/audio.h>
#include <libopencm3/usb/usbstd.h>
static midi_device_t midi_device[USBH_AC_MIDI_MAX_DEVICES];
static const midi_config_t *midi_config = NULL;
static bool initialized = false;
void midi_driver_init(const midi_config_t *config)
{
uint32_t i;
midi_config = config;
for (i = 0; i < USBH_AC_MIDI_MAX_DEVICES; i++) {
midi_device[i].state = 0;
}
initialized = true;
}
/**
*
*
*/
static void *init(usbh_device_t *usbh_dev)
{
if (!midi_config || !initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
midi_device_t *drvdata = NULL;
// find free data space for midi device
for (i = 0; i < USBH_AC_MIDI_MAX_DEVICES; i++) {
if (midi_device[i].state == 0) {
drvdata = &midi_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_out_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->endpoint_out_toggle = 0;
drvdata->usbh_device = usbh_dev;
drvdata->write_callback_user = NULL;
drvdata->sending = false;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
midi_device_t *midi = drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
struct usb_config_descriptor *cfg =
(struct usb_config_descriptor*)descriptor;
midi->buffer[0] = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
break;
case USB_DT_INTERFACE:
break;
case USB_DT_ENDPOINT:
{
struct usb_endpoint_descriptor *ep =
(struct usb_endpoint_descriptor*)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_BULK) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
midi->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_AC_MIDI_BUFFER) {
midi->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
midi->endpoint_in_maxpacketsize = USBH_AC_MIDI_BUFFER;
}
} else {
midi->endpoint_out_address = epaddr;
midi->endpoint_out_maxpacketsize = ep->wMaxPacketSize;
}
if (midi->endpoint_in_address && midi->endpoint_out_address) {
midi->state = 1;
return true;
}
}
}
break;
case USB_AUDIO_DT_CS_ENDPOINT:
{
struct usb_midi_in_jack_descriptor *midi_in_jack_desc =
(struct usb_midi_in_jack_descriptor *) descriptor;
(void)midi_in_jack_desc;
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void midi_in_message(midi_device_t *midi, const uint8_t datalen)
{
uint8_t i = 0;
if (midi_config->read_callback) {
for (i = 0; i < datalen; i += 4) {
// uint8_t cable_number = (midi->buffer[i] & 0xf0) >> 4;
uint8_t code_id = midi->buffer[i]&0xf;
uint8_t *ptrdata = &midi->buffer[i];
if (code_id < 2) {
continue;
}
midi_config->read_callback(midi->device_id, ptrdata);
}
}
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t status)
{
midi_device_t *midi = (midi_device_t *)dev->drvdata;
switch (midi->state) {
case 26:
{
switch (status.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
midi_in_message(midi, midi->endpoint_in_maxpacketsize);
midi->state = 25;
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
midi_in_message(midi, status.transferred_length);
midi->state = 25;
break;
default:
LOG_PRINTF("FATAL ERROR, MIDI DRIVER DEAD \n");
//~ dev->drv->remove();
midi->state = 0;
break;
}
}
break;
case 102:
{
midi->state = 101;
LOG_PRINTF("\n CAN'T TOUCH THIS... ignoring data\n");
}
break;
default:
break;
}
}
static void read_midi_in(void *drvdata, const uint8_t nextstate)
{
midi_device_t *midi = drvdata;
usbh_packet_t packet;
packet.address = midi->usbh_device->address;
packet.data.in = &midi->buffer[0];
packet.datalen = midi->endpoint_in_maxpacketsize;
packet.endpoint_address = midi->endpoint_in_address;
packet.endpoint_size_max = midi->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_BULK;
packet.speed = midi->usbh_device->speed;
packet.callback = event;
packet.callback_arg = midi->usbh_device;
packet.toggle = &midi->endpoint_in_toggle;
midi->state = nextstate;
usbh_read(midi->usbh_device,&packet);
}
/**
*
* @param t_us global time us
*/
static void poll(void *drvdata, uint32_t t_us)
{
(void)drvdata;
midi_device_t *midi = drvdata;
switch (midi->state) {
/// Upon configuration, some controllers send additional error data
/// case 100, 101, 102 cares for ignoring those data
case 100:
{
midi->time_us_config = t_us;
midi->state = 101;
}
break;
case 101:
{
read_midi_in(drvdata, 102);
}
break;
case 102:
{
// if elapsed MIDI initial delay microseconds
if (t_us - midi->time_us_config > MIDI_INITIAL_DELAY) {
midi->state = 26;
}
}
break;
case 25:
{
read_midi_in(drvdata, 26);
}
break;
case 1:
{
midi->state = 100;
midi->endpoint_in_toggle = 0;
LOG_PRINTF("\nMIDI CONFIGURED\n");
// Notify user
if (midi_config->notify_connected) {
midi_config->notify_connected(midi->device_id);
}
}
break;
}
}
// don't call directly
static void write_callback(usbh_device_t *dev, usbh_packet_callback_data_t status)
{
(void)status;
midi_device_t *midi = (midi_device_t *)dev->drvdata;
if (midi->sending) {
midi->sending = false;
const midi_write_callback_t callback = midi->write_callback_user;
if (!callback) {
return;
}
if (status.status & USBH_PACKET_CALLBACK_STATUS_OK) {
callback(midi->write_packet.datalen);
} else {
if (status.status & USBH_PACKET_CALLBACK_STATUS_ERRSIZ) {
const uint32_t length = status.transferred_length;
callback(length);
} else {
callback(0);
}
}
}
}
void usbh_midi_write(uint8_t device_id, const void *data, uint32_t length, midi_write_callback_t callback)
{
// bad device_id handling
if (device_id >= USBH_AC_MIDI_MAX_DEVICES) {
return;
}
midi_device_t *midi = &midi_device[device_id];
// device with provided device_id is not alive
if (midi->state == 0) {
return;
}
usbh_device_t *dev = midi->usbh_device;
if (midi->endpoint_out_address == 0) {
return;
}
midi->sending = true;
midi->write_callback_user = callback;
midi->write_packet.data.out = data;
midi->write_packet.datalen = length;
midi->write_packet.address = dev->address;
midi->write_packet.endpoint_address = midi->endpoint_out_address;
midi->write_packet.endpoint_size_max = midi->endpoint_out_maxpacketsize;
midi->write_packet.endpoint_type = USBH_ENDPOINT_TYPE_BULK;
midi->write_packet.speed = dev->speed;
midi->write_packet.callback = write_callback;
midi->write_packet.callback_arg = midi->usbh_device;
midi->write_packet.toggle = &midi->endpoint_out_toggle;
usbh_write(dev, &midi->write_packet);
}
static void remove(void *drvdata)
{
midi_device_t *midi = drvdata;
if (midi_config->notify_disconnected) {
midi_config->notify_disconnected(midi->device_id);
}
midi->state = 0;
midi->endpoint_in_address = 0;
midi->endpoint_out_address = 0;
}
static const usbh_dev_driver_info_t usbh_midi_driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x01,
.ifaceSubClass = 0x03,
.ifaceProtocol = -1,
};
const usbh_dev_driver_t usbh_midi_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &usbh_midi_driver_info
};

View file

@ -0,0 +1,52 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_AC_MIDI_PRIVATE_
#define USBH_DRIVER_AC_MIDI_PRIVATE_
#include "driver/usbh_device_driver.h"
#include "usbh_driver_ac_midi.h"
#include <stdint.h>
#define MIDI_INITIAL_DELAY (100000)
struct _midi_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_AC_MIDI_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint16_t endpoint_out_maxpacketsize;
uint8_t endpoint_in_address;
uint8_t endpoint_out_address;
uint8_t state;
uint8_t endpoint_in_toggle;
uint8_t endpoint_out_toggle;
uint8_t device_id;
bool sending;
midi_write_callback_t write_callback_user;
usbh_packet_t write_packet;
// Timestamp at sending config command
uint32_t time_us_config;
};
typedef struct _midi_device midi_device_t;
#endif

View file

@ -0,0 +1,368 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usart_helpers.h"
#include "usbh_driver_gp_xbox.h"
#include "driver/usbh_device_driver.h"
#include <stdint.h>
#include <libopencm3/usb/usbstd.h>
enum STATES {
STATE_INACTIVE,
STATE_INITIAL,
STATE_READING_REQUEST,
STATE_READING_COMPLETE,
};
#define GP_XBOX_CORRECT_TRANSFERRED_LENGTH 20
struct _gp_xbox_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_GP_XBOX_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
enum STATES state_next;
uint8_t endpoint_in_toggle;
uint8_t device_id;
uint8_t configuration_value;
};
typedef struct _gp_xbox_device gp_xbox_device_t;
static gp_xbox_device_t gp_xbox_device[USBH_GP_XBOX_MAX_DEVICES];
static const gp_xbox_config_t *gp_xbox_config;
static bool initialized = false;
static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox);
void gp_xbox_driver_init(const gp_xbox_config_t *config)
{
if (!config) {
return;
}
initialized = true;
uint32_t i;
gp_xbox_config = config;
for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
gp_xbox_device[i].state_next = STATE_INACTIVE;
}
}
/**
*
*
*/
static void *init(usbh_device_t *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
gp_xbox_device_t *drvdata = 0;
// find free data space for gp_xbox device
for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
if (gp_xbox_device[i].state_next == STATE_INACTIVE) {
drvdata = &gp_xbox_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->usbh_device = usbh_dev;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
struct usb_config_descriptor *cfg = (struct usb_config_descriptor*)descriptor;
gp_xbox->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
break;
case USB_DT_INTERFACE:
break;
case USB_DT_ENDPOINT:
{
struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor*)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
gp_xbox->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_GP_XBOX_BUFFER) {
gp_xbox->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
gp_xbox->endpoint_in_maxpacketsize = USBH_GP_XBOX_BUFFER;
}
}
if (gp_xbox->endpoint_in_address) {
gp_xbox->state_next = STATE_INITIAL;
return true;
}
}
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void parse_data(usbh_device_t *dev)
{
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)dev->drvdata;
uint8_t *packet = gp_xbox->buffer;
gp_xbox_packet_t gp_xbox_packet;
gp_xbox_packet.buttons = 0;
// DPAD
const uint8_t data1 = packet[2];
const uint8_t data2 = packet[3];
if (data1 & (1 << 0)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_TOP;
}
if (data1 & (1 << 1)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_BOTTOM;
}
if (data1 & (1 << 2)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_LEFT;
}
if (data1 & (1 << 3)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_RIGHT;
}
// Start + select
if (data1 & (1 << 4)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_START;
}
if (data1 & (1 << 5)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_SELECT;
}
// axis buttons
if (data1 & (1 << 6)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_LEFT;
}
if (data1 & (1 << 7)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_RIGHT;
}
// buttons ABXY
if (data2 & (1 << 4)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_A;
}
if (data2 & (1 << 5)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_B;
}
if (data2 & (1 << 6)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_X;
}
if (data2 & (1 << 7)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_Y;
}
// buttons rear
if (data2 & (1 << 0)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_LT;
}
if (data2 & (1 << 1)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_RT;
}
if (data2 & (1 << 2)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_XBOX;
}
// rear levers
gp_xbox_packet.axis_rear_left = packet[4];
gp_xbox_packet.axis_rear_right = packet[5];
gp_xbox_packet.axis_left_x = packet[7]*256 + packet[6];
gp_xbox_packet.axis_left_y = packet[9]*256 + packet[8];
gp_xbox_packet.axis_right_x = packet[11]*256 + packet[10];
gp_xbox_packet.axis_right_y = packet[13]*256 + packet[12];
// call update callback
if (gp_xbox_config->update) {
gp_xbox_config->update(gp_xbox->device_id, gp_xbox_packet);
}
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)dev->drvdata;
switch (gp_xbox->state_next) {
case STATE_READING_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
parse_data(dev);
gp_xbox->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length == GP_XBOX_CORRECT_TRANSFERRED_LENGTH) {
parse_data(dev);
}
gp_xbox->state_next = STATE_READING_REQUEST;
break;
default:
ERROR(cb_data.status);
gp_xbox->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_INACTIVE:
{
LOG_PRINTF("XBOX inactive");
}
break;
default:
{
LOG_PRINTF("Unknown state\n");
}
break;
}
}
static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox)
{
usbh_packet_t packet;
packet.address = gp_xbox->usbh_device->address;
packet.data.in = &gp_xbox->buffer[0];
packet.datalen = gp_xbox->endpoint_in_maxpacketsize;
packet.endpoint_address = gp_xbox->endpoint_in_address;
packet.endpoint_size_max = gp_xbox->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = gp_xbox->usbh_device->speed;
packet.callback = event;
packet.callback_arg = gp_xbox->usbh_device;
packet.toggle = &gp_xbox->endpoint_in_toggle;
gp_xbox->state_next = STATE_READING_COMPLETE;
usbh_read(gp_xbox->usbh_device, &packet);
// LOG_PRINTF("@gp_xbox EP1 | \n");
}
/**
* \param time_curr_us - monotically rising time (see usbh_hubbed.h)
* unit is microseconds
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
(void)time_curr_us;
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
switch (gp_xbox->state_next) {
case STATE_READING_REQUEST:
{
read_gp_xbox_in(gp_xbox);
}
break;
case STATE_INITIAL:
{
gp_xbox->state_next = STATE_READING_REQUEST;
gp_xbox->endpoint_in_toggle = 0;
LOG_PRINTF("\ngp_xbox CONFIGURED\n");
if (gp_xbox_config->notify_connected) {
gp_xbox_config->notify_connected(gp_xbox->device_id);
}
}
break;
default:
{
// do nothing - probably transfer is in progress
}
break;
}
}
static void remove(void *drvdata)
{
LOG_PRINTF("Removing xbox\n");
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
if (gp_xbox_config->notify_disconnected) {
gp_xbox_config->notify_disconnected(gp_xbox->device_id);
}
gp_xbox->state_next = STATE_INACTIVE;
gp_xbox->endpoint_in_address = 0;
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = 0xff,
.deviceSubClass = 0xff,
.deviceProtocol = 0xff,
.idVendor = 0x045e,
.idProduct = 0x028e,
.ifaceClass = 0xff,
.ifaceSubClass = 93,
.ifaceProtocol = 0x01
};
const usbh_dev_driver_t usbh_gp_xbox_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

412
fw/src/usbh_driver_hid.c Normal file
View file

@ -0,0 +1,412 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usbh_core.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hid.h"
#include "usart_helpers.h"
#include <libopencm3/usb/usbstd.h>
#include <libopencm3/usb/hid.h>
#include <stdint.h>
#include <stddef.h>
#define USB_HID_SET_REPORT 0x09
#define USB_HID_SET_IDLE 0x0A
enum STATES {
STATE_INACTIVE,
STATE_READING_REQUEST,
STATE_READING_COMPLETE_AND_CHECK_REPORT,
STATE_SET_REPORT_EMPTY_READ,
STATE_GET_REPORT_DESCRIPTOR_READ_SETUP,// configuration is complete at this point. We write request
STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE,// after the read finishes, we parse that descriptor
STATE_SET_IDLE,
STATE_SET_IDLE_COMPLETE,
};
enum REPORT_STATE {
REPORT_STATE_NULL,
REPORT_STATE_READY,
REPORT_STATE_PENDING,
};
struct _hid_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_HID_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
enum STATES state_next;
uint8_t endpoint_in_toggle;
uint8_t device_id;
uint8_t configuration_value;
uint16_t report0_length;
enum REPORT_STATE report_state;
uint8_t report_data[USBH_HID_REPORT_BUFFER];
uint8_t report_data_length;
enum HID_TYPE hid_type;
uint8_t interface_number;
};
typedef struct _hid_device hid_device_t;
struct hid_report_decriptor {
struct usb_hid_descriptor header;
struct _report_descriptor_info {
uint8_t bDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) report_descriptors_info[];
} __attribute__((packed));
static hid_device_t hid_device[USBH_HID_MAX_DEVICES];
static hid_config_t hid_config;
static bool initialized = false;
void hid_driver_init(const hid_config_t *config)
{
uint32_t i;
initialized = true;
hid_config = *config;
for (i = 0; i < USBH_HID_MAX_DEVICES; i++) {
hid_device[i].state_next = STATE_INACTIVE;
}
}
static void *init(usbh_device_t *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
hid_device_t *drvdata = NULL;
// find free data space for HID device
for (i = 0; i < USBH_HID_MAX_DEVICES; i++) {
if (hid_device[i].state_next == STATE_INACTIVE) {
drvdata = &hid_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->report0_length = 0;
drvdata->usbh_device = usbh_dev;
drvdata->report_state = REPORT_STATE_NULL;
drvdata->hid_type = HID_TYPE_NONE;
break;
}
}
return drvdata;
}
static void parse_report_descriptor(hid_device_t *hid, const uint8_t *buffer, uint32_t length)
{
// TODO
// Do some parsing!
// add some checks
hid->report_state = REPORT_STATE_READY;
// TODO: parse this from buffer!
hid->report_data_length = 1;
(void)buffer;
(void)length;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
hid_device_t *hid = (hid_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
const struct usb_config_descriptor * cfg = (const struct usb_config_descriptor*)descriptor;
hid->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
{
const struct usb_device_descriptor *devDesc = (const struct usb_device_descriptor *)descriptor;
(void)devDesc;
}
break;
case USB_DT_INTERFACE:
{
const struct usb_interface_descriptor *ifDesc = (const struct usb_interface_descriptor *)descriptor;
if (ifDesc->bInterfaceProtocol == 0x01) {
hid->hid_type = HID_TYPE_KEYBOARD;
hid->interface_number = ifDesc->bInterfaceNumber;
} else if (ifDesc->bInterfaceProtocol == 0x02) {
hid->hid_type = HID_TYPE_MOUSE;
hid->interface_number = ifDesc->bInterfaceNumber;
}
}
break;
case USB_DT_ENDPOINT:
{
const struct usb_endpoint_descriptor *ep = (const struct usb_endpoint_descriptor *)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
hid->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_HID_BUFFER) {
hid->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
hid->endpoint_in_maxpacketsize = USBH_HID_BUFFER;
}
}
}
}
break;
case USB_DT_HID:
{
const struct hid_report_decriptor *desc = (const struct hid_report_decriptor *)descriptor;
if (desc->header.bNumDescriptors > 0 && desc->report_descriptors_info[0].bDescriptorType == USB_DT_REPORT) {
hid->report0_length = desc->report_descriptors_info[0].wDescriptorLength;
}
}
break;
default:
break;
}
if (hid->endpoint_in_address && hid->report0_length) {
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_SETUP;
return true;
}
return false;
}
static void report_event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
(void)cb_data;// UNUSED
hid_device_t *hid = (hid_device_t *)dev->drvdata;
hid->report_state = REPORT_STATE_READY;
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hid_device_t *hid = (hid_device_t *)dev->drvdata;
switch (hid->state_next) {
case STATE_READING_COMPLETE_AND_CHECK_REPORT:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (hid_config.hid_in_message_handler) {
hid_config.hid_in_message_handler(hid->device_id, hid->buffer, cb_data.transferred_length);
}
hid->state_next = STATE_READING_REQUEST;
break;
default:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE: // read complete, SET_IDLE to 0
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
LOG_PRINTF("READ REPORT COMPLETE \n");
hid->state_next = STATE_READING_REQUEST;
hid->endpoint_in_toggle = 0;
parse_report_descriptor(hid, hid->buffer, cb_data.transferred_length);
break;
default:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
default:
break;
}
}
static void read_hid_in_endpoint(void *drvdata)
{
hid_device_t *hid = (hid_device_t *)drvdata;
usbh_packet_t packet;
packet.address = hid->usbh_device->address;
packet.data.in = &hid->buffer[0];
packet.datalen = hid->endpoint_in_maxpacketsize;
packet.endpoint_address = hid->endpoint_in_address;
packet.endpoint_size_max = hid->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = hid->usbh_device->speed;
packet.callback = event;
packet.callback_arg = hid->usbh_device;
packet.toggle = &hid->endpoint_in_toggle;
hid->state_next = STATE_READING_COMPLETE_AND_CHECK_REPORT;
usbh_read(hid->usbh_device, &packet);
}
/**
* @param time_curr_us - monotically rising time
* unit is microseconds
* @see usbh_poll()
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
(void)time_curr_us;
hid_device_t *hid = (hid_device_t *)drvdata;
usbh_device_t *dev = hid->usbh_device;
switch (hid->state_next) {
case STATE_READING_REQUEST:
{
read_hid_in_endpoint(drvdata);
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ_SETUP:
{
hid->endpoint_in_toggle = 0;
// We support only the first report descriptor with index 0
// limit the size of the report descriptor!
if (hid->report0_length > USBH_HID_BUFFER) {
hid->report0_length = USBH_HID_BUFFER;
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_INTERFACE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_REPORT << 8;
setup_data.wIndex = 0;
setup_data.wLength = hid->report0_length;
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE;
device_control(dev, event, &setup_data, hid->buffer);
}
break;
default:
// do nothing - probably transfer is in progress
break;
}
}
static void remove(void *drvdata)
{
hid_device_t *hid = (hid_device_t *)drvdata;
hid->state_next = STATE_INACTIVE;
hid->endpoint_in_address = 0;
}
bool hid_set_report(uint8_t device_id, uint8_t val)
{
if (device_id >= USBH_HID_MAX_DEVICES) {
LOG_PRINTF("invalid device id\n");
return false;
}
hid_device_t *hid = &hid_device[device_id];
if (hid->report_state != REPORT_STATE_READY) {
LOG_PRINTF("reporting is not ready\n");
// store and update afterwards
return false;
}
if (hid->report_data_length == 0) {
LOG_PRINTF("reporting is not available (report len=0)\n");
return false;
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE;
setup_data.bRequest = USB_HID_SET_REPORT;
setup_data.wValue = 0x02 << 8;
setup_data.wIndex = hid->interface_number;
setup_data.wLength = hid->report_data_length;
hid->report_data[0] = val;
hid->report_state = REPORT_STATE_PENDING;
device_control(hid->usbh_device, report_event, &setup_data, &hid->report_data);
return true;
}
bool hid_is_connected(uint8_t device_id)
{
if (device_id >= USBH_HID_MAX_DEVICES) {
LOG_PRINTF("is connected: invalid device id\n");
return false;
}
return hid_device[device_id].state_next == STATE_INACTIVE;
}
enum HID_TYPE hid_get_type(uint8_t device_id)
{
if (hid_is_connected(device_id)) {
return HID_TYPE_NONE;
}
return hid_device[device_id].hid_type;
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x03, // HID class
.ifaceSubClass = -1,
.ifaceProtocol = -1, // Do not care
};
const usbh_dev_driver_t usbh_hid_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

675
fw/src/usbh_driver_hub.c Normal file
View file

@ -0,0 +1,675 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usbh_driver_hub_private.h"
#include "driver/usbh_device_driver.h"
#include "usart_helpers.h"
#include "usbh_config.h"
#include <stddef.h>
#include <stdint.h>
static hub_device_t hub_device[USBH_MAX_HUBS];
static bool initialized = false;
void hub_driver_init(void)
{
uint32_t i;
initialized = true;
for (i = 0; i < USBH_MAX_HUBS; i++) {
hub_device[i].device[0] = 0;
hub_device[i].ports_num = 0;
hub_device[i].current_port = CURRENT_PORT_NONE;
}
}
static void *init(usbh_device_t *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
hub_device_t *drvdata = NULL;
// find free data space for hub device
for (i = 0; i < USBH_MAX_HUBS; i++) {
if (hub_device[i].device[0] == 0) {
break;
}
}
LOG_PRINTF("{%d}",i);
if (i == USBH_MAX_HUBS) {
LOG_PRINTF("Unable to initialize hub driver");
return 0;
}
drvdata = &hub_device[i];
drvdata->state = EVENT_STATE_NONE;
drvdata->ports_num = 0;
drvdata->device[0] = usbh_dev;
drvdata->busy = 0;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_maxpacketsize = 0;
return drvdata;
}
/**
* @returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
hub_device_t *hub = (hub_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_ENDPOINT:
{
struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor *)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
hub->endpoint_in_address = epaddr&0x7f;
hub->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
}
}
LOG_PRINTF("ENDPOINT DESCRIPTOR FOUND\n");
}
break;
case USB_DT_HUB:
{
struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)descriptor;
if ( desc->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) {
hub->ports_num = desc->head.bNbrPorts;
} else {
LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\n");
hub->ports_num = USBH_HUB_MAX_DEVICES;
}
LOG_PRINTF("HUB DESCRIPTOR FOUND \n");
}
break;
default:
LOG_PRINTF("TYPE: %02X \n",desc_type);
break;
}
if (hub->endpoint_in_address) {
hub->state = EVENT_STATE_INITIAL;
LOG_PRINTF("end enum");
return true;
}
return false;
}
// Enumerate
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hub_device_t *hub = (hub_device_t *)dev->drvdata;
LOG_PRINTF("\nHUB->STATE = %d\n", hub->state);
switch (hub->state) {
case EVENT_STATE_POLL:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
uint8_t i;
uint8_t *buf = hub->buffer;
uint32_t psc = 0; // Limit: up to 4 bytes...
for (i = 0; i < cb_data.transferred_length; i++) {
psc += buf[i] << (i*8);
}
int8_t port = 0;
LOG_PRINTF("psc:%d\n",psc);
// Driver error... port not found
if (!psc) {
// Continue reading status change endpoint
hub->state = EVENT_STATE_POLL_REQ;
break;
}
for (i = 0; i <= hub->ports_num; i++) {
if (psc & (1<<i)) {
port = i;
psc = 0;
break;
}
}
if (hub->current_port >= 1) {
if (hub->current_port != port) {
LOG_PRINTF("X");
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
struct usb_setup_data setup_data;
// If regular port event, else hub event
if (port) {
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
} else {
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
}
setup_data.bRequest = USB_REQ_GET_STATUS;
setup_data.wValue = 0;
setup_data.wIndex = port;
setup_data.wLength = 4;
hub->state = EVENT_STATE_GET_STATUS_COMPLETE;
hub->current_port = port;
LOG_PRINTF("\n\nPORT FOUND: %d\n", port);
device_control(dev, event, &setup_data, &hub->hub_and_port_status[port]);
}
break;
default:
ERROR(cb_data.status);
hub->state = EVENT_STATE_NONE;
break;
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
// In case of EAGAIN error, retry read on status endpoint
hub->state = EVENT_STATE_POLL_REQ;
LOG_PRINTF("HUB: Retrying...\n");
break;
}
break;
case EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE:// Hub descriptor found
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_hub_descriptor *hub_descriptor =
(struct usb_hub_descriptor *)hub->buffer;
// Check size
if (hub_descriptor->head.bDescLength > hub->desc_len) {
struct usb_setup_data setup_data;
hub->desc_len = hub_descriptor->head.bDescLength;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = 0x29<<8;
setup_data.wIndex = 0;
setup_data.wLength = hub->desc_len;
hub->state = EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE;
device_control(dev, event, &setup_data, hub->buffer);
break;
} else if (hub_descriptor->head.bDescLength == hub->desc_len) {
hub->ports_num = hub_descriptor->head.bNbrPorts;
hub->state = EVENT_STATE_ENABLE_PORTS;
hub->index = 0;
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
event(dev, cb_data);
} else {
//try again
}
}
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
{
LOG_PRINTF("->\t\t\t\t\t ERRSIZ: deschub\n");
struct usb_hub_descriptor*hub_descriptor =
(struct usb_hub_descriptor *)hub->buffer;
if (cb_data.transferred_length >= sizeof(struct usb_hub_descriptor_head)) {
if (cb_data.transferred_length == hub_descriptor->head.bDescLength) {
// Process HUB descriptor
if ( hub_descriptor->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) {
hub->ports_num = hub_descriptor->head.bNbrPorts;
} else {
LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\n");
hub->ports_num = USBH_HUB_MAX_DEVICES;
}
hub->state = EVENT_STATE_ENABLE_PORTS;
hub->index = 0;
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
event(dev, cb_data);
}
}
}
break;
default:
ERROR(cb_data.status);
break;
}
}
break;
case EVENT_STATE_ENABLE_PORTS:// enable ports
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
if (hub->index < hub->ports_num) {
hub->index++;
struct usb_setup_data setup_data;
LOG_PRINTF("[!%d!]",hub->index);
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_SET_FEATURE;
setup_data.wValue = HUB_FEATURE_PORT_POWER;
setup_data.wIndex = hub->index;
setup_data.wLength = 0;
device_control(dev, event, &setup_data, 0);
} else {
// TODO:
// Delay Based on hub descriptor field bPwr2PwrGood
// delay_ms_busy_loop(200);
LOG_PRINTF("\nHUB CONFIGURED & PORTS POWERED\n");
// get device status
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_STATUS;
setup_data.wValue = 0;
setup_data.wIndex = 0;
setup_data.wLength = 4;
hub->state = EVENT_STATE_GET_PORT_STATUS;
hub->index = 0;
device_control(dev, event, &setup_data, hub->buffer);
}
break;
default:
ERROR(cb_data.status);
break;
}
}
break;
case EVENT_STATE_GET_PORT_STATUS:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
if (hub->index < hub->ports_num) {
//TODO: process data contained in hub->buffer
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = USB_REQ_GET_STATUS;
setup_data.wValue = 0;
setup_data.wIndex = ++hub->index;
setup_data.wLength = 4;
hub->state = EVENT_STATE_GET_PORT_STATUS;
device_control(dev, event, &setup_data, hub->buffer);
} else {
hub->busy = 0;
hub->state = EVENT_STATE_POLL_REQ;
}
}
break;
default:
ERROR(cb_data.status);
break;
}
}
break;
case EVENT_STATE_GET_STATUS_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
int8_t port = hub->current_port;
LOG_PRINTF("|%d",port);
// Get Port status, else Get Hub status
if (port) {
uint16_t stc = hub->hub_and_port_status[port].stc;
// Connection status changed
if (stc & (1<<HUB_FEATURE_PORT_CONNECTION)) {
// Check, whether device is in connected state
if (!hub->device[port]) {
if (!usbh_enum_available() || hub->busy) {
LOG_PRINTF("\n\t\t\tCannot enumerate %d %d\n", !usbh_enum_available(), hub->busy);
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
// clear feature C_PORT_CONNECTION
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
setup_data.wValue = HUB_FEATURE_C_PORT_CONNECTION;
setup_data.wIndex = port;
setup_data.wLength = 0;
hub->state = EVENT_STATE_PORT_RESET_REQ;
device_control(dev, event, &setup_data, 0);
} else if(stc & (1<<HUB_FEATURE_PORT_RESET)) {
// clear feature C_PORT_RESET
// Reset processing is complete, enumerate device
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
setup_data.wValue = HUB_FEATURE_C_PORT_RESET;
setup_data.wIndex = port;
setup_data.wLength = 0;
hub->state = EVENT_STATE_PORT_RESET_COMPLETE;
LOG_PRINTF("RESET");
device_control(dev, event, &setup_data, 0);
} else {
LOG_PRINTF("another STC %d\n", stc);
}
} else {
hub->state = EVENT_STATE_POLL_REQ;
LOG_PRINTF("HUB status change\n");
}
}
break;
default:
ERROR(cb_data.status);
// continue
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
break;
case EVENT_STATE_PORT_RESET_REQ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
int8_t port = hub->current_port;
uint16_t stc = hub->hub_and_port_status[port].stc;
if (!hub->device[port]) {
if ((stc) & (1<<HUB_FEATURE_PORT_CONNECTION)) {
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_SET_FEATURE;
setup_data.wValue = HUB_FEATURE_PORT_RESET;
setup_data.wIndex = port;
setup_data.wLength = 0;
hub->state = EVENT_STATE_GET_PORT_STATUS;
LOG_PRINTF("CONN");
hub->busy = 1;
device_control(dev, event, &setup_data, 0);
}
} else {
LOG_PRINTF("\t\t\t\tDISCONNECT EVENT\n");
device_remove(hub->device[port]);
hub->device[port] = 0;
hub->current_port = CURRENT_PORT_NONE;
hub->state = EVENT_STATE_POLL_REQ;
hub->busy = 0;
}
}
break;
default:
ERROR(cb_data.status);
// continue
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
break;
case EVENT_STATE_PORT_RESET_COMPLETE: // RESET COMPLETE, start enumeration
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
LOG_PRINTF("\nPOLL\n");
int8_t port = hub->current_port;
uint16_t sts = hub->hub_and_port_status[port].sts;
if (sts & (1<<HUB_FEATURE_PORT_ENABLE)) {
hub->device[port] = usbh_get_free_device(dev);
if (!hub->device[port]) {
LOG_PRINTF("\nFATAL ERROR\n");
return;// DEAD END
}
if ((sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) &&
!(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) {
#define DISABLE_LOW_SPEED
#ifdef DISABLE_LOW_SPEED
LOG_PRINTF("Low speed device");
// Disable Low speed device immediately
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
setup_data.wValue = HUB_FEATURE_PORT_ENABLE;
setup_data.wIndex = port;
setup_data.wLength = 0;
// After write process another devices, poll for events
//Expecting all ports are powered (constant/non-changeable after init)
hub->state = EVENT_STATE_GET_PORT_STATUS;
hub->current_port = CURRENT_PORT_NONE;
device_control(dev, event, &setup_data, 0);
#else
hub->device[port]->speed = USBH_SPEED_LOW;
LOG_PRINTF("Low speed device");
hub->timestamp_us = hub->time_curr_us;
hub->state = EVENT_STATE_SLEEP_500_MS; // schedule wait for 500ms
#endif
} else if (!(sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) &&
!(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) {
hub->device[port]->speed = USBH_SPEED_FULL;
LOG_PRINTF("Full speed device");
hub->timestamp_us = hub->time_curr_us;
hub->state = EVENT_STATE_SLEEP_500_MS; // schedule wait for 500ms
}
} else {
LOG_PRINTF("%s:%d Do not know what to do, when device is disabled after reset\n", __FILE__, __LINE__);
hub->state = EVENT_STATE_POLL_REQ;
return;
}
}
break;
default:
ERROR(cb_data.status);
// continue
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
break;
default:
LOG_PRINTF("UNHANDLED EVENT %d\n",hub->state);
break;
}
}
static void read_ep1(void *drvdata)
{
hub_device_t *hub = (hub_device_t *)drvdata;
usbh_packet_t packet;
packet.address = hub->device[0]->address;
packet.data.in = hub->buffer;
packet.datalen = hub->endpoint_in_maxpacketsize;
packet.endpoint_address = hub->endpoint_in_address;
packet.endpoint_size_max = hub->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = hub->device[0]->speed;
packet.callback = event;
packet.callback_arg = hub->device[0];
packet.toggle = &hub->endpoint_in_toggle;
hub->state = EVENT_STATE_POLL;
usbh_read(hub->device[0], &packet);
LOG_PRINTF("@hub %d/EP1 | \n", hub->device[0]->address);
}
/**
* @param time_curr_us - monotically rising time
* unit is microseconds
* @see usbh_poll()
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
hub_device_t *hub = (hub_device_t *)drvdata;
usbh_device_t *dev = hub->device[0];
hub->time_curr_us = time_curr_us;
switch (hub->state) {
case EVENT_STATE_POLL_REQ:
{
if (usbh_enum_available()) {
read_ep1(hub);
} else {
LOG_PRINTF("enum not available\n");
}
}
break;
case EVENT_STATE_INITIAL:
{
if (hub->ports_num) {
hub->index = 0;
hub->state = EVENT_STATE_ENABLE_PORTS;
LOG_PRINTF("No need to get HUB DESC\n");
event(dev, (usbh_packet_callback_data_t){0, 0});
} else {
hub->endpoint_in_toggle = 0;
struct usb_setup_data setup_data;
hub->desc_len = hub->device[0]->packet_size_max0;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = 0x29 << 8;
setup_data.wIndex = 0;
setup_data.wLength = hub->desc_len;
hub->state = EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE;
device_control(dev, event, &setup_data, hub->buffer);
LOG_PRINTF("DO Need to get HUB DESC\n");
}
}
break;
case EVENT_STATE_SLEEP_500_MS:
if (hub->time_curr_us - hub->timestamp_us > 500000) {
int8_t port = hub->current_port;
LOG_PRINTF("PORT: %d\n", port);
LOG_PRINTF("NEW device at address: %d\n", hub->device[port]->address);
hub->device[port]->lld = hub->device[0]->lld;
device_enumeration_start(hub->device[port]);
hub->current_port = CURRENT_PORT_NONE;
// Maybe error, when assigning address is taking too long
//
// Detail:
// USB hub cannot enable another port while the device
// the current one is also in address state (has address==0)
// Only one device on bus can have address==0
hub->busy = 0;
hub->state = EVENT_STATE_POLL_REQ;
}
break;
default:
break;
}
if (usbh_enum_available()) {
uint32_t i;
for (i = 1; i < USBH_HUB_MAX_DEVICES + 1; i++) {
if (hub->device[i]) {
if (hub->device[i]->drv && hub->device[i]->drvdata) {
hub->device[i]->drv->poll(hub->device[i]->drvdata, time_curr_us);
}
}
}
}
}
static void remove(void *drvdata)
{
hub_device_t *hub = (hub_device_t *)drvdata;
uint8_t i;
hub->state = EVENT_STATE_NONE;
hub->endpoint_in_address = 0;
hub->busy = 0;
for (i = 0; i < USBH_HUB_MAX_DEVICES + 1; i++) {
hub->device[i] = 0;
}
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = 0x09,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x09,
.ifaceSubClass = -1,
.ifaceProtocol = -1
};
const usbh_dev_driver_t usbh_hub_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

View file

@ -0,0 +1,123 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_HUB_PRIVATE_
#define USBH_DRIVER_HUB_PRIVATE_
#include "usbh_config.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hub.h"
#include <stdint.h>
#include <stdbool.h>
#include <libopencm3/usb/usbstd.h>
// # HUB DEFINITIONS
#define HUB_FEATURE_PORT_CONNECTION 0
#define HUB_FEATURE_PORT_ENABLE 1
#define HUB_FEATURE_PORT_SUSPEND 2
#define HUB_FEATURE_PORT_OVERCURRENT 3
#define HUB_FEATURE_PORT_RESET 4
#define HUB_FEATURE_PORT_POWER 8
#define HUB_FEATURE_PORT_LOWSPEED 9
#define HUB_FEATURE_PORT_HIGHSPEED 10
#define HUB_FEATURE_C_PORT_CONNECTION 16
#define HUB_FEATURE_C_PORT_ENABLE 17
#define HUB_FEATURE_C_PORT_SUSPEND 18
#define HUB_FEATURE_C_PORT_OVERCURRENT 19
#define HUB_FEATURE_C_PORT_RESET 20
#define HUB_REQ_GET_STATUS 0
#define HUB_REQ_CLEAR_FEATURE 1
#define HUB_REQ_SET_FEATURE 3
#define HUB_REQ_GET_DESCRIPTOR 6
#define USB_DT_HUB (41)
#define USB_DT_HUB_SIZE (9)
// Hub buffer: must be larger than hub descriptor
#define USBH_HUB_BUFFER_SIZE (USB_DT_HUB_SIZE)
#define CURRENT_PORT_NONE -1
enum EVENT_STATE {
EVENT_STATE_NONE,
EVENT_STATE_INITIAL,
EVENT_STATE_POLL_REQ,
EVENT_STATE_POLL,
EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE,
EVENT_STATE_ENABLE_PORTS,
EVENT_STATE_GET_PORT_STATUS,
EVENT_STATE_PORT_RESET_REQ,
EVENT_STATE_PORT_RESET_COMPLETE,
EVENT_STATE_SLEEP_500_MS,
EVENT_STATE_GET_STATUS_COMPLETE,
};
struct _hub_device {
usbh_device_t *device[USBH_HUB_MAX_DEVICES + 1];
uint8_t buffer[USBH_HUB_BUFFER_SIZE];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
uint8_t endpoint_in_toggle;
enum EVENT_STATE state;
uint8_t desc_len;
uint16_t ports_num;
int8_t index;
int8_t current_port;
struct {
uint16_t sts;
uint16_t stc;
} hub_and_port_status[USBH_HUB_MAX_DEVICES + 1];
bool busy;
uint32_t time_curr_us;
uint32_t timestamp_us;
};
typedef struct _hub_device hub_device_t;
struct usb_hub_descriptor_head {
uint8_t bDescLength;
uint8_t bDescriptorType;
uint8_t bNbrPorts;
uint16_t wHubCharacteristics;
uint8_t bPwrOn2PwrGood;
uint8_t bHubContrCurrent;
} __attribute__((packed));
struct usb_hub_descriptor_body {
uint8_t bDeviceRemovable;
uint8_t PortPwrCtrlMask;
} __attribute__((packed));
// for hubs with up to 7 ports on hub
struct usb_hub_descriptor {
struct usb_hub_descriptor_head head;
struct usb_hub_descriptor_body body[1];
} __attribute__((packed));
#endif

1041
fw/src/usbh_lld_stm32f4.c Normal file

File diff suppressed because it is too large Load diff

521
fw/src/words.c Normal file
View file

@ -0,0 +1,521 @@
#include "words.h"
const char * const even[256] = {
"aardvark", /* 00 */
"absurd", /* 01 */
"accrue", /* 02 */
"acme", /* 03 */
"adrift", /* 04 */
"adult", /* 05 */
"afflict", /* 06 */
"ahead", /* 07 */
"aimless", /* 08 */
"Algol", /* 09 */
"allow", /* 0A */
"alone", /* 0B */
"ammo", /* 0C */
"ancient", /* 0D */
"apple", /* 0E */
"artist", /* 0F */
"assume", /* 10 */
"Athens", /* 11 */
"atlas", /* 12 */
"Aztec", /* 13 */
"baboon", /* 14 */
"backfield", /* 15 */
"backward", /* 16 */
"banjo", /* 17 */
"beaming", /* 18 */
"bedlamp", /* 19 */
"beehive", /* 1A */
"beeswax", /* 1B */
"befriend", /* 1C */
"Belfast", /* 1D */
"berserk", /* 1E */
"billiard", /* 1F */
"bison", /* 20 */
"blackjack", /* 21 */
"blockade", /* 22 */
"blowtorch", /* 23 */
"bluebird", /* 24 */
"bombast", /* 25 */
"bookshelf", /* 26 */
"brackish", /* 27 */
"breadline", /* 28 */
"breakup", /* 29 */
"brickyard", /* 2A */
"briefcase", /* 2B */
"Burbank", /* 2C */
"button", /* 2D */
"buzzard", /* 2E */
"cement", /* 2F */
"chairlift", /* 30 */
"chatter", /* 31 */
"checkup", /* 32 */
"chisel", /* 33 */
"choking", /* 34 */
"chopper", /* 35 */
"Christmas", /* 36 */
"clamshell", /* 37 */
"classic", /* 38 */
"classroom", /* 39 */
"cleanup", /* 3A */
"clockwork", /* 3B */
"cobra", /* 3C */
"commence", /* 3D */
"concert", /* 3E */
"cowbell", /* 3F */
"crackdown", /* 40 */
"cranky", /* 41 */
"crowfoot", /* 42 */
"crucial", /* 43 */
"crumpled", /* 44 */
"crusade", /* 45 */
"cubic", /* 46 */
"dashboard", /* 47 */
"deadbolt", /* 48 */
"deckhand", /* 49 */
"dogsled", /* 4A */
"dragnet", /* 4B */
"drainage", /* 4C */
"dreadful", /* 4D */
"drifter", /* 4E */
"dropper", /* 4F */
"drumbeat", /* 50 */
"drunken", /* 51 */
"Dupont", /* 52 */
"dwelling", /* 53 */
"eating", /* 54 */
"edict", /* 55 */
"egghead", /* 56 */
"eightball", /* 57 */
"endorse", /* 58 */
"endow", /* 59 */
"enlist", /* 5A */
"erase", /* 5B */
"escape", /* 5C */
"exceed", /* 5D */
"eyeglass", /* 5E */
"eyetooth", /* 5F */
"facial", /* 60 */
"fallout", /* 61 */
"flagpole", /* 62 */
"flatfoot", /* 63 */
"flytrap", /* 64 */
"fracture", /* 65 */
"framework", /* 66 */
"freedom", /* 67 */
"frighten", /* 68 */
"gazelle", /* 69 */
"Geiger", /* 6A */
"glitter", /* 6B */
"glucose", /* 6C */
"goggles", /* 6D */
"goldfish", /* 6E */
"gremlin", /* 6F */
"guidance", /* 70 */
"hamlet", /* 71 */
"highchair", /* 72 */
"hockey", /* 73 */
"indoors", /* 74 */
"indulge", /* 75 */
"inverse", /* 76 */
"involve", /* 77 */
"island", /* 78 */
"jawbone", /* 79 */
"keyboard", /* 7A */
"kickoff", /* 7B */
"kiwi", /* 7C */
"klaxon", /* 7D */
"locale", /* 7E */
"lockup", /* 7F */
"merit", /* 80 */
"minnow", /* 81 */
"miser", /* 82 */
"Mohawk", /* 83 */
"mural", /* 84 */
"music", /* 85 */
"necklace", /* 86 */
"Neptune", /* 87 */
"newborn", /* 88 */
"nightbird", /* 89 */
"Oakland", /* 8A */
"obtuse", /* 8B */
"offload", /* 8C */
"optic", /* 8D */
"orca", /* 8E */
"payday", /* 8F */
"peachy", /* 90 */
"pheasant", /* 91 */
"physique", /* 92 */
"playhouse", /* 93 */
"Pluto", /* 94 */
"preclude", /* 95 */
"prefer", /* 96 */
"preshrunk", /* 97 */
"printer", /* 98 */
"prowler", /* 99 */
"pupil", /* 9A */
"puppy", /* 9B */
"python", /* 9C */
"quadrant", /* 9D */
"quiver", /* 9E */
"quota", /* 9F */
"ragtime", /* A0 */
"ratchet", /* A1 */
"rebirth", /* A2 */
"reform", /* A3 */
"regain", /* A4 */
"reindeer", /* A5 */
"rematch", /* A6 */
"repay", /* A7 */
"retouch", /* A8 */
"revenge", /* A9 */
"reward", /* AA */
"rhythm", /* AB */
"ribcage", /* AC */
"ringbolt", /* AD */
"robust", /* AE */
"rocker", /* AF */
"ruffled", /* B0 */
"sailboat", /* B1 */
"sawdust", /* B2 */
"scallion", /* B3 */
"scenic", /* B4 */
"scorecard", /* B5 */
"Scotland", /* B6 */
"seabird", /* B7 */
"select", /* B8 */
"sentence", /* B9 */
"shadow", /* BA */
"shamrock", /* BB */
"showgirl", /* BC */
"skullcap", /* BD */
"skydive", /* BE */
"slingshot", /* BF */
"slowdown", /* C0 */
"snapline", /* C1 */
"snapshot", /* C2 */
"snowcap", /* C3 */
"snowslide", /* C4 */
"solo", /* C5 */
"southward", /* C6 */
"soybean", /* C7 */
"spaniel", /* C8 */
"spearhead", /* C9 */
"spellbind", /* CA */
"spheroid", /* CB */
"spigot", /* CC */
"spindle", /* CD */
"spyglass", /* CE */
"stagehand", /* CF */
"stagnate", /* D0 */
"stairway", /* D1 */
"standard", /* D2 */
"stapler", /* D3 */
"steamship", /* D4 */
"sterling", /* D5 */
"stockman", /* D6 */
"stopwatch", /* D7 */
"stormy", /* D8 */
"sugar", /* D9 */
"surmount", /* DA */
"suspense", /* DB */
"sweatband", /* DC */
"swelter", /* DD */
"tactics", /* DE */
"talon", /* DF */
"tapeworm", /* E0 */
"tempest", /* E1 */
"tiger", /* E2 */
"tissue", /* E3 */
"tonic", /* E4 */
"topmost", /* E5 */
"tracker", /* E6 */
"transit", /* E7 */
"trauma", /* E8 */
"treadmill", /* E9 */
"Trojan", /* EA */
"trouble", /* EB */
"tumor", /* EC */
"tunnel", /* ED */
"tycoon", /* EE */
"uncut", /* EF */
"unearth", /* F0 */
"unwind", /* F1 */
"uproot", /* F2 */
"upset", /* F3 */
"upshot", /* F4 */
"vapor", /* F5 */
"village", /* F6 */
"virus", /* F7 */
"Vulcan", /* F8 */
"waffle", /* F9 */
"wallet", /* FA */
"watchword", /* FB */
"wayside", /* FC */
"willow", /* FD */
"woodlark", /* FE */
"Zulu" /* FF */
};
const char * const odd[256] = {
"aardvark", /* 00 */
"absurd", /* 01 */
"accrue", /* 02 */
"acme", /* 03 */
"adrift", /* 04 */
"adult", /* 05 */
"afflict", /* 06 */
"ahead", /* 07 */
"aimless", /* 08 */
"Algol", /* 09 */
"allow", /* 0A */
"alone", /* 0B */
"ammo", /* 0C */
"ancient", /* 0D */
"apple", /* 0E */
"artist", /* 0F */
"assume", /* 10 */
"Athens", /* 11 */
"atlas", /* 12 */
"Aztec", /* 13 */
"baboon", /* 14 */
"backfield", /* 15 */
"backward", /* 16 */
"banjo", /* 17 */
"beaming", /* 18 */
"bedlamp", /* 19 */
"beehive", /* 1A */
"beeswax", /* 1B */
"befriend", /* 1C */
"Belfast", /* 1D */
"berserk", /* 1E */
"billiard", /* 1F */
"bison", /* 20 */
"blackjack", /* 21 */
"blockade", /* 22 */
"blowtorch", /* 23 */
"bluebird", /* 24 */
"bombast", /* 25 */
"bookshelf", /* 26 */
"brackish", /* 27 */
"breadline", /* 28 */
"breakup", /* 29 */
"brickyard", /* 2A */
"briefcase", /* 2B */
"Burbank", /* 2C */
"button", /* 2D */
"buzzard", /* 2E */
"cement", /* 2F */
"chairlift", /* 30 */
"chatter", /* 31 */
"checkup", /* 32 */
"chisel", /* 33 */
"choking", /* 34 */
"chopper", /* 35 */
"Christmas", /* 36 */
"clamshell", /* 37 */
"classic", /* 38 */
"classroom", /* 39 */
"cleanup", /* 3A */
"clockwork", /* 3B */
"cobra", /* 3C */
"commence", /* 3D */
"concert", /* 3E */
"cowbell", /* 3F */
"crackdown", /* 40 */
"cranky", /* 41 */
"crowfoot", /* 42 */
"crucial", /* 43 */
"crumpled", /* 44 */
"crusade", /* 45 */
"cubic", /* 46 */
"dashboard", /* 47 */
"deadbolt", /* 48 */
"deckhand", /* 49 */
"dogsled", /* 4A */
"dragnet", /* 4B */
"drainage", /* 4C */
"dreadful", /* 4D */
"drifter", /* 4E */
"dropper", /* 4F */
"drumbeat", /* 50 */
"drunken", /* 51 */
"Dupont", /* 52 */
"dwelling", /* 53 */
"eating", /* 54 */
"edict", /* 55 */
"egghead", /* 56 */
"eightball", /* 57 */
"endorse", /* 58 */
"endow", /* 59 */
"enlist", /* 5A */
"erase", /* 5B */
"escape", /* 5C */
"exceed", /* 5D */
"eyeglass", /* 5E */
"eyetooth", /* 5F */
"facial", /* 60 */
"fallout", /* 61 */
"flagpole", /* 62 */
"flatfoot", /* 63 */
"flytrap", /* 64 */
"fracture", /* 65 */
"framework", /* 66 */
"freedom", /* 67 */
"frighten", /* 68 */
"gazelle", /* 69 */
"Geiger", /* 6A */
"glitter", /* 6B */
"glucose", /* 6C */
"goggles", /* 6D */
"goldfish", /* 6E */
"gremlin", /* 6F */
"guidance", /* 70 */
"hamlet", /* 71 */
"highchair", /* 72 */
"hockey", /* 73 */
"indoors", /* 74 */
"indulge", /* 75 */
"inverse", /* 76 */
"involve", /* 77 */
"island", /* 78 */
"jawbone", /* 79 */
"keyboard", /* 7A */
"kickoff", /* 7B */
"kiwi", /* 7C */
"klaxon", /* 7D */
"locale", /* 7E */
"lockup", /* 7F */
"merit", /* 80 */
"minnow", /* 81 */
"miser", /* 82 */
"Mohawk", /* 83 */
"mural", /* 84 */
"music", /* 85 */
"necklace", /* 86 */
"Neptune", /* 87 */
"newborn", /* 88 */
"nightbird", /* 89 */
"Oakland", /* 8A */
"obtuse", /* 8B */
"offload", /* 8C */
"optic", /* 8D */
"orca", /* 8E */
"payday", /* 8F */
"peachy", /* 90 */
"pheasant", /* 91 */
"physique", /* 92 */
"playhouse", /* 93 */
"Pluto", /* 94 */
"preclude", /* 95 */
"prefer", /* 96 */
"preshrunk", /* 97 */
"printer", /* 98 */
"prowler", /* 99 */
"pupil", /* 9A */
"puppy", /* 9B */
"python", /* 9C */
"quadrant", /* 9D */
"quiver", /* 9E */
"quota", /* 9F */
"ragtime", /* A0 */
"ratchet", /* A1 */
"rebirth", /* A2 */
"reform", /* A3 */
"regain", /* A4 */
"reindeer", /* A5 */
"rematch", /* A6 */
"repay", /* A7 */
"retouch", /* A8 */
"revenge", /* A9 */
"reward", /* AA */
"rhythm", /* AB */
"ribcage", /* AC */
"ringbolt", /* AD */
"robust", /* AE */
"rocker", /* AF */
"ruffled", /* B0 */
"sailboat", /* B1 */
"sawdust", /* B2 */
"scallion", /* B3 */
"scenic", /* B4 */
"scorecard", /* B5 */
"Scotland", /* B6 */
"seabird", /* B7 */
"select", /* B8 */
"sentence", /* B9 */
"shadow", /* BA */
"shamrock", /* BB */
"showgirl", /* BC */
"skullcap", /* BD */
"skydive", /* BE */
"slingshot", /* BF */
"slowdown", /* C0 */
"snapline", /* C1 */
"snapshot", /* C2 */
"snowcap", /* C3 */
"snowslide", /* C4 */
"solo", /* C5 */
"southward", /* C6 */
"soybean", /* C7 */
"spaniel", /* C8 */
"spearhead", /* C9 */
"spellbind", /* CA */
"spheroid", /* CB */
"spigot", /* CC */
"spindle", /* CD */
"spyglass", /* CE */
"stagehand", /* CF */
"stagnate", /* D0 */
"stairway", /* D1 */
"standard", /* D2 */
"stapler", /* D3 */
"steamship", /* D4 */
"sterling", /* D5 */
"stockman", /* D6 */
"stopwatch", /* D7 */
"stormy", /* D8 */
"sugar", /* D9 */
"surmount", /* DA */
"suspense", /* DB */
"sweatband", /* DC */
"swelter", /* DD */
"tactics", /* DE */
"talon", /* DF */
"tapeworm", /* E0 */
"tempest", /* E1 */
"tiger", /* E2 */
"tissue", /* E3 */
"tonic", /* E4 */
"topmost", /* E5 */
"tracker", /* E6 */
"transit", /* E7 */
"trauma", /* E8 */
"treadmill", /* E9 */
"Trojan", /* EA */
"trouble", /* EB */
"tumor", /* EC */
"tunnel", /* ED */
"tycoon", /* EE */
"uncut", /* EF */
"unearth", /* F0 */
"unwind", /* F1 */
"uproot", /* F2 */
"upset", /* F3 */
"upshot", /* F4 */
"vapor", /* F5 */
"village", /* F6 */
"virus", /* F7 */
"Vulcan", /* F8 */
"waffle", /* F9 */
"wallet", /* FA */
"watchword", /* FB */
"wayside", /* FC */
"willow", /* FD */
"woodlark", /* FE */
"Zulu", /* FF */
};

7
fw/src/words.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __ADJECTIVES_H__
#define __ADJECTIVES_H__
extern const char * const even[256];
extern const char * const odd[256];
#endif