Add 'fw/' from commit '5b94dee9cf'
git-subtree-dir: fw git-subtree-mainline:6eddc61626git-subtree-split:5b94dee9cf
This commit is contained in:
commit
b328ef6059
63 changed files with 13005 additions and 0 deletions
15
fw/.gitignore
vendored
Normal file
15
fw/.gitignore
vendored
Normal 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
3
fw/.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "libopencm3"]
|
||||||
|
path = libopencm3
|
||||||
|
url = https://amirhammad@github.com/amirhammad/libopencm3
|
||||||
17
fw/.travis.yml
Normal file
17
fw/.travis.yml
Normal 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
108
fw/CMakeLists.txt
Normal 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
674
fw/COPYING.GPL3
Normal 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
165
fw/COPYING.LGPL3
Normal 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
2406
fw/Doxyfile
Normal file
File diff suppressed because it is too large
Load diff
105
fw/README.md
Normal file
105
fw/README.md
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
[](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
88
fw/USB_icon.svg
Normal 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
34
fw/adjectives.py
Normal 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
1
fw/build/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
*
|
||||||
6
fw/cmake/doc.cmake
Normal file
6
fw/cmake/doc.cmake
Normal 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
|
||||||
|
)
|
||||||
18
fw/cmake/openocd_flash.cmake
Normal file
18
fw/cmake/openocd_flash.cmake
Normal 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
15
fw/cmake/toolchain.cmake
Normal 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
54
fw/hexcom.py
Executable 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
27
fw/hexdump.py
Normal 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
384
fw/hexnoise.py
Executable 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
27
fw/include/cobs.h
Normal 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__
|
||||||
326
fw/include/driver/usbh_device_driver.h
Normal file
326
fw/include/driver/usbh_device_driver.h
Normal 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
62
fw/include/usbh_config.h
Normal 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
65
fw/include/usbh_core.h
Normal 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_
|
||||||
65
fw/include/usbh_driver_ac_midi.h
Normal file
65
fw/include/usbh_driver_ac_midi.h
Normal 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
|
||||||
78
fw/include/usbh_driver_gp_xbox.h
Normal file
78
fw/include/usbh_driver_gp_xbox.h
Normal 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
|
||||||
85
fw/include/usbh_driver_hid.h
Normal file
85
fw/include/usbh_driver_hid.h
Normal 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
|
||||||
39
fw/include/usbh_driver_hub.h
Normal file
39
fw/include/usbh_driver_hub.h
Normal 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
|
||||||
44
fw/include/usbh_lld_stm32f4.h
Normal file
44
fw/include/usbh_lld_stm32f4.h
Normal 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
4
fw/initRepo.sh
Executable 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
0
fw/keymap.py
Normal file
1
fw/libopencm3
Submodule
1
fw/libopencm3
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 798c1edf4d11e8a40a7263dc465fa225a63fa7e9
|
||||||
52
fw/libusbhost_stm32f4.ld
Normal file
52
fw/libusbhost_stm32f4.ld
Normal 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
34
fw/nouns.py
Normal 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
16
fw/openocd.cfg
Normal 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
164
fw/pairing.py
Executable 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
BIN
fw/secureusb_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
69
fw/src/CMakeLists.txt
Normal file
69
fw/src/CMakeLists.txt
Normal 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
390
fw/src/cobs.c
Normal 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
|
||||||
|
|
||||||
93
fw/src/crypto/CMakeLists.txt
Normal file
93
fw/src/crypto/CMakeLists.txt
Normal 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
684
fw/src/demo.c
Normal 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
1
fw/src/frama_c_cmdline
Normal 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
48
fw/src/hid_keycodes.c
Normal 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
218
fw/src/hid_keycodes.h
Normal 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
445
fw/src/noise.c
Normal 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
56
fw/src/noise.h
Normal 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
98
fw/src/packet_interface.c
Normal 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
58
fw/src/packet_interface.h
Normal 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
257
fw/src/pgp_wordlist
Normal 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
135
fw/src/rand_stm32.c
Normal 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
11
fw/src/rand_stm32.h
Normal 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
523
fw/src/tinyprintf.c
Normal 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
186
fw/src/tinyprintf.h
Normal 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
25
fw/src/tracing.h
Normal 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
149
fw/src/usart_helpers.c
Normal 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
88
fw/src/usart_helpers.h
Normal 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
726
fw/src/usbh_core.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
364
fw/src/usbh_driver_ac_midi.c
Normal file
364
fw/src/usbh_driver_ac_midi.c
Normal 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
|
||||||
|
};
|
||||||
52
fw/src/usbh_driver_ac_midi_private.h
Normal file
52
fw/src/usbh_driver_ac_midi_private.h
Normal 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
|
||||||
368
fw/src/usbh_driver_gp_xbox.c
Normal file
368
fw/src/usbh_driver_gp_xbox.c
Normal 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
412
fw/src/usbh_driver_hid.c
Normal 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
675
fw/src/usbh_driver_hub.c
Normal 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
|
||||||
|
};
|
||||||
123
fw/src/usbh_driver_hub_private.h
Normal file
123
fw/src/usbh_driver_hub_private.h
Normal 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
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
521
fw/src/words.c
Normal 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
7
fw/src/words.h
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef __ADJECTIVES_H__
|
||||||
|
#define __ADJECTIVES_H__
|
||||||
|
|
||||||
|
extern const char * const even[256];
|
||||||
|
extern const char * const odd[256];
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Add a link
Reference in a new issue