From 7acc6fe474766788687a5257be21ac549bed77f3 Mon Sep 17 00:00:00 2001 From: Amir Hammad Date: Wed, 1 Apr 2015 16:22:05 +0200 Subject: libusbhost: Open source USB host stack for embedded devices First public version, date: 1.4.2015 Signed-off-by: Amir Hammad --- .gitignore | 9 + .gitmodules | 3 + COPYING.GPL3 | 674 ++++++++++++++++++++++ COPYING.LGPL3 | 165 ++++++ DEVICE_DRIVER_HOWTO | 3 + Makefile | 256 +++++++++ README.md | 78 +++ build/.gitignore | 0 compileDemo.sh | 2 + config.mk | 10 + include/driver/usbh_device_driver.h | 131 +++++ include/usbh_config.h | 56 ++ include/usbh_driver_gp_xbox.h | 73 +++ include/usbh_driver_hid_mouse.h | 43 ++ include/usbh_driver_hub.h | 36 ++ include/usbh_hubbed.h | 76 +++ include/usbh_lld_stm32f4.h | 43 ++ initRepo.sh | 4 + libopencm3 | 1 + libopencm3_stm32f4.ld | 32 ++ src/demo.c | 186 +++++++ src/usart_helpers.c | 287 ++++++++++ src/usart_helpers.h | 56 ++ src/usbh_driver_gp_xbox.c | 420 ++++++++++++++ src/usbh_driver_hid_mouse.c | 294 ++++++++++ src/usbh_driver_hub.c | 865 +++++++++++++++++++++++++++++ src/usbh_driver_hub_private.h | 108 ++++ src/usbh_hubbed.c | 634 +++++++++++++++++++++ src/usbh_lld_stm32f4.c | 1048 +++++++++++++++++++++++++++++++++++ 29 files changed, 5593 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 COPYING.GPL3 create mode 100644 COPYING.LGPL3 create mode 100644 DEVICE_DRIVER_HOWTO create mode 100644 Makefile create mode 100644 README.md create mode 100644 build/.gitignore create mode 100755 compileDemo.sh create mode 100644 config.mk create mode 100644 include/driver/usbh_device_driver.h create mode 100644 include/usbh_config.h create mode 100644 include/usbh_driver_gp_xbox.h create mode 100644 include/usbh_driver_hid_mouse.h create mode 100644 include/usbh_driver_hub.h create mode 100644 include/usbh_hubbed.h create mode 100644 include/usbh_lld_stm32f4.h create mode 100755 initRepo.sh create mode 160000 libopencm3 create mode 100644 libopencm3_stm32f4.ld create mode 100644 src/demo.c create mode 100644 src/usart_helpers.c create mode 100644 src/usart_helpers.h create mode 100644 src/usbh_driver_gp_xbox.c create mode 100644 src/usbh_driver_hid_mouse.c create mode 100644 src/usbh_driver_hub.c create mode 100644 src/usbh_driver_hub_private.h create mode 100644 src/usbh_hubbed.c create mode 100644 src/usbh_lld_stm32f4.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdf34d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.o +*.d +*.elf +*.map +*.bin +*.hex +*.cproject +*.project +*.a diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2822909 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libopencm3"] + path = libopencm3 + url = https://amirhammad@github.com/amirhammad/libopencm3 diff --git a/COPYING.GPL3 b/COPYING.GPL3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING.GPL3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/COPYING.LGPL3 b/COPYING.LGPL3 new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/COPYING.LGPL3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. diff --git a/DEVICE_DRIVER_HOWTO b/DEVICE_DRIVER_HOWTO new file mode 100644 index 0000000..e26c9f1 --- /dev/null +++ b/DEVICE_DRIVER_HOWTO @@ -0,0 +1,3 @@ +TODO + +See usbh_driver*.c in src directory for example of device drivers. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9fe1c80 --- /dev/null +++ b/Makefile @@ -0,0 +1,256 @@ +## +## This file is part of the libusbhost project. +## Imported and adopted from libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## Copyright (C) 2013 Frantisek Burian +## Copyright (C) 2014 Amir Hammad +## +## 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 . +## + +BINARY = demo +BINARY := $(addprefix build/, demo) +LIBUSBHOSTNAME = usbhost + +LIBUSBHOST := $(addprefix build/lib, $(LIBUSBHOSTNAME)) +LIBNAME = opencm3_stm32f4 +DEFS = -DSTM32F4 + +# load user config +include config.mk +DEFS += $(USER_CONFIG) + +ifdef USART_DEBUG +DEFS += -DUSART_DEBUG +endif + +DEFS += -Iinclude +LDSCRIPT = lib$(LIBNAME).ld + +SRCDIR = src +OPENCM3_DIR ?= ./libopencm3 +FP_FLAGS ?= -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mfp16-format=alternative +ARCH_FLAGS = -mthumb -mcpu=cortex-m4 $(FP_FLAGS) + +################################################################################ +# OpenOCD specific variables + +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2 +OOCD_BOARD ?= stm32f4discovery + +################################################################################ +# Black Magic Probe specific variables +# Set the BMP_PORT to a serial port and then BMP is used for flashing +BMP_PORT ?= + +################################################################################ +# texane/stlink specific variables +#STLINK_PORT ?= :4242 + +# Be silent per default, but 'make V=1' will show all compiler calls. +ifneq ($(V),1) +Q := @ +NULL := 2>/dev/null +endif + +############################################################################### +# Executables + +PREFIX ?= arm-none-eabi + +CC := $(PREFIX)-gcc +CXX := $(PREFIX)-g++ +LD := $(PREFIX)-gcc +AR := $(PREFIX)-ar +AS := $(PREFIX)-as +OBJCOPY := $(PREFIX)-objcopy +OBJDUMP := $(PREFIX)-objdump +GDB := $(PREFIX)-gdb +STFLASH = $(shell which st-flash) +STYLECHECK := /checkpatch.pl +STYLECHECKFLAGS := --no-tree -f --terse --mailback +STYLECHECKFILES := $(shell find . -name '*.[ch]') + + +############################################################################### +# Source files + +LDSCRIPT ?= $(BINARY).ld + + +SRCS = $(sort $(notdir $(wildcard $(SRCDIR)/*.c))) +OBJSDEMO = $(patsubst %.c, build/%.o ,$(SRCS)) +OBJS = $(filter-out $(BINARY).o, $(OBJSDEMO)) + + +INCLUDE_DIR = $(OPENCM3_DIR)/include +LIB_DIR = $(OPENCM3_DIR)/lib +SCRIPT_DIR = $(OPENCM3_DIR)/scripts + +############################################################################### +# C flags + +CFLAGS += -Ofast -g +CFLAGS += -Wextra -Wshadow -Wimplicit-function-declaration +CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes +CFLAGS += -fno-common -ffunction-sections -fdata-sections + +############################################################################### +# C++ flags + +CXXFLAGS += -Ofast -g +CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ +CXXFLAGS += -fno-common -ffunction-sections -fdata-sections + +############################################################################### +# C & C++ preprocessor common flags + +CPPFLAGS += -MD +CPPFLAGS += -Wall -Wundef +CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS) + +############################################################################### +# Linker flags + +LDFLAGS += --static -nostartfiles +LDFLAGS += -L$(LIB_DIR) +LDFLAGS += -T$(LDSCRIPT) +LDFLAGS += -Wl,-Map=build/$*.map +LDFLAGS += -Wl,--gc-sections +ifeq ($(V),99) +LDFLAGS += -Wl,--print-gc-sections +endif + +############################################################################### +# Used libraries + +LDLIBS += -l$(LIBNAME) +LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +############################################################################### +############################################################################### +############################################################################### + +.SUFFIXES: .elf .bin .hex .srec .list .map .images +.SECONDEXPANSION: +.SECONDARY: + + +all: elf bin lib + +elf: $(BINARY).elf +bin: $(BINARY).bin +hex: $(BINARY).hex +srec: $(BINARY).srec +list: $(BINARY).list +lib: $(LIBUSBHOST).a +images: $(BINARY).images +flash: $(BINARY).flash + +%.images: %.bin %.hex %.srec %.list %.map + @#printf "*** $* images generated ***\n" + +%.bin: %.elf + @printf " OBJCOPY $(*).bin\n" + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + +%.hex: %.elf + @#printf " OBJCOPY $(*).hex\n" + $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex + +%.srec: %.elf + @#printf " OBJCOPY $(*).srec\n" + $(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec + +%.list: %.elf + @#printf " OBJDUMP $(*).list\n" + $(Q)$(OBJDUMP) -S $(*).elf > $(*).list + +-include $(OBJSDEMO:.o=.d) +build/%.elf build/%.map: $(OBJSDEMO) $(LDSCRIPT) + @printf " LD $(*).elf\n" + $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJSDEMO) $(LDLIBS) -o build/$*.elf + +build/%.o:$(SRCDIR)/%.c + @printf " CC $(*).c\n" + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $(SRCDIR)/$*.c + +build/%.o: $(SRCDIR)/%.cxx + @printf " CXX $(*).cxx\n" + $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $(SRCDIR)/$(*).cxx + +build/%.o: $(SRCDIR)/%.cpp + @printf " CXX $(*).cpp\n" + $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $(SRCDIR)/$(*).cpp +$(LIB_DIR)/lib$(LIBNAME).a: + +clean: + @#printf " CLEAN\n" + @rm -f build/* + + +%.stlink-flash: %.bin + @printf " FLASH $<\n" + $(Q)$(STFLASH) write $(*).bin 0x8000000 + +ifeq ($(STLINK_PORT),) +ifeq ($(BMP_PORT),) +ifeq ($(OOCD_SERIAL),) +%.flash: %.hex + @printf " FLASH $<\n" + @# IMPORTANT: Don't use "resume", only "reset" will work correctly! + $(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \ + -f board/$(OOCD_BOARD).cfg \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset" \ + -c "shutdown" $(NULL) +else +%.flash: %.hex + @printf " FLASH $<\n" + @# IMPORTANT: Don't use "resume", only "reset" will work correctly! + $(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \ + -f board/$(OOCD_BOARD).cfg \ + -c "ft2232_serial $(OOCD_SERIAL)" \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset" \ + -c "shutdown" $(NULL) +endif +else +%.flash: %.elf + @printf " GDB $(*).elf (flash)\n" + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(BMP_PORT)' \ + -x $(SCRIPT_DIR)/black_magic_probe_flash.scr \ + $(*).elf +endif +else +%.flash: %.elf + @printf " GDB $(*).elf (flash)\n" + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(STLINK_PORT)' \ + -x $(SCRIPT_DIR)/stlink_flash.scr \ + $(*).elf +endif + +.PHONY: images clean stylecheck styleclean elf bin hex srec list testing + +-include $(OBJS:.o=.d) +build/lib$(LIBUSBHOSTNAME).a: $(OBJS) + @printf " LIB $@\n" + $(Q)$(AR) rcs $@ $(OBJS) diff --git a/README.md b/README.md new file mode 100644 index 0000000..ff631ad --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +###General Information + + +**This library is in an active development.** +**WARNING**: None of its features are considered stable ! + +This library implement usb host driver allowing users use +or write device drivers, which functionality +is abstracted of low level implementation. + +Main objectives are: +- 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 +- uses static allocation for all its buffers, +so no allocation and reallocation is affecting performance +(possibility of memory fragmentation. execution time indeterminism), +so no malloc(), realloc(), free(). +- written in C, with the support to use it with C++. +- does not depend on any Operating System. Library libopencm3 is used for testing purposes and to get proper defines. +So no runtime dependency is on this library. + + + +Currently supported devices (yet tested) are: +* stm32f407 (stm32f4 Discovery) + +Native device drivers (mostly for demonstration purposes): +- HUB +- Gamepad - XBox compatible Controller +- mouse (draft: only displays raw data) + +###Practical info + +!!! Do not forget to invoke "make clean" before new build when defines change + +**How to initialize repository** + +> ./initRepo.sh + +fetch libopencm3 submodule and compile needed libraries + + +**How to compile demo** + +Edit config.mk for library configuration (By default Full speed OTG periphery on stm32f4 is supported) + + +> ./compileDemo.sh + +compiles demo, that can be flashed into stm32f4 Discovery platform and debug by USART + + +**How to upload firmware (FLASH) to stm32f4 Discovery** + +> sudo make flash + + +**How to view debug data** + +connect uart to USART6 pins on gpios: GPIOC6(TX - data), GPIOC7(RX - not used) +configure uart baud on PC side to 921600 with 1 stop bit, no parity, 8bit data, no handshake + + +**How to compile library only** + +> make lib + +**libusbhost.a** is built without usart debug support +(check compileDemo.sh for hint on how to compile with debug) + + +###Contact +Amir Hammad - *amir.hammad@hotmail.com* + +**Library is maintained there** +> http://github.com/libusbhost/libusbhost + diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/compileDemo.sh b/compileDemo.sh new file mode 100755 index 0000000..7731473 --- /dev/null +++ b/compileDemo.sh @@ -0,0 +1,2 @@ +#!/bin/sh +USART_DEBUG=1 OPENCM3_DIR=libopencm3 make all diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..03fae74 --- /dev/null +++ b/config.mk @@ -0,0 +1,10 @@ + + +USER_CONFIG = + + +# Uncomment to enable OTG_HS support - low level driver +#USER_CONFIG += -DUSE_STM32F4_USBH_DRIVER_HS + +# Uncomment to enable OTG_FS support - low level driver +USER_CONFIG += -DUSE_STM32F4_USBH_DRIVER_FS diff --git a/include/driver/usbh_device_driver.h b/include/driver/usbh_device_driver.h new file mode 100644 index 0000000..f8837e0 --- /dev/null +++ b/include/driver/usbh_device_driver.h @@ -0,0 +1,131 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_DEVICE_DRIVER_ +#define USBH_DEVICE_DRIVER_ + +#include "usbh_config.h" +#include "usbh_hubbed.h" + +#include + +BEGIN_DECLS + +#define USBH_EPTYP_CONTROL (0) +#define USBH_EPTYP_ISOCHRONOUS (1) +#define USBH_EPTYP_BULK (2) +#define USBH_EPTYP_INTERRUPT (3) + +#define USBH_SPEED_FULL (0) +#define USBH_SPEED_LOW (1) +#define 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 +}; + +struct _usbh_device { + uint16_t packet_size_max0; + int8_t address; + uint8_t speed; // (USBH_SPEED_*) + uint8_t state; // for enumeration purposes + uint8_t toggle0; + const usbh_dev_driver_t *drv; + void *drvdata; + const void *lld; +}; +typedef struct _usbh_device usbh_device_t; + +struct _usbh_packet_callback_data { + enum USBH_PACKET_CALLBACK_STATUS status; + 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_packet { + void *data; // Pointer to data + uint16_t datalen; // Data length 0..1023 + int8_t address; // Device address + uint8_t endpoint_type; // Endpoint type (see USBH_EPTYP_*) + uint8_t endpoint_address; // Endpoint number 0..15 + uint16_t endpoint_size_max; // Max packet size for an endpoint + uint8_t speed; // (USBH_SPEED_*) + uint8_t *toggle; + usbh_packet_callback_t callback; + void *callback_arg; +}; +typedef struct _usbh_packet usbh_packet_t; + +struct _usbh_driver { + void (*init)(void *drvdata); + void (*write)(void *drvdata, const usbh_packet_t *packet); + void (*read)(void *drvdata, usbh_packet_t *packet); + enum USBH_POLL_STATUS (*poll)(void *drvdata, uint32_t t_us); + uint8_t (*root_speed)(void *drvdata); + + // Pointer to Low-level driver data + void *driver_data; +}; +typedef struct _usbh_driver usbh_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; + + +#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 +void device_xfer_control_read(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev); +void device_xfer_control_write(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev); + + +END_DECLS + +#endif diff --git a/include/usbh_config.h b/include/usbh_config.h new file mode 100644 index 0000000..1b60954 --- /dev/null +++ b/include/usbh_config.h @@ -0,0 +1,56 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#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) + +// MOUSE +#define USBH_HID_MOUSE_MAX_DEVICES (2) + +#define USBH_HID_MOUSE_BUFFER (32) + +// 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 diff --git a/include/usbh_driver_gp_xbox.h b/include/usbh_driver_gp_xbox.h new file mode 100644 index 0000000..8080f30 --- /dev/null +++ b/include/usbh_driver_gp_xbox.h @@ -0,0 +1,73 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_DRIVER_GP_XBOX_ +#define USBH_DRIVER_GP_XBOX_ + +#include "usbh_hubbed.h" + +#include + +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; + +void gp_xbox_driver_init(const gp_xbox_config_t *config); + +extern const usbh_dev_driver_t usbh_gp_xbox_driver; + +END_DECLS + +#endif diff --git a/include/usbh_driver_hid_mouse.h b/include/usbh_driver_hid_mouse.h new file mode 100644 index 0000000..c12fed4 --- /dev/null +++ b/include/usbh_driver_hid_mouse.h @@ -0,0 +1,43 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_DRIVER_HID_MOUSE_ +#define USBH_DRIVER_HID_MOUSE_ + +#include "usbh_hubbed.h" + +#include + +BEGIN_DECLS + +struct _hid_mouse_config { + void (*mouse_in_message_handler)(uint8_t device_id, const uint8_t *data); +}; +typedef struct _hid_mouse_config hid_mouse_config_t; + +void hid_mouse_driver_init(const hid_mouse_config_t *config); + +extern const usbh_dev_driver_t usbh_hid_mouse_driver; + +END_DECLS + +#endif diff --git a/include/usbh_driver_hub.h b/include/usbh_driver_hub.h new file mode 100644 index 0000000..cda1463 --- /dev/null +++ b/include/usbh_driver_hub.h @@ -0,0 +1,36 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_DRIVER_HUB_ +#define USBH_DRIVER_HUB_ + +#include "usbh_hubbed.h" + +BEGIN_DECLS + +void hub_driver_init(void); + +extern const usbh_dev_driver_t usbh_hub_driver; + +END_DECLS + +#endif diff --git a/include/usbh_hubbed.h b/include/usbh_hubbed.h new file mode 100644 index 0000000..00d3b99 --- /dev/null +++ b/include/usbh_hubbed.h @@ -0,0 +1,76 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_HUBBED_ +#define USBH_HUBBED_ + +#include "usbh_config.h" + +#include + +/* 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 + +#ifndef bool +#define bool _Bool +#define false 0 +#define true 1 +#endif + + +// set to -1 to unused items +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 { + bool (*analyze_descriptor)(void *drv, void *descriptor); + void *(*init)(void *usbh_dev); + void (*poll)(void *drvdata, uint32_t t_us); + void (*remove)(void *drvdata); + const usbh_dev_driver_info_t * const info; +}; +typedef struct _usbh_dev_driver usbh_dev_driver_t; + +void usbh_init(const void *drivers[], const usbh_dev_driver_t * const device_drivers[]); +void usbh_poll(uint32_t t_us); + +END_DECLS + +#endif diff --git a/include/usbh_lld_stm32f4.h b/include/usbh_lld_stm32f4.h new file mode 100644 index 0000000..c55c615 --- /dev/null +++ b/include/usbh_lld_stm32f4.h @@ -0,0 +1,43 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_LLD_STM32F4_H_ +#define USBH_LLD_STM32F4_H_ + +#include "usbh_hubbed.h" + +#include + +BEGIN_DECLS + +// pass this to usbh init +extern const void *usbh_lld_stm32f4_drivers[]; + +#ifdef USART_DEBUG +void print_channels(const void *drvdata); +#else +#define print_channels(arg) ((void)arg) +#endif + +END_DECLS + +#endif diff --git a/initRepo.sh b/initRepo.sh new file mode 100755 index 0000000..10ff5cb --- /dev/null +++ b/initRepo.sh @@ -0,0 +1,4 @@ +#!/bin/sh +git submodule init +git submodule update +make -C libopencm3 -j3 lib/stm32/f4 diff --git a/libopencm3 b/libopencm3 new file mode 160000 index 0000000..798c1ed --- /dev/null +++ b/libopencm3 @@ -0,0 +1 @@ +Subproject commit 798c1edf4d11e8a40a7263dc465fa225a63fa7e9 diff --git a/libopencm3_stm32f4.ld b/libopencm3_stm32f4.ld new file mode 100644 index 0000000..46a80f2 --- /dev/null +++ b/libopencm3_stm32f4.ld @@ -0,0 +1,32 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * Copyright (C) 2011 Stephen Caudle + * + * 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 . + */ + +/* Linker script for ST STM32F4DISCOVERY (STM32F407VG, 1024K flash, 128K RAM). */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} + +/* Include the common ld script. */ +INCLUDE ./libopencm3/lib/libopencm3_stm32f4.ld + diff --git a/src/demo.c b/src/demo.c new file mode 100644 index 0000000..f4ef456 --- /dev/null +++ b/src/demo.c @@ -0,0 +1,186 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#include "usart_helpers.h" /// provides LOG_PRINTF macros used for debugging +#include "usbh_hubbed.h" /// provides usbh_init() and usbh_poll() +#include "usbh_lld_stm32f4.h" /// provides low level usb host driver for stm32f4 platform +#include "usbh_driver_hid_mouse.h" /// provides usb device driver Human Interface Device - type mouse +#include "usbh_driver_hub.h" /// provides usb full speed hub driver (Low speed devices on hub are not supported) +#include "usbh_driver_gp_xbox.h" /// provides usb device driver for Gamepad: Microsoft XBOX compatible Controller + + // STM32f407 compatible +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static inline void delay_ms_busy_loop(uint32_t ms) +{ + volatile uint32_t i; + for (i = 0; i < 14903*ms; i++); +} + + +/* Set STM32 to 168 MHz. */ +static void clock_setup(void) +{ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + // GPIO + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPAEN); // OTG_FS + button + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPBEN); // OTG_HS + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPCEN); // USART + OTG_FS charge pump + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); // LEDS + + // periphery + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_USART6EN);// USART + rcc_peripheral_enable_clock(&RCC_AHB2ENR, RCC_AHB2ENR_OTGFSEN); + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_OTGHSEN); +} + +static void gpio_setup(void) +{ + /* Set GPIO12-15 (in GPIO port D) to 'output push-pull'. */ + gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, GPIO12 | GPIO13 | GPIO14 | GPIO15); + + /* Set */ + gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0); + gpio_clear(GPIOC, GPIO0); + + // OTG_FS + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); + + // OTG_HS + gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO15 | GPIO14); + gpio_set_af(GPIOB, GPIO_AF12, GPIO14 | GPIO15); + + // USART TX + gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7); + gpio_set_af(GPIOC, GPIO_AF8, GPIO6 | GPIO7); + + // button + gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO0); +} + +static const usbh_dev_driver_t *device_drivers[] = { + &usbh_hub_driver, + &usbh_hid_mouse_driver, + &usbh_gp_xbox_driver, + 0 +}; + +static void gp_xbox_update(uint8_t device_id, gp_xbox_packet_t packet) +{ + (void)device_id; + (void)packet; + LOG_PRINTF("update %d: %d %d \r\n", device_id, packet.axis_left_x, packet.buttons & GP_XBOX_BUTTON_A); +} + + +static void gp_xbox_connected(uint8_t device_id) +{ + (void)device_id; + LOG_PRINTF("connected %d", device_id); +} + +static void gp_xbox_disconnected(uint8_t device_id) +{ + (void)device_id; + LOG_PRINTF("disconnected %d", device_id); +} + +static const gp_xbox_config_t gp_xbox_config = { + .update = &gp_xbox_update, + .notify_connected = &gp_xbox_connected, + .notify_disconnected = &gp_xbox_disconnected +}; + +static void mouse_in_message_handler(uint8_t device_id, const uint8_t *data) +{ + (void)device_id; + (void)data; + // print only first 4 bytes, since every mouse should have at least these four set. + // Report descriptors are not read by driver for now, so we do not know what each byte means + LOG_PRINTF("MOUSE EVENT %02X %02X %02X %02X \r\n", data[0], data[1], data[2], data[3]); +} + +static const hid_mouse_config_t mouse_config = { + .mouse_in_message_handler = &mouse_in_message_handler +}; + +int main(void) +{ + clock_setup(); + gpio_setup(); + +#ifdef USART_DEBUG + usart_init(USART6, 921600); +#endif + LOG_PRINTF("\r\n\r\n\r\n\r\n\r\n###################\r\nInit\r\n"); + + /** + * device driver initialization + * + * Pass configuration struct where the callbacks are defined + */ + hid_mouse_driver_init(&mouse_config); + hub_driver_init(); + gp_xbox_driver_init(&gp_xbox_config); + + gpio_set(GPIOD, GPIO13); + + /** + * Pass array of supported low level drivers + * In case of stm32f407, there are up to two supported OTG hosts on one chip. + * Each one can be enabled or disabled in config.mk - optimization for speed + * + * Pass array of supported device drivers + */ + usbh_init(usbh_lld_stm32f4_drivers, device_drivers); + gpio_clear(GPIOD, GPIO13); + + LOG_PRINTF("USB init complete\r\n"); + + uint32_t i = 0; + + while (1) { + LOG_FLUSH(); + + // Toggle some led + gpio_set(GPIOD, GPIO14); + usbh_poll(i); + gpio_clear(GPIOD, GPIO14); + + delay_ms_busy_loop(1); + i += 1000; + } + + return 0; +} diff --git a/src/usart_helpers.c b/src/usart_helpers.c new file mode 100644 index 0000000..31a7556 --- /dev/null +++ b/src/usart_helpers.c @@ -0,0 +1,287 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + + +#include "usart_helpers.h" + +#include +#include +#include +#include +#include +#include + + +#ifndef USART_DEBUG + +void usart_init(uint32_t usart, uint32_t baudrate) +{ + (void)usart; + (void)baudrate; +} +void usart_printf(const char *str, ...) +{ + (void)str; +} +void usart_vprintf(const char *str, va_list va) +{ + (void)va; + (void)str; +} +void usart_fifo_send(void){} + +void usart_call_cmd(struct usart_commands * commands) +{ + (void)commands; +} +void usart_interrupt(void){} + +#else +#warning compiling with debug functions + +#define USART_FIFO_OUT_SIZE (4096) +uint8_t usart_fifo_out_data[USART_FIFO_OUT_SIZE]; +uint32_t usart_fifo_out_len = 0; +uint32_t usart_fifo_out_index = 0; + +#define USART_FIFO_IN_SIZE (1024) +uint8_t usart_fifo_in_data[USART_FIFO_IN_SIZE]; +uint32_t usart_fifo_in_len = 0; +uint32_t usart_fifo_in_index = 0; + +static uint32_t usart = 0; + +static uint8_t usart_fifo_pop(void) +{ + uint8_t ret; + usart_fifo_out_len--; + ret = usart_fifo_out_data[usart_fifo_out_index]; + usart_fifo_out_index++; + if (usart_fifo_out_index == USART_FIFO_OUT_SIZE ) { + usart_fifo_out_index = 0; + } + return ret; +} + +static void usart_fifo_push(uint8_t aData) +{ + uint32_t i; + if( (usart_fifo_out_len + 1) == USART_FIFO_OUT_SIZE)//overflow + { + usart_fifo_out_len = 0; + LOG_PRINTF("OVERFLOW!"); + return; + } + + i = usart_fifo_out_index + usart_fifo_out_len; + if (i >= USART_FIFO_OUT_SIZE) { + i -= USART_FIFO_OUT_SIZE; + } + usart_fifo_out_data[i] = aData; + usart_fifo_out_len++; +} + + +static uint8_t usart_fifo_in_pop(void) +{ + uint8_t ret; + usart_fifo_in_len--; + ret = usart_fifo_in_data[usart_fifo_in_index]; + usart_fifo_in_index++; + if (usart_fifo_in_index == USART_FIFO_IN_SIZE ) { + usart_fifo_in_index = 0; + } + return ret; +} + +static void usart_fifo_in_push(uint8_t aData) +{ + uint32_t i; + if( (usart_fifo_in_len + 1) == USART_FIFO_IN_SIZE)//overflow + { + usart_fifo_in_len = 0; + return; + } + + i = usart_fifo_in_index + usart_fifo_in_len; + if (i >= USART_FIFO_IN_SIZE) { + i -= USART_FIFO_IN_SIZE; + } + usart_fifo_in_data[i] = aData; + usart_fifo_in_len++; +} + + +static void usart_write(const char * data, uint32_t len) +{ + uint32_t i; + for(i = 0; i < len; i++) + { + usart_fifo_push(data[i]); + } +} +void usart_printf(const char *str, ...) +{ + va_list va; + va_start(va, str); + usart_vprintf(str, va); + va_end(va); + +} + +void usart_vprintf(const char *str, va_list va) +{ + char databuffer[128]; + int i = vsnprintf(databuffer, 128, str, va); + if (i > 0) { + usart_write(databuffer, i); + } +} + + + +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_rx_interrupt(arg_usart); + usart_enable(arg_usart); + usart = arg_usart; +} +void usart_interrupt(void) +{ + if (usart_get_interrupt_source(usart, USART_SR_RXNE)) { + uint8_t data = usart_recv(usart); + usart_fifo_in_push(data); + if ( data != 3 && data != '\r' && data != '\n') { + usart_fifo_push(data); + } else { + LOG_PRINTF("\r\n>>"); + } + } +} + +void usart_fifo_send(void) +{ + while(usart_fifo_out_len) { + uint8_t data = usart_fifo_pop(); + usart_wait_send_ready(usart); + usart_send(usart, data); + } +} +static char command[128]; +static uint8_t command_len = 0; +static uint8_t command_argindex = 0; + +static uint8_t usart_read_command(void) +{ + uint32_t fifo_len = usart_fifo_in_len; + while (fifo_len) { + uint8_t data = usart_fifo_in_pop(); + + if ((data >= 'A') && (data <= 'Z')) { + data += 'a'-'A'; + } + + if (((data >= 'a') && (data <= 'z')) || ((data >='0') && (data<='9'))) { + command[command_len++] = data; + } else if (data == ' ') { + if (command_len) { + if (command_argindex == 0) { + command[command_len++] = 0; + command_argindex = command_len; + } else { + command[command_len++] = ' '; + } + } + } else if (data == '\r' || data == '\n') { + if (command_len) { + command[command_len++] = 0; + if (!command_argindex) { + command_argindex = command_len; + } + return 1; + } + } else if (data == 127) { + if (command_len) { + if (command_argindex) { + if (command_len == command_argindex) { + command_argindex = 0; + } + } + command[command_len] = '\0'; + command_len--; + } + } else if (data == 3) { + command_len = 0; + command_argindex = 0; + } else { + LOG_PRINTF("%d ",data); + } + + fifo_len--; + } + return 0; +} +void usart_call_cmd(struct usart_commands * commands) +{ + uint32_t i = 0; + if(!usart_read_command()) { + return; + } + if (!command_len) { + LOG_PRINTF("#2"); + return; + } + //~ for (i = 0; i < command_len; i++) { + //~ LOG_PRINTF("%c", command[i]); + //~ } + i=0; + while(commands[i].cmd != NULL) { + if (!strcmp((char*)command, (char*)commands[i].cmd)) { + if (commands[i].callback) { + if(command_argindex == command_len) { + commands[i].callback(NULL); + } else { + commands[i].callback(&command[command_argindex]); + } + } + usart_write("\r\n>>",4); + command_len = 0; + command_argindex = 0; + return; + } else { + + } + i++; + } + command_len = 0; + command_argindex = 0; + LOG_PRINTF("INVALID COMMAND\r\n>>"); +} + +#endif diff --git a/src/usart_helpers.h b/src/usart_helpers.h new file mode 100644 index 0000000..2a2f561 --- /dev/null +++ b/src/usart_helpers.h @@ -0,0 +1,56 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#ifndef USBH_USART_HELPERS_H +#define USBH_USART_HELPERS_H + +#include "usbh_hubbed.h" +#include +#include + +BEGIN_DECLS + +struct usart_commands{ + const char * cmd; + void (*callback)(const char * arg); +}; + + +void usart_init(uint32_t usart, uint32_t baudrate); +void usart_printf(const char *str, ...); +void usart_vprintf(const char *str, va_list va); +void usart_fifo_send(void); + +void usart_call_cmd(struct usart_commands * commands); +void usart_interrupt(void); + +#ifdef USART_DEBUG +#define LOG_PRINTF(format, ...) usart_printf(format, ##__VA_ARGS__); +#define LOG_FLUSH() usart_fifo_send() +#else +#define LOG_PRINTF(dummy, ...) ((void)dummy) +#define LOG_FLUSH() +#endif + +END_DECLS + +#endif diff --git a/src/usbh_driver_gp_xbox.c b/src/usbh_driver_gp_xbox.c new file mode 100644 index 0000000..52955d9 --- /dev/null +++ b/src/usbh_driver_gp_xbox.c @@ -0,0 +1,420 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + + +#include "usart_helpers.h" +#include "usbh_driver_gp_xbox.h" +#include "driver/usbh_device_driver.h" + +#include +#include + +static void *gp_xbox_init(void *usbh_dev); +static bool gp_xbox_analyze_descriptor(void *drvdata, void *descriptor); +static void gp_xbox_poll(void *drvdata, uint32_t tflp); +static void gp_xbox_remove(void *drvdata); + +static const usbh_dev_driver_info_t usbh_gp_xbox_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 = gp_xbox_init, + .analyze_descriptor = gp_xbox_analyze_descriptor, + .poll = gp_xbox_poll, + .remove = gp_xbox_remove, + .info = &usbh_gp_xbox_driver_info +}; + +enum STATES { + STATE_INACTIVE, + STATE_READING_COMPLETE, + STATE_READING_REQUEST, + STATE_SET_CONFIGURATION_REQUEST, + STATE_SET_CONFIGURATION_EMPTY_READ, + STATE_SET_CONFIGURATION_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 *gp_xbox_init(void *usbh_dev) +{ + if (!initialized) { + LOG_PRINTF("driver not initialized"); + return false; + } + + 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 gp_xbox_analyze_descriptor(void *drvdata, void *descriptor) +{ + gp_xbox_device_t *gp_xbox = 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_SET_CONFIGURATION_REQUEST; + 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 = 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 = 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; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + gp_xbox->state_next = STATE_INACTIVE; + break; + } + } + break; + + case STATE_SET_CONFIGURATION_EMPTY_READ: + { + LOG_PRINTF("|empty packet read|"); + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + gp_xbox->state_next = STATE_SET_CONFIGURATION_COMPLETE; + device_xfer_control_read(0, 0, event, dev); + break; + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + gp_xbox->state_next = STATE_INACTIVE; + break; + } + } + break; + case STATE_SET_CONFIGURATION_COMPLETE: // Configured + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + gp_xbox->state_next = STATE_READING_REQUEST; + gp_xbox->endpoint_in_toggle = 0; + LOG_PRINTF("\r\ngp_xbox CONFIGURED\r\n"); + if (gp_xbox_config->notify_connected) { + gp_xbox_config->notify_connected(gp_xbox->device_id); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + 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\r\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 = &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_EPTYP_BULK; + 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 | \r\n"); +} + +/** + * + * tflp time from last poll [us] + */ +static void gp_xbox_poll(void *drvdata, uint32_t tflp) +{ + (void)tflp; + gp_xbox_device_t *gp_xbox = drvdata; + usbh_device_t *dev = gp_xbox->usbh_device; + + switch (gp_xbox->state_next) { + case STATE_READING_REQUEST: + { + read_gp_xbox_in(gp_xbox); + } + break; + + case STATE_SET_CONFIGURATION_REQUEST: + { + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00000000; + setup_data.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.wValue = gp_xbox->configuration_value; + setup_data.wIndex = 0; + setup_data.wLength = 0; + + gp_xbox->state_next = STATE_SET_CONFIGURATION_EMPTY_READ; + + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + break; + + default: + { + // do nothing - probably transfer is in progress + } + break; + } +} + +static void gp_xbox_remove(void *drvdata) +{ + LOG_PRINTF("Removing xbox\r\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; +} diff --git a/src/usbh_driver_hid_mouse.c b/src/usbh_driver_hid_mouse.c new file mode 100644 index 0000000..5c01451 --- /dev/null +++ b/src/usbh_driver_hid_mouse.c @@ -0,0 +1,294 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#include "usbh_hubbed.h" +#include "driver/usbh_device_driver.h" +#include "usbh_driver_hid_mouse.h" +#include "usart_helpers.h" + +#include + +static void *mouse_init(void *usbh_dev); +static bool mouse_analyze_descriptor(void *drvdata, void *descriptor); +static void mouse_poll(void *drvdata, uint32_t tflp); +static void mouse_remove(void *drvdata); + +static const usbh_dev_driver_info_t usbh_hid_mouse_driver_info = { + .deviceClass = -1, + .deviceSubClass = -1, + .deviceProtocol = -1, + .idVendor = -1, + .idProduct = -1, + .ifaceClass = 0x03, + .ifaceSubClass = -1, + .ifaceProtocol = 0x02 +}; + +const usbh_dev_driver_t usbh_hid_mouse_driver = { + .init = mouse_init, + .analyze_descriptor = mouse_analyze_descriptor, + .poll = mouse_poll, + .remove = mouse_remove, + .info = &usbh_hid_mouse_driver_info +}; + +enum STATES { + STATE_INACTIVE, + STATE_READING_COMPLETE, + STATE_READING_REQUEST, + STATE_SET_CONFIGURATION_REQUEST, + STATE_SET_CONFIGURATION_EMPTY_READ, + STATE_SET_CONFIGURATION_COMPLETE +}; + +struct _hid_mouse_device { + usbh_device_t *usbh_device; + uint8_t buffer[USBH_HID_MOUSE_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 _hid_mouse_device hid_mouse_device_t; + +static hid_mouse_device_t mouse_device[USBH_HID_MOUSE_MAX_DEVICES]; +static const hid_mouse_config_t *mouse_config; + + + + +#include + + + +void hid_mouse_driver_init(const hid_mouse_config_t *config) +{ + uint32_t i; + mouse_config = config; + for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) { + mouse_device[i].state_next = STATE_INACTIVE; + } +} + +/** + * + * + */ +static void *mouse_init(void *usbh_dev) +{ + uint32_t i; + hid_mouse_device_t *drvdata = 0; + + // find free data space for mouse device + for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) { + if (mouse_device[i].state_next == STATE_INACTIVE) { + drvdata = &mouse_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 mouse_analyze_descriptor(void *drvdata, void *descriptor) +{ + hid_mouse_device_t *mouse = 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; + mouse->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)) { + mouse->endpoint_in_address = epaddr&0x7f; + if (ep->wMaxPacketSize < USBH_HID_MOUSE_BUFFER) { + mouse->endpoint_in_maxpacketsize = ep->wMaxPacketSize; + } else { + mouse->endpoint_in_maxpacketsize = USBH_HID_MOUSE_BUFFER; + } + } + + if (mouse->endpoint_in_address) { + mouse->state_next = STATE_SET_CONFIGURATION_REQUEST; + return true; + } + } + } + break; + // TODO Class Specific descriptors + default: + break; + } + return false; +} + +static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data) +{ + hid_mouse_device_t *mouse = dev->drvdata; + switch (mouse->state_next) { + case STATE_READING_COMPLETE: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + mouse->state_next = STATE_READING_REQUEST; + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + mouse->state_next = STATE_INACTIVE; + break; + } + } + break; + + case STATE_SET_CONFIGURATION_EMPTY_READ: + { + LOG_PRINTF("|empty packet read|"); + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + mouse->state_next++; + device_xfer_control_read(0, 0, event, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + mouse->state_next = STATE_INACTIVE; + break; + } + } + break; + case STATE_SET_CONFIGURATION_COMPLETE: // Configured + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + mouse->state_next = STATE_READING_REQUEST; + mouse->endpoint_in_toggle = 0; + LOG_PRINTF("\r\nMOUSE CONFIGURED\r\n"); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + mouse->state_next = STATE_INACTIVE; + break; + } + } + break; + default: + break; + } +} + + +static void read_mouse_in(void *drvdata) +{ + hid_mouse_device_t *mouse = drvdata; + usbh_packet_t packet; + + packet.address = mouse->usbh_device->address; + packet.data = &mouse->buffer[0]; + packet.datalen = mouse->endpoint_in_maxpacketsize; + packet.endpoint_address = mouse->endpoint_in_address; + packet.endpoint_size_max = mouse->endpoint_in_maxpacketsize; + packet.endpoint_type = USBH_EPTYP_BULK; + packet.speed = mouse->usbh_device->speed; + packet.callback = event; + packet.callback_arg = mouse->usbh_device; + packet.toggle = &mouse->endpoint_in_toggle; + + mouse->state_next = STATE_READING_COMPLETE; + usbh_read(mouse->usbh_device, &packet); + + // LOG_PRINTF("@MOUSE EP1 | \r\n"); + +} + +/** + * + * tflp time from last poll [us] + */ +static void mouse_poll(void *drvdata, uint32_t tflp) +{ + (void)drvdata; + (void)tflp; + hid_mouse_device_t *mouse = drvdata; + usbh_device_t *dev = mouse->usbh_device; + switch (mouse->state_next) { + case STATE_READING_REQUEST: + { + read_mouse_in(drvdata); + } + break; + + case STATE_SET_CONFIGURATION_REQUEST: + { + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00000000; + setup_data.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.wValue = mouse->configuration_value; + setup_data.wIndex = 0; + setup_data.wLength = 0; + + mouse->state_next++; + + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + break; + + default: + // do nothing - probably transfer is in progress + break; + } +} + +static void mouse_remove(void *drvdata) +{ + hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata; + mouse->state_next = STATE_INACTIVE; + mouse->endpoint_in_address = 0; +} diff --git a/src/usbh_driver_hub.c b/src/usbh_driver_hub.c new file mode 100644 index 0000000..c82eacd --- /dev/null +++ b/src/usbh_driver_hub.c @@ -0,0 +1,865 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#include "usbh_driver_hub_private.h" +#include "driver/usbh_device_driver.h" +#include "usart_helpers.h" +#include "usbh_config.h" + +#include + + +static hub_device_t hub_device[USBH_MAX_HUBS]; + +static void *hub_init(void *usbh_dev); +static bool hub_analyze_descriptor(void *drvdata, void *descriptor); +static void hub_poll(void *drvdata, uint32_t tflp); +static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data); +static void hub_remove(void *drvdata); + +static const usbh_dev_driver_info_t usbh_hub_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 = hub_init, + .analyze_descriptor = hub_analyze_descriptor, + .poll = hub_poll, + .remove = hub_remove, + .info = &usbh_hub_driver_info +}; + + + +void hub_driver_init(void) +{ + uint32_t i; + + 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 = -1; + } +} + + +/** + * + * + */ +static void *hub_init(void *usbh_dev) +{ + uint32_t i; + hub_device_t *drvdata = 0; + // 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); + LOG_FLUSH(); + if (i == USBH_MAX_HUBS) { + LOG_PRINTF("ERRRRRRR"); + return 0; + } + + drvdata = &hub_device[i]; + drvdata->state = 0; + drvdata->ports_num = 0; + drvdata->device[0] = usbh_dev; + drvdata->busy = 0; + drvdata->endpoint_in_address = 0; + drvdata->endpoint_in_maxpacketsize = 0; + +// for (i = 1; i < USBH_MAX_HUBS + 1; i++) { +// drvdata->device[i]->address = 0; +// drvdata->device[i]->state = 0; +// } + return drvdata; +} + +/** + * Returns true if all needed data are parsed + */ +static bool hub_analyze_descriptor(void *drvdata, void *descriptor) +{ + hub_device_t *hub = 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; + hub->buffer[0] = cfg->bConfigurationValue; + } + 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)) { + hub->endpoint_in_address = epaddr&0x7f; + hub->endpoint_in_maxpacketsize = ep->wMaxPacketSize; + } + } + LOG_PRINTF("ENDPOINT DESCRIPTOR FOUND\r\n"); + } + break; + + case USB_DT_HUB: + { + struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)descriptor; + //~ hub->ports_num = desc->head.bNbrPorts; + if ( desc->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) { + hub->ports_num = desc->head.bNbrPorts; + } else { + LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\r\n"); + hub->ports_num = USBH_HUB_MAX_DEVICES; + } + LOG_PRINTF("HUB DESCRIPTOR FOUND \r\n"); + } + break; + + default: + LOG_PRINTF("TYPE: %02X \r\n",desc_type); + break; + } + + if (hub->endpoint_in_address) { + hub->state = 1; + LOG_PRINTF("end enum"); + return true; + } + return false; +} + +// Enumerate +static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data) +{ + //~ usbh_device_t *dev = arg; + hub_device_t *hub = dev->drvdata; + + LOG_PRINTF("\r\nHUB->STATE = %d\r\n", hub->state); + switch (hub->state) { + case 26: + 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\r\n",psc); + // Driver error... port not found + if (!psc) { + // Continue reading status change endpoint + hub->state = 25; + break; + } + + for (i = 0; i <= hub->ports_num; i++) { + if (psc & (1<current_port >= 1) { + if (hub->current_port != port) { + LOG_PRINTF("X"); + hub->state = 25; + break; + } + } + struct usb_setup_data setup_data; + // If regular port event, else hub event + if (port) { + setup_data.bmRequestType = 0b10100011; + } else { + setup_data.bmRequestType = 0b10100000; + } + + + //~ LOG_PRINTF("port:%d", port); + setup_data.bRequest = USB_REQ_GET_STATUS; + setup_data.wValue = 0; + setup_data.wIndex = port; + setup_data.wLength = 4; + hub->state = 31; + + hub->current_port = port; + LOG_PRINTF("\r\n\r\nPORT FOUND: %d\r\n", port); + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + hub->state = 0; + break; + + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + + // In case of EAGAIN error, retry read on status endpoint + hub->state = 25; + LOG_PRINTF("HUB: Retrying...\r\n"); + break; + } + break; + + case 2: + { + LOG_PRINTF("|empty packet read|"); + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + device_xfer_control_read(0, 0, event, dev); + hub->state++; + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + + case 3: // Get HUB Descriptor write + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + if (hub->ports_num) { + hub->index = 0; + hub->state = 6; + LOG_PRINTF("No need to get HUB DESC\r\n"); + event(dev, cb_data); + } else { + hub->endpoint_in_toggle = 0; + + struct usb_setup_data setup_data; + hub->desc_len = hub->device[0]->packet_size_max0; + + setup_data.bmRequestType = 0b10100000; + setup_data.bRequest = USB_REQ_GET_DESCRIPTOR; + setup_data.wValue = 0x29<<8; + setup_data.wIndex = 0; + setup_data.wLength = hub->desc_len; + + hub->state++; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + LOG_PRINTF("DO Need to get HUB DESC\r\n"); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + + case 4: // Get HUB Descriptor read + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + hub->state++; + device_xfer_control_read(hub->buffer, hub->desc_len, event, dev); // "error dynamic size" - bad comment, investigate + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + + case 5:// 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 = 0b10100000; + setup_data.bRequest = USB_REQ_GET_DESCRIPTOR; + setup_data.wValue = 0x29<<8; + setup_data.wIndex = 0; + setup_data.wLength = hub->desc_len; + + hub->state = 4; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + break; + } else if (hub_descriptor->head.bDescLength == hub->desc_len) { + hub->ports_num = hub_descriptor->head.bNbrPorts; + + hub->state++; + 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\r\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\r\n"); + hub->ports_num = USBH_HUB_MAX_DEVICES; + } + hub->state++; + hub->index = 0; + + cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK; + event(dev, cb_data); + } + } + } + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + break; + } + } + break; + + case 6:// 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 = 0b00100011; + setup_data.bRequest = HUB_REQ_SET_FEATURE; + setup_data.wValue = HUB_FEATURE_PORT_POWER; + setup_data.wIndex = hub->index; + setup_data.wLength = 0; + + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } else { + hub->state++; + // TODO: + // Delay Based on hub descriptor field bPwr2PwrGood + // delay_ms_busy_loop(200); + + LOG_PRINTF("\r\nHUB CONFIGURED & PORTS POWERED\r\n"); + + cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK; + event(dev, cb_data); + } + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + + case 7: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + { + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b10100000; + setup_data.bRequest = USB_REQ_GET_STATUS; + setup_data.wValue = 0; + setup_data.wIndex = 0; + setup_data.wLength = 4; + + hub->state++; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + + } + break; + case 8: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + device_xfer_control_read(hub->buffer, 4, event, dev); + hub->index = 0; + hub->state++; + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + case 9: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + { + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b10100011; + setup_data.bRequest = USB_REQ_GET_STATUS; + setup_data.wValue = 0; + setup_data.wIndex = ++hub->index; + setup_data.wLength = 4; + + hub->state++; + + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + case 10: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + device_xfer_control_read(hub->buffer, 4, event, dev); + hub->state++; + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + case 11: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + if (hub->index < hub->ports_num) { + hub->state = 9; + // process data contained in hub->buffer + // TODO: + cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK; + event(dev, cb_data); + } else { + hub->busy = 0; + hub->state = 25; + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + break; + } + } + break; + + case 31: // Read port status + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + { + int8_t port = hub->current_port; + hub->state++; + + // TODO: rework to endianess aware, + // (maybe whole library is affected by this) + // Detail: + // Isn't universal. Here is endianess ok, + // but on another architecture must not be + device_xfer_control_read(&hub->hub_and_port_status[port], 4, event, dev); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + // continue + hub->state = 25; + break; + } + + } + break; + case 32: + { + 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<device[port]) { + if (!usbh_enum_available() || hub->busy) { + LOG_PRINTF("\r\n\t\t\tCannot enumerate %d %d\r\n", !usbh_enum_available(), hub->busy); + hub->state = 25; + break; + } + } + + // clear feature C_PORT_CONNECTION + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00100011; + 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 = 33; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + + } else if(stc & (1<state = 35; + + LOG_PRINTF("RESET"); + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } else { + LOG_PRINTF("another STC %d\r\n", stc); + } + } else { + hub->state = 25; + LOG_PRINTF("HUB status change\r\n"); + } + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + // continue + hub->state = 25; + break; + } + } + break; + case 33: + { + 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<state = 11; + LOG_PRINTF("CONN"); + + hub->busy = 1; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + } else { + LOG_PRINTF("\t\t\t\tDISCONNECT EVENT\r\n"); + if (hub->device[port]->drv && hub->device[port]->drvdata) { + hub->device[port]->drv->remove(hub->device[port]->drvdata); + } + hub->device[port]->address = -1; + + hub->device[port]->drv = 0; + hub->device[port]->drvdata = 0; + hub->device[port] = 0; + hub->current_port = CURRENT_PORT_NONE; + hub->state = 25; + hub->busy = 0; + } + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + // continue + hub->state = 25; + break; + } + } + break; + case 35: // RESET COMPLETE, start enumeration + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + { + LOG_PRINTF("\r\nPOLL\r\n"); + int8_t port = hub->current_port; + uint16_t sts = hub->hub_and_port_status[port].sts; + + + if (sts & (1<device[port] = usbh_get_free_device(dev); + + if (!hub->device[port]) { + LOG_PRINTF("\r\nFATAL ERROR\r\n"); + return;// DEAD END + } + if ((sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) && + !(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) { + LOG_PRINTF("Low speed device"); + + // Disable Low speed device immediately + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00100011; + 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 + hub->state = 11;//Expecting all ports are powered (constant/non-changeable after init) + hub->current_port = CURRENT_PORT_NONE; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } 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 = 100; // schedule wait for 500ms + } + + + } else { + LOG_PRINTF("%s:%d Do not know what to do, when device is disabled after reset\r\n", __FILE__, __LINE__); + hub->state = 25; + return; + } + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + ERROR(cb_data.status); + // continue + hub->state = 25; + break; + } + } + break; + default: + LOG_PRINTF("UNHANDLED EVENT %d\r\n",hub->state); + break; + } +} + +static void read_ep1(void *drvdata) +{ + hub_device_t *hub = drvdata; + usbh_packet_t packet; + + packet.address = hub->device[0]->address; + packet.data = 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_EPTYP_INTERRUPT; + packet.speed = hub->device[0]->speed; + packet.callback = event; + packet.callback_arg = hub->device[0]; + packet.toggle = &hub->endpoint_in_toggle; + + hub->state = 26; + usbh_read(hub->device[0], &packet); + LOG_PRINTF("@hub %d/EP1 | \r\n", hub->device[0]->address); + +} + +/** + * + * tflp time from last poll [ms] + */ +static void hub_poll(void *drvdata, uint32_t time_curr_us) +{ + hub_device_t *hub = drvdata; + usbh_device_t *dev = hub->device[0]; + + hub->time_curr_us = time_curr_us; + + switch (hub->state) { + case 25: + { + if (usbh_enum_available()) { + read_ep1(hub); + } else { + LOG_PRINTF("enum not available\r\n"); + } + } + break; + + case 1: + { + LOG_PRINTF("CFGVAL: %d\r\n", hub->buffer[0]); + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00000000; + setup_data.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.wValue = hub->buffer[0]; + setup_data.wIndex = 0; + setup_data.wLength = 0; + + hub->state += 2; + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + + } + break; + case 100: + if (hub->time_curr_us - hub->timestamp_us > 500000) { + int8_t port = hub->current_port; + LOG_PRINTF("PORT: %d", port); + LOG_PRINTF("\r\nNEW device at address: %d\r\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 = 25; + } + 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 hub_remove(void *drvdata) +{ + hub_device_t *hub = drvdata; + uint8_t i; + + // Call fast... to avoid polling + hub->state = 0; + hub->endpoint_in_address = 0; + hub->busy = 0; + for (i = 1; i < USBH_HUB_MAX_DEVICES + 1; i++) { + if (hub->device[i]) { + if (hub->device[i]->drv && hub->device[i]->drvdata) { + if (hub->device[i]->drv->remove != hub_remove) { + LOG_PRINTF("\t\t\t\tHUB REMOVE %d\r\n",hub->device[i]->address); + hub->device[i]->drv->remove(hub->device[i]->drvdata); + } + } + hub->device[i] = 0; + } + hub->device[0]->drv = 0; + hub->device[0]->drvdata = 0; + hub->device[0] = 0; + + } +} diff --git a/src/usbh_driver_hub_private.h b/src/usbh_driver_hub_private.h new file mode 100644 index 0000000..4b98880 --- /dev/null +++ b/src/usbh_driver_hub_private.h @@ -0,0 +1,108 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#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 +#include +#include + + +// # 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 + +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; + uint8_t 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 diff --git a/src/usbh_hubbed.c b/src/usbh_hubbed.c new file mode 100644 index 0000000..e935f99 --- /dev/null +++ b/src/usbh_hubbed.c @@ -0,0 +1,634 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#include "usbh_config.h" +#include "usbh_lld_stm32f4.h" +#include "driver/usbh_device_driver.h" +#include "usart_helpers.h" + +#include +#include + +static struct { + bool enumeration_run; + const usbh_driver_t * const *lld_drivers; + const usbh_dev_driver_t * const *dev_drivers; + int8_t address_temporary; +} usbh_data = {0}; + +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; +} + +/** + * + */ +static const usbh_dev_driver_t *find_driver(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); + + return usbh_data.dev_drivers[i]; + } + return 0; +#undef CHECK_PARTIAL_COMPATIBILITY +} + + +static void device_register(void *descriptors, uint16_t descriptors_len, usbh_device_t *dev) +{ + uint32_t i = 0; + dev->drv = 0; + uint8_t *buf = (uint8_t *)descriptors; + + dev->drv = 0; + dev->drvdata = 0; + + 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"); + 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_DEVICE: + { + struct usb_device_descriptor *device_desc = (void*)&buf[i]; + LOG_PRINTF("DEVICE DESCRIPTOR"); + device_info.deviceClass = device_desc->bDeviceClass; + device_info.deviceSubClass = device_desc->bDeviceSubClass; + } + break; + + case USB_DT_INTERFACE: + { + LOG_PRINTF("INTERFACE_DESCRIPTOR\r\n"); + struct usb_interface_descriptor *iface = (void*)&buf[i]; + device_info.ifaceClass = iface->bInterfaceClass; + device_info.ifaceSubClass = iface->bInterfaceSubClass; + device_info.ifaceProtocol = iface->bInterfaceProtocol; + const usbh_dev_driver_t *driver = find_driver(&device_info); + if (driver) { + dev->drv = driver; + dev->drvdata = dev->drv->init(dev); + if (!dev->drvdata) { + LOG_PRINTF("CANT TOUCH THIS"); + } + break; + } + } + break; + default: + break; + } + + if (desc_len == 0) { + LOG_PRINTF("PROBLEM WITH PARSE %d\r\n",i); + return; + } + i += desc_len; + + } + + if (dev->drv && dev->drvdata) { + // analyze descriptors + LOG_PRINTF("ANALYZE"); + i = 0; + while (i < descriptors_len) { + desc_len = buf[i]; + void *drvdata = dev->drvdata; + LOG_PRINTF("[%d]",buf[i+1]); + if (dev->drv->analyze_descriptor(drvdata, &buf[i])) { + LOG_PRINTF("Device Initialized\r\n"); + return; + } + i += desc_len; + } + } + LOG_PRINTF("Device NOT Initialized\r\n"); +} + +void usbh_init(const void *drivers_lld[], const usbh_dev_driver_t * const device_drivers[]) +{ + if (!drivers_lld) { + return; + } + + usbh_data.lld_drivers = (const usbh_driver_t **)drivers_lld; + usbh_data.dev_drivers = device_drivers; + + // TODO: init structures + uint32_t k = 0; + while (usbh_data.lld_drivers[k]) { + LOG_PRINTF("DRIVER %d\r\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; + } + LOG_PRINTF("DRIVER %d", k); + usbh_data.lld_drivers[k]->init(usbh_data.lld_drivers[k]->driver_data); + + k++; + } + +} + +/* + * NEW ENUMERATE + * + */ +void device_xfer_control_write(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev) +{ + usbh_packet_t packet; + + packet.data = data; + packet.datalen = datalen; + packet.address = dev->address; + packet.endpoint_address = 0; + packet.endpoint_size_max = dev->packet_size_max0; + packet.endpoint_type = USBH_EPTYP_CONTROL; + packet.speed = dev->speed; + packet.callback = callback; + packet.callback_arg = dev; + packet.toggle = &dev->toggle0; + + usbh_write(dev, &packet); + LOG_PRINTF("WR@device...%d | \r\n", dev->address); +} + +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 = data; + packet.datalen = datalen; + packet.address = dev->address; + packet.endpoint_address = 0; + packet.endpoint_size_max = dev->packet_size_max0; + packet.endpoint_type = USBH_EPTYP_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 | \r\n", dev->address); +} + + + +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_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\r\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\r\n\r\n\r\n", usbh_device[i].address); + } + } + + return 0; +} + +static void device_enumeration_terminate(usbh_device_t *dev) +{ + reset_enumeration(); + dev->state = 0; + dev->address = -1; +} + +/* Do not call this function directly, + * only via callback passing into low-level function + * If you must, call it carefully ;) + */ +static void device_enumerate(usbh_device_t *dev, usbh_packet_callback_data_t cb_data) +{ + const usbh_driver_t *lld = dev->lld; + usbh_generic_data_t *lld_data = lld->driver_data; + uint8_t *usbh_buffer = lld_data->usbh_buffer; + uint8_t state_start = dev->state; // Detection of hang +// LOG_PRINTF("\r\nSTATE: %d\r\n", state); + switch (dev->state) { + case 1: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + dev->state++; + LOG_PRINTF("::%d::", dev->address); + device_xfer_control_read(0, 0, device_enumerate, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + } + break; + case 2: + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + if (dev->address == 0) { + dev->address = usbh_data.address_temporary; + LOG_PRINTF("ADDR: %d\r\n", dev->address); + } + + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b10000000; + 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++; + device_xfer_control_write(&setup_data, sizeof(setup_data), + device_enumerate, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + break; + + case 3: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + dev->state++; + device_xfer_control_read(&usbh_buffer[0], USB_DT_DEVICE_SIZE, + device_enumerate, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + dev->state = 2; + + // WARNING: Recursion + // .. but should work + cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK; + device_enumerate(dev, cb_data); + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + } + break; + + case 4: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + { + struct usb_device_descriptor *ddt = + (struct usb_device_descriptor *)&usbh_buffer[0]; + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b10000000; + setup_data.bRequest = USB_REQ_GET_DESCRIPTOR; + setup_data.wValue = USB_DT_CONFIGURATION << 8; + setup_data.wIndex = 0; + setup_data.wLength = ddt->bMaxPacketSize0;//USB_DT_CONFIGURATION_SIZE; + + dev->state++; + device_xfer_control_write(&setup_data, sizeof(setup_data), + device_enumerate, dev); + } + 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; + dev->state = 2; + + // WARNING: Recursion + // .. but should work + cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK; + device_enumerate(dev, cb_data); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + } + break; + case 5: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + dev->state++; + device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE], + dev->packet_size_max0, device_enumerate, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + } + break; + + case 6: + { + 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]; + struct usb_setup_data setup_data; + LOG_PRINTF("WRITE: LEN: %d", cdt->wTotalLength); + setup_data.bmRequestType = 0b10000000; + 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++; + device_xfer_control_write(&setup_data, sizeof(setup_data), + device_enumerate, dev); + } + 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) { + dev->state = 8; + + // WARNING: Recursion + // .. but should work + cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK; + device_enumerate(dev, cb_data); + } + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + } + break; + + case 7: + { + 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++; + device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE], + cdt->wTotalLength, device_enumerate, dev); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + } + break; + + case 8: + { + 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("TOTAL_LENGTH: %d\r\n", cdt->wTotalLength); + device_register(usbh_buffer, cdt->wTotalLength + USB_DT_DEVICE_SIZE, dev); + dev->state++; + + reset_enumeration(); + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + device_enumeration_terminate(dev); + ERROR(cb_data.status); + break; + } + + } + break; + } + + if (state_start == dev->state) { + LOG_PRINTF("\r\n !HANG %d\r\n", state_start); + } +} + +void device_enumeration_start(usbh_device_t *dev) +{ + set_enumeration(); + dev->state = 1; + + // 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("\r\n\r\n\r\n ENUMERATION OF DEVICE@%d STARTED \r\n\r\n", address); + + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00000000; + setup_data.bRequest = USB_REQ_SET_ADDRESS; + setup_data.wValue = address; + setup_data.wIndex = 0; + setup_data.wLength = 0; + + device_xfer_control_write(&setup_data, sizeof(setup_data), + device_enumerate, dev); +} + +/** + * Should be called with at least 1kHz frequency + * + */ +void usbh_poll(uint32_t t_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, t_us); + + switch (poll_status) { + case USBH_POLL_STATUS_DEVICE_CONNECTED: + // New device found + LOG_PRINTF("\r\nDEVICE FOUND\r\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; + + device_enumeration_start(&usbh_device[0]); + break; + + case USBH_POLL_STATUS_DEVICE_DISCONNECTED: + { + // Device disconnected + if (usbh_device[0].drv && usbh_device[0].drvdata) { + usbh_device[0].drv->remove(usbh_device[0].drvdata); + } + usbh_device[0].drv = 0; + usbh_device[0].drvdata = 0; + + uint32_t i; + for (i = 1; i < USBH_MAX_DEVICES; i++) { + usbh_device[i].address = -1; + usbh_device[i].drv = 0; + usbh_device[i].drvdata = 0; + } + } + break; + + default: + break; + } + + if (lld_data->usbh_device[0].drv && usbh_device[0].drvdata) { + usbh_device[0].drv->poll(usbh_device[0].drvdata, t_us); + } + + k++; + } +} + +void usbh_read(usbh_device_t *dev, usbh_packet_t *packet) +{ + const usbh_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_driver_t *lld = dev->lld; + lld->write(lld->driver_data, packet); +} + diff --git a/src/usbh_lld_stm32f4.c b/src/usbh_lld_stm32f4.c new file mode 100644 index 0000000..6cf6015 --- /dev/null +++ b/src/usbh_lld_stm32f4.c @@ -0,0 +1,1048 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2015 Amir Hammad + * + * + * 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 . + * + */ + +#include "usbh_lld_stm32f4.h" +#include "usart_helpers.h" +#include "driver/usbh_device_driver.h" + +#include +#include +#include +#include + + + +/* Receive FIFO size in 32-bit words. */ +#define RX_FIFO_SIZE (64) +/* Transmit NON-periodic FIFO size in 32-bit words. */ +#define TX_NP_FIFO_SIZE (64) +/* Transmit periodic FIFO size in 32-bit words. */ +#define TX_P_FIFO_SIZE (64) + + + + +enum CHANNEL_STATE { + CHANNEL_STATE_FREE = 0, + CHANNEL_STATE_WORK = 1 +}; + +struct _channel { + enum CHANNEL_STATE state; + usbh_packet_t packet; + uint32_t data_index; //used in receive function + uint8_t error_count; +}; +typedef struct _channel channel_t; + +enum DEVICE_STATE { + DEVICE_STATE_INIT = 0, + DEVICE_STATE_RUN = 1, + DEVICE_STATE_RESET = 2 +}; + +enum DEVICE_POLL_STATE { + DEVICE_POLL_STATE_DISCONN = 0, + DEVICE_POLL_STATE_DEVCONN = 1, + DEVICE_POLL_STATE_DEVRST = 2, + DEVICE_POLL_STATE_RUN = 3 +}; + +struct _usbh_lld_stm32f4_driver_data { + usbh_generic_data_t generic; + const uint32_t base; + channel_t *channels; + const uint8_t num_channels; + + uint32_t poll_sequence; + enum DEVICE_POLL_STATE dpstate; + enum DEVICE_STATE state; + uint32_t state_prev;//for reset only + uint32_t time_curr_us; + uint32_t timestamp_us; +}; +typedef struct _usbh_lld_stm32f4_driver_data usbh_lld_stm32f4_driver_data_t; + + + +/* + * Define correct REBASE. If only one driver is enabled use directly OTG base + * + */ +#if defined(USE_STM32F4_USBH_DRIVER_FS) || \ + defined(USE_STM32F4_USBH_DRIVER_HS) + +#if defined(USE_STM32F4_USBH_DRIVER_FS) && \ + defined(USE_STM32F4_USBH_DRIVER_HS) +#define REBASE(reg) MMIO32(dev->base + reg) +#define REBASE_CH(reg, x) MMIO32(dev->base + reg(x)) +#elif defined(USE_STM32F4_USBH_DRIVER_FS) +#define REBASE(reg) MMIO32(USB_OTG_FS_BASE + reg) +#define REBASE_CH(reg, x) MMIO32(USB_OTG_FS_BASE + reg(x)) +#elif defined(USE_STM32F4_USBH_DRIVER_HS) +#define REBASE(reg) MMIO32(USB_OTG_HS_BASE + reg) +#define REBASE_CH(reg, x) MMIO32(USB_OTG_HS_BASE + reg(x)) +#endif + + +static void stm32f4_usbh_init(void *drvdata); +static enum USBH_POLL_STATUS stm32f4_usbh_poll(void *drvdata, uint32_t time_curr_us); +static void stm32f4_usbh_read(void *drvdata, usbh_packet_t *packet); +static void stm32f4_usbh_write(void *drvdata, const usbh_packet_t *packet); +uint8_t stm32f4_root_speed(void *drvdata); + +static int8_t get_free_channel(void *drvdata); +static void channels_init(void *drvdata); +static void rxflvl_handle(void *drvdata); +static void free_channel(void *drvdata, uint8_t channel); + +// USB Full Speed - OTG_FS +#if defined(USE_STM32F4_USBH_DRIVER_FS) +#define NUM_CHANNELS_FS (8) +static channel_t channels_fs[NUM_CHANNELS_FS]; +static usbh_lld_stm32f4_driver_data_t driver_data_fs = { + .base = USB_OTG_FS_BASE, + .channels = channels_fs, + .num_channels = NUM_CHANNELS_FS +}; +const usbh_driver_t stm32f4_usbh_driver_fs = { + .init = stm32f4_usbh_init, + .poll = stm32f4_usbh_poll, + .read = stm32f4_usbh_read, + .write = stm32f4_usbh_write, + .root_speed = stm32f4_root_speed, + .driver_data = &driver_data_fs +}; +#endif + +// USB High Speed - OTG_HS +#if defined(USE_STM32F4_USBH_DRIVER_HS) +#define NUM_CHANNELS_HS (12) +static channel_t channels_hs[NUM_CHANNELS_HS]; +static usbh_lld_stm32f4_driver_data_t driver_data_hs = { + .base = USB_OTG_HS_BASE, + .channels = channels_hs, + .num_channels = NUM_CHANNELS_HS +}; +const usbh_driver_t stm32f4_usbh_driver_hs = { + .init = stm32f4_usbh_init, + .poll = stm32f4_usbh_poll, + .read = stm32f4_usbh_read, + .write = stm32f4_usbh_write, + .root_speed = stm32f4_root_speed, + .driver_data = &driver_data_hs +}; +#endif + + + +static inline void reset_start(usbh_lld_stm32f4_driver_data_t *dev) +{ + + // apply reset condition on port + REBASE(OTG_HPRT) |= OTG_HPRT_PRST; + + // push current state to stack + dev->state_prev = dev->state; + + // move to new state + dev->state = DEVICE_STATE_RESET; + + // schedule disable reset condition after ~10ms + dev->timestamp_us = dev->time_curr_us; +} + +/** + * Should be nonblocking + * + */ +static void stm32f4_usbh_init(void *drvdata) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + dev->state = DEVICE_STATE_INIT; + dev->poll_sequence = 0; + dev->timestamp_us = dev->time_curr_us; + + //Disable interrupts first + REBASE(OTG_GAHBCFG) &= ~OTG_GAHBCFG_GINT; + + // Select full speed phy + REBASE(OTG_GUSBCFG) |= OTG_GUSBCFG_PHYSEL; +} + +static void stm32f4_usbh_port_channel_setup( + void *drvdata, uint32_t channel, uint32_t address, + uint32_t eptyp, uint32_t epnum, uint32_t epdir, + uint32_t max_packet_size) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + channel_t *channels = dev->channels; + + // TODO: maybe to function + switch (eptyp) { + case USBH_EPTYP_CONTROL: + eptyp = OTG_HCCHAR_EPTYP_CONTROL; + break; + case USBH_EPTYP_BULK: + eptyp = OTG_HCCHAR_EPTYP_BULK; + break; + case USBH_EPTYP_INTERRUPT: + eptyp = OTG_HCCHAR_EPTYP_INTERRUPT; + break; + case USBH_EPTYP_ISOCHRONOUS: + eptyp = OTG_HCCHAR_EPTYP_ISOCHRONOUS; + break; + } + + uint32_t speed = 0; + if (channels[channel].packet.speed == USBH_SPEED_LOW) { + speed = OTG_HCCHAR_LSDEV; + } + + REBASE_CH(OTG_HCCHAR, channel) = OTG_HCCHAR_CHENA | + (OTG_HCCHAR_DAD_MASK & (address << 22)) | + OTG_HCCHAR_MCNT_1 | + (OTG_HCCHAR_EPTYP_MASK & (eptyp << 18)) | + (speed) | + (epdir) | + (OTG_HCCHAR_EPNUM_MASK & (epnum << 11) )| + (OTG_HCCHAR_MPSIZ_MASK & max_packet_size); + +} + + +/** + * TODO: Check for maximum datalength + */ +static void stm32f4_usbh_read(void *drvdata, usbh_packet_t *packet) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + channel_t *channels = dev->channels; + + int8_t channel = get_free_channel(dev); + if (channel == -1) { + // BIG PROBLEM + LOG_PRINTF("FATAL ERROR IN, NO CHANNEL LEFT \r\n"); + usbh_packet_callback_data_t cb_data; + cb_data.status = USBH_PACKET_CALLBACK_STATUS_EFATAL; + cb_data.transferred_length = 0; + packet->callback(packet->callback_arg, cb_data); + return; + } + + channels[channel].data_index = 0; + channels[channel].packet = *packet; + + uint32_t dpid; + if (packet->toggle[0]) { + dpid = OTG_HCTSIZ_DPID_DATA1; + } else { + dpid = OTG_HCTSIZ_DPID_DATA0; + } + + uint32_t num_packets; + if (packet->datalen) { + num_packets = ((packet->datalen - 1) / packet->endpoint_size_max) + 1; + } else { + num_packets = 0; + } + + REBASE_CH(OTG_HCTSIZ, channel) = dpid | (num_packets << 19) | packet->datalen; + + stm32f4_usbh_port_channel_setup(dev, channel, + packet->address, + packet->endpoint_type, + packet->endpoint_address, + OTG_HCCHAR_EPDIR_IN, + packet->endpoint_size_max); +} + +/** + * + * Bug: datalen > max_packet_size ... + */ +static void stm32f4_usbh_write(void *drvdata, const usbh_packet_t *packet) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + channel_t *channels = dev->channels; + + int8_t channel = get_free_channel(dev); + + if (channel == -1) { + // BIG PROBLEM + LOG_PRINTF("FATAL ERROR OUT, NO CHANNEL LEFT \r\n"); + usbh_packet_callback_data_t cb_data; + cb_data.status = USBH_PACKET_CALLBACK_STATUS_EFATAL; + cb_data.transferred_length = 0; + packet->callback(packet->callback_arg, cb_data); + return; + } + + channels[channel].data_index = 0; + channels[channel].packet = *packet; + + uint32_t dpid; + if (packet->endpoint_type == USBH_EPTYP_CONTROL) { + dpid = OTG_HCTSIZ_DPID_MDATA; + packet->toggle[0] = 0; + } else if(packet->endpoint_type == USBH_EPTYP_INTERRUPT) { + if (packet->toggle[0]) { + dpid = OTG_HCTSIZ_DPID_DATA1; + } else { + dpid = OTG_HCTSIZ_DPID_DATA0; + } + } else if (packet->endpoint_type == USBH_EPTYP_BULK) { + if (packet->toggle[0]) { + dpid = OTG_HCTSIZ_DPID_DATA1; + } else { + dpid = OTG_HCTSIZ_DPID_DATA0; + } + } else { + dpid = OTG_HCTSIZ_DPID_DATA0; // ! TODO: BUG + LOG_PRINTF("BUG, %d",__LINE__); + } + + uint32_t num_packets; + if (packet->datalen) { + num_packets = ((packet->datalen - 1) / packet->endpoint_size_max) + 1; + } else { + num_packets = 1; + } + REBASE_CH(OTG_HCTSIZ, channel) = dpid | (num_packets << 19) | packet->datalen; + + stm32f4_usbh_port_channel_setup(dev, channel, + packet->address, + packet->endpoint_type, + packet->endpoint_address, + OTG_HCCHAR_EPDIR_OUT, + packet->endpoint_size_max); + + if (packet->endpoint_type == USBH_EPTYP_CONTROL || + packet->endpoint_type == USBH_EPTYP_BULK) { + + volatile uint32_t *fifo = &REBASE_CH(OTG_FIFO, channel) + RX_FIFO_SIZE; + const uint32_t * buf32 = packet->data; + uint32_t i; + for(i = packet->datalen; i > 0; i-=4) { + *fifo++ = *buf32++; + } + } else { + volatile uint32_t *fifo = &REBASE_CH(OTG_FIFO, channel) + + RX_FIFO_SIZE + TX_NP_FIFO_SIZE; + const uint32_t * buf32 = packet->data; + uint32_t i; + for(i = packet->datalen; i > 0; i-=4) { + *fifo++ = *buf32++; + } + } + LOG_PRINTF("->WRITE %08X\r\n", REBASE_CH(OTG_HCCHAR, channel)); +} + +static void rxflvl_handle(void *drvdata) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + channel_t *channels = dev->channels; + uint32_t rxstsp = REBASE(OTG_GRXSTSP); + uint8_t channel = rxstsp&0xf; + uint32_t len = (rxstsp>>4) & 0x1ff; + if ((rxstsp&OTG_GRXSTSP_PKTSTS_MASK) == OTG_GRXSTSP_PKTSTS_IN) { + uint8_t *data = channels[channel].packet.data; + uint32_t *buf32 = (uint32_t *)&data[channels[channel].data_index]; + + int32_t i; + uint32_t extra; + if (!len) { + return; + } + // Receive data from fifo + volatile uint32_t *fifo = &REBASE_CH(OTG_FIFO, channel); + for (i = len; i > 4; i -= 4) { + *buf32++ = *fifo++; + } + extra = *fifo; + + memcpy(buf32, &extra, i); + channels[channel].data_index += len; + + // If transfer not complete, Enable channel to continue + if ( channels[channel].data_index < channels[channel].packet.datalen) { + if (len == channels[channel].packet.endpoint_size_max) { + REBASE_CH(OTG_HCCHAR, channel) |= OTG_HCCHAR_CHENA; + LOG_PRINTF("CHENA[%d/%d] ", channels[channel].data_index, channels[channel].packet.datalen); + } + + } + + } else if ((rxstsp&OTG_GRXSTSP_PKTSTS_MASK) == OTG_GRXSTSP_PKTSTS_IN_COMP) { +#ifdef USART_DEBUG + uint32_t i; + LOG_PRINTF("\r\nDATA: "); + for (i = 0; i < channels[channel].data_index; i++) { + uint8_t *data = channels[channel].packet.data; + LOG_PRINTF("%02X ", data[i]); + } +#endif + } else if ((rxstsp&OTG_GRXSTSP_PKTSTS_MASK) == OTG_GRXSTSP_PKTSTS_CHH) { + + } else { + + } +} + + +static enum USBH_POLL_STATUS poll_run(usbh_lld_stm32f4_driver_data_t *dev) +{ + channel_t *channels = dev->channels; + + if (dev->dpstate == DEVICE_POLL_STATE_DISCONN) { + REBASE(OTG_GINTSTS) = REBASE(OTG_GINTSTS); + // Check for connection of device + if ((REBASE(OTG_HPRT) & OTG_HPRT_PCDET) && + (REBASE(OTG_HPRT) & OTG_HPRT_PCSTS) ) { + + dev->dpstate = DEVICE_POLL_STATE_DEVCONN; + dev->timestamp_us = dev->time_curr_us; + return USBH_POLL_STATUS_NONE; + } + } + + if (dev->dpstate == DEVICE_POLL_STATE_DEVCONN) { + // May be other condition, e.g. Debounce done, + // using 0.5s wait by default + if (dev->time_curr_us - dev->timestamp_us < 500000) { + return USBH_POLL_STATUS_NONE; + } + + if ((REBASE(OTG_HPRT) & OTG_HPRT_PCDET) && + (REBASE(OTG_HPRT) & OTG_HPRT_PCSTS) ) { + if ((REBASE(OTG_HPRT) & OTG_HPRT_PSPD_MASK) == OTG_HPRT_PSPD_FULL) { + REBASE(OTG_HFIR) = (REBASE(OTG_HFIR) & ~OTG_HFIR_FRIVL_MASK) | 48000; + if ((REBASE(OTG_HCFG) & OTG_HCFG_FSLSPCS_MASK) != OTG_HCFG_FSLSPCS_48MHz) { + REBASE(OTG_HCFG) = (REBASE(OTG_HCFG) & ~OTG_HCFG_FSLSPCS_MASK) | OTG_HCFG_FSLSPCS_48MHz; + LOG_PRINTF("\r\n Reset Full-Speed \r\n"); + } + channels_init(dev); + dev->dpstate = DEVICE_POLL_STATE_DEVRST; + reset_start(dev); + + } else if ((REBASE(OTG_HPRT) & OTG_HPRT_PSPD_MASK) == OTG_HPRT_PSPD_LOW) { + REBASE(OTG_HFIR) = (REBASE(OTG_HFIR) & ~OTG_HFIR_FRIVL_MASK) | 6000; + if ((REBASE(OTG_HCFG) & OTG_HCFG_FSLSPCS_MASK) != OTG_HCFG_FSLSPCS_6MHz) { + REBASE(OTG_HCFG) = (REBASE(OTG_HCFG) & ~OTG_HCFG_FSLSPCS_MASK) | OTG_HCFG_FSLSPCS_6MHz; + LOG_PRINTF("\r\n Reset Low-Speed \r\n"); + } + + channels_init(dev); + dev->dpstate = DEVICE_POLL_STATE_DEVRST; + reset_start(dev); + } + return USBH_POLL_STATUS_NONE; + } + } + + if (dev->dpstate == DEVICE_POLL_STATE_DEVRST) { + if (dev->time_curr_us - dev->timestamp_us < 210000) { + return USBH_POLL_STATUS_NONE; + } else { + dev->dpstate = DEVICE_POLL_STATE_RUN; + } + } + + // ELSE RUN + + if (REBASE(OTG_GINTSTS) & OTG_GINTSTS_SOF) { + REBASE(OTG_GINTSTS) = OTG_GINTSTS_SOF; + } + + while (REBASE(OTG_GINTSTS) & OTG_GINTSTS_RXFLVL) { + //receive data + rxflvl_handle(dev); + } + + if (REBASE(OTG_GINTSTS) & OTG_GINTSTS_HPRTINT) { + if (REBASE(OTG_HPRT) & OTG_HPRT_PENCHNG) { + uint32_t hprt = REBASE(OTG_HPRT); + // Clear Interrupt + // HARDWARE BUG - not mentioned in errata + // To clear interrupt write 0 to PENA + // To disable port write 1 to PENCHNG + REBASE(OTG_HPRT) &= ~OTG_HPRT_PENA; + LOG_PRINTF("PENCHNG"); + if ((hprt & OTG_HPRT_PENA)) { + return USBH_POLL_STATUS_DEVICE_CONNECTED; + } + + } + + if (REBASE(OTG_HPRT) & OTG_HPRT_POCCHNG) { + // TODO: Check for functionality + REBASE(OTG_HPRT) |= OTG_HPRT_POCCHNG; + LOG_PRINTF("POCCHNG"); + } + } + + if (REBASE(OTG_GINTSTS) & OTG_GINTSTS_DISCINT) { + REBASE(OTG_GINTSTS) = OTG_GINTSTS_DISCINT; + LOG_PRINTF("DISCINT"); + + /* + * When the voltage drops, DISCINT interrupt is generated although + * Device is connected, so there is no need to reinitialize channels. + * Often, DISCINT is bad interpreted upon insertion of device + */ + if (!(REBASE(OTG_HPRT) & OTG_HPRT_PCSTS)) { + LOG_PRINTF("discint processsing..."); + channels_init(dev); + } + REBASE(OTG_GINTSTS) = REBASE(OTG_GINTSTS); + dev->dpstate = DEVICE_POLL_STATE_DISCONN; + return USBH_POLL_STATUS_DEVICE_DISCONNECTED; + } + + if (REBASE(OTG_GINTSTS) & OTG_GINTSTS_HCINT) { + uint32_t channel; + + for(channel = 0; channel < dev->num_channels; channel++) + { + if (channels[channel].state != CHANNEL_STATE_WORK || + !(REBASE(OTG_HAINT)&(1<poll_sequence) { + case 0:// wait until AHBIDL is set + if (REBASE(OTG_GRSTCTL) & OTG_GRSTCTL_AHBIDL) { + done = 1; + } + break; + + case 1:// wait 1ms and issue core soft reset + + // needs delay to not hang?? Do not know why. + // Maybe after AHBIDL is set, it needs to set up some things + if (dev->time_curr_us - dev->timestamp_us > 1000) { + REBASE(OTG_GRSTCTL) |= OTG_GRSTCTL_CSRST; + done = 1; + } + break; + + case 2:// wait until core soft reset processing is done + if (!(REBASE(OTG_GRSTCTL) & OTG_GRSTCTL_CSRST)) { + done = 1; + } + break; + + case 3:// wait for 50ms + if (dev->time_curr_us - dev->timestamp_us > 50000) { + done = 1; + } + break; + + case 4:// wait until AHBIDL is set and power up the USB + if (REBASE(OTG_GRSTCTL) & OTG_GRSTCTL_AHBIDL) { + REBASE(OTG_GCCFG) = OTG_GCCFG_VBUSASEN | OTG_GCCFG_VBUSBSEN | + OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN; + done = 1; + } + break; + + case 5:// wait for 50ms and force host only mode + if (dev->time_curr_us - dev->timestamp_us > 50000) { + + // Core initialized + // Force host only mode. + REBASE(OTG_GUSBCFG) |= OTG_GUSBCFG_FHMOD; + done = 1; + } + break; + + case 6:// wait for 200ms and reset PHY clock start reset processing + if (dev->time_curr_us - dev->timestamp_us > 200000) { + /* Restart the PHY clock. */ + REBASE(OTG_PCGCCTL) = 0; + + REBASE(OTG_HCFG) = (REBASE(OTG_HCFG) & ~OTG_HCFG_FSLSPCS_MASK) | + OTG_HCFG_FSLSPCS_48MHz; + + // Start reset processing + REBASE(OTG_HPRT) |= OTG_HPRT_PRST; + + done = 1; + + } + break; + + case 7:// wait for reset processing to be done(12ms), disable PRST + if (dev->time_curr_us - dev->timestamp_us > 12000) { + + REBASE(OTG_HPRT) &= ~OTG_HPRT_PRST; + done = 1; + } + break; + + case 8:// wait 12ms after PRST was disabled, configure fifo + if (dev->time_curr_us - dev->timestamp_us > 12000) { + + REBASE(OTG_HCFG) &= ~OTG_HCFG_FSLSS; + + REBASE(OTG_GRXFSIZ) = RX_FIFO_SIZE; + REBASE(OTG_GNPTXFSIZ) = (TX_NP_FIFO_SIZE << 16) | + RX_FIFO_SIZE; + REBASE(OTG_HPTXFSIZ) = (TX_P_FIFO_SIZE << 16) | + (RX_FIFO_SIZE + TX_NP_FIFO_SIZE); + + // FLUSH RX FIFO + REBASE(OTG_GRSTCTL) |= OTG_GRSTCTL_RXFFLSH; + + done = 1; + } + break; + + case 9: // wait to RX FIFO become flushed, flush TX + if (!(REBASE(OTG_GRSTCTL) & OTG_GRSTCTL_RXFFLSH)) { + REBASE(OTG_GRSTCTL) |= OTG_GRSTCTL_TXFFLSH | (0x10 << 6); + + done = 1; + } + break; + + case 10: // wait to TX FIFO become flushed + if (!(REBASE(OTG_GRSTCTL) & OTG_GRSTCTL_TXFFLSH)) { + + channels_init(dev); + + REBASE(OTG_GOTGINT) |= 1 << 19; + REBASE(OTG_GINTMSK) = 0; + REBASE(OTG_GINTSTS) = ~0; + REBASE(OTG_HPRT) |= OTG_HPRT_PPWR; + + done = 1; + } + break; + + case 11: // wait 200ms + if (dev->time_curr_us - dev->timestamp_us > 200000) { + + // Uncomment to enable Interrupt generation + REBASE(OTG_GAHBCFG) |= OTG_GAHBCFG_GINT; + + LOG_PRINTF("INIT COMPLETE\r\n"); + + // Finish + dev->state = DEVICE_STATE_RUN; + dev->dpstate = DEVICE_POLL_STATE_DISCONN; + + done = 1; + } + } + + if (done) { + dev->poll_sequence++; + dev->timestamp_us = dev->time_curr_us; + LOG_PRINTF("\t\t POLL SEQUENCE %d\r\n", dev->poll_sequence); + } + +} + +static void poll_reset(usbh_lld_stm32f4_driver_data_t *dev) +{ + if (dev->time_curr_us - dev->timestamp_us > 10000) { + REBASE(OTG_HPRT) &= ~OTG_HPRT_PRST; + dev->state = dev->state_prev; + dev->state_prev = DEVICE_STATE_RESET; + + LOG_PRINTF("RESET"); + } else { + LOG_PRINTF("waiting %d < %d\r\n",dev->time_curr_us, dev->timestamp_us); + } +} + +static enum USBH_POLL_STATUS stm32f4_usbh_poll(void *drvdata, uint32_t time_curr_us) +{ + + (void)time_curr_us; + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + uint32_t ret = ret = USBH_POLL_STATUS_NONE; + + // TODO: Check overflow case + dev->time_curr_us = time_curr_us; + + switch (dev->state) { + case DEVICE_STATE_RUN: + ret = poll_run(dev); + break; + + case DEVICE_STATE_INIT: + poll_init(dev); + break; + + case DEVICE_STATE_RESET: + poll_reset(dev); + break; + default: + break; + } + + return ret; + +} + + +/** + * + * Returns positive free channel id + * otherwise -1 for error + */ +static int8_t get_free_channel(void *drvdata) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + channel_t *channels = dev->channels; + uint32_t i = 0; + for (i = 0; i < dev->num_channels; i++) { + if (dev->channels[i].state == CHANNEL_STATE_FREE && + !(REBASE_CH(OTG_HCCHAR, i) & OTG_HCCHAR_CHENA)) { + channels[i].state = CHANNEL_STATE_WORK; + REBASE_CH(OTG_HCINT, i) = ~0; + REBASE_CH(OTG_HCINTMSK, i) |= OTG_HCINTMSK_ACKM | OTG_HCINTMSK_NAKM | + OTG_HCINTMSK_TXERRM | OTG_HCINTMSK_XFRCM | + OTG_HCINTMSK_DTERRM | OTG_HCINTMSK_BBERRM | + OTG_HCINTMSK_CHHM | OTG_HCINTMSK_STALLM | + OTG_HCINTMSK_FRMORM; + REBASE(OTG_HAINTMSK) |= (1 << i); + dev->channels[i].error_count = 0; + return i; + } + } + return -1; +} + +/* + * Do not clear callback and callback data, so channel can be freed even before callback is called + * This saves number of active channels: When one transfer ends, in callback driver can write/read to this channel again (indirectly) + */ +static void free_channel(void *drvdata, uint8_t channel) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + channel_t *channels = dev->channels; + + if (REBASE_CH(OTG_HCCHAR, channel) & OTG_HCCHAR_CHENA) { + REBASE_CH(OTG_HCCHAR, channel) |= OTG_HCCHAR_CHDIS; + REBASE_CH(OTG_HCINT, channel) = ~0; + LOG_PRINTF("\r\nDisabling channel %d\r\n", channel); + } else { + channels[channel].state = CHANNEL_STATE_FREE; + } +} +/** + * Init channels + */ +static void channels_init(void *drvdata) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + + uint32_t i = 0; + for (i = 0; i < dev->num_channels; i++) { + REBASE_CH(OTG_HCINT, i) = ~0; + REBASE_CH(OTG_HCINTMSK, i) = 0x7ff; + free_channel(dev, i); + } + + // Enable interrupt mask bits for all channels + REBASE(OTG_HAINTMSK) = (1 << dev->num_channels) - 1; +} + +/** + * Get speed of connected device + * + */ +uint8_t stm32f4_root_speed(void *drvdata) +{ + usbh_lld_stm32f4_driver_data_t *dev = drvdata; + (void)dev; + uint32_t hprt_speed = REBASE(OTG_HPRT) & OTG_HPRT_PSPD_MASK; + if (hprt_speed == OTG_HPRT_PSPD_LOW) { + return USBH_SPEED_LOW; + } else if(hprt_speed == OTG_HPRT_PSPD_FULL) { + return USBH_SPEED_FULL; + } else if(hprt_speed == OTG_HPRT_PSPD_HIGH) { + return USBH_SPEED_HIGH; + } else { + // Should not happen(let the compiler be happy) + return USBH_SPEED_FULL; + } +} +#endif // if defined otg_hs or otg_fs + + +#ifdef USART_DEBUG + +/** + * Just for debug + */ +void print_channels(const void *lld) +{ + usbh_lld_stm32f4_driver_data_t *dev = ((usbh_driver_t *)lld)->driver_data; + channel_t *channels = dev->channels; + int32_t i; + LOG_PRINTF("\r\nCHANNELS: \r\n"); + for (i = 0;i < dev->num_channels;i++) { + LOG_PRINTF("%4d %4d %4d %08X\r\n", channels[i].state, channels[i].packet.address, channels[i].packet.datalen, MMIO32(dev->base + OTG_HCINT(i))); + } +} +#endif + + +const void *usbh_lld_stm32f4_drivers[] = { +#if defined(USE_STM32F4_USBH_DRIVER_FS) + &stm32f4_usbh_driver_fs, +#endif + +#if defined(USE_STM32F4_USBH_DRIVER_HS) + &stm32f4_usbh_driver_hs, +#endif + 0 +}; -- cgit