diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-15 10:01:21 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-15 10:01:21 +0000 |
commit | e30e2339419728c8beff094bff8deb359207c10c (patch) | |
tree | a56dfe853667ad38d8c8c5aa7fe81b8aff5ee326 | |
parent | Initial commit. (diff) | |
download | firmware-free-e30e2339419728c8beff094bff8deb359207c10c.tar.xz firmware-free-e30e2339419728c8beff094bff8deb359207c10c.zip |
Adding upstream version 20200122.upstream/20200122upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
172 files changed, 34135 insertions, 0 deletions
@@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + diff --git a/av7110/Boot.S b/av7110/Boot.S new file mode 100644 index 0000000..d562fdc --- /dev/null +++ b/av7110/Boot.S @@ -0,0 +1,109 @@ +/* + Boot.S: boot loader for Siemens DVB-S card + + Copyright (C) 2001 Convergence integrated media GmbH + Written by Ralph Metzler + <rjkm@convergence.de> + Copyright (C) 2006 Matthieu CASTET <castet.mattheiu@free.fr> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +*/ + +/* + check AV711x_3_1.pdf for some hardware infos + build it with : + $ cc -mbig-endian -c Boot.S + $ ld -Ttext 0x2c000000 -EB -o Boot Boot.o + $ objcopy -Obinary Boot +*/ + + .text + .align + .globl _start +_start: + b reset // reset vector + movs pc, r14 // undefined + subs pc, r14, #4 // SWI + subs pc, r14, #4 // prefetch abort + subs pc, r14, #8 // data abort + subs pc, r14, #4 // reserved + subs pc, r14, #4 // IRQ + subs pc, r14, #4 // FIQ + + .word tbl // table needed by firmware ROM +tbl: .word (endtbl - tbl) + .word 0 + .word conf +endtbl: .word 0 +conf: .word 0xa5a55a5a + .word 0x001f1555 + .word 0x00000009 + +reset: ldr r13, buffer + ldr r4, flag + mov r0, #0 + str r0, [r4] + str r0, [r4, #4] + + ldr r1, wait_address + ldr r2, flag_address + ldr r3, sram + +copycode: // copy the code HW Sram + ldmia r1!, {r5-r12} + stmia r3!, {r5-r12} + cmp r1, r2 + ble copycode + ldr pc, sram // jump to the copied code + +wait: ldrh r1, [r4] // wait for flag!=0 + cmp r1, #0 + beq wait + + mov r1, r13 // buffer address + ldr r3, [r4,#4] // destaddr + + ldrh r2, [r4,#2] // get segment length + add r2, r2, #63 // round length to next 64 bytes + movs r2, r2, lsr #6 // and divide by 64 + moveq r0, #2 // if 0, set flag to 2, else signal + strh r0, [r4] // that buffer is accepted by setting to 0 + beq wait + +copyloop: + ldmia r1!, {r5-r12} + stmia r3!, {r5-r12} + ldmia r1!, {r5-r12} + stmia r3!, {r5-r12} + subs r2, r2, #1 + bne copyloop + + eor r13, r13, #0x1400 // switch to other buffer + b wait + +// flag is stored at 0x2c0003f8, length at 0x2c0003fa, +// destaddr at 0x2c0003fc + +flag: .word 0x2c0003f8 + + +// buffer 1 is at 0x2c000400, buffer 2 at 0x2c001000 + +buffer: .word 0x2c000400 + +sram: .word 0x9e000800 +wait_address: .word wait +flag_address: .word flag diff --git a/av7110/Makefile b/av7110/Makefile new file mode 100644 index 0000000..a2776fe --- /dev/null +++ b/av7110/Makefile @@ -0,0 +1,11 @@ +CC = arm-linux-gnueabi-gcc +LD = arm-linux-gnueabi-ld +OBJCOPY = arm-linux-gnueabi-objcopy + +bootcode.bin: Boot.S + $(CC) -mbig-endian -c Boot.S + $(LD) -Ttext 0x2c000000 -EB -o Boot Boot.o + $(OBJCOPY) -Obinary Boot bootcode.bin + +clean: + rm -f Boot.o Boot bootcode.bin diff --git a/av7110/bootcode.bin b/av7110/bootcode.bin Binary files differnew file mode 100644 index 0000000..f315775 --- /dev/null +++ b/av7110/bootcode.bin diff --git a/carl9170-1.fw b/carl9170-1.fw Binary files differnew file mode 100644 index 0000000..05c1f48 --- /dev/null +++ b/carl9170-1.fw diff --git a/carl9170fw/CMakeLists.txt b/carl9170fw/CMakeLists.txt new file mode 100644 index 0000000..c017bbb --- /dev/null +++ b/carl9170fw/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 2.8) + +project(carl9170) + +#if you don't want the full compiler output, remove the following line +#set(CMAKE_VERBOSE_MAKEFILE ON) + +include("config.cmake") + +add_subdirectory(carlfw) + +if (CONFIG_CARL9170FW_BUILD_MINIBOOT) + add_subdirectory(minifw) +endif (CONFIG_CARL9170FW_BUILD_MINIBOOT) + +if (CONFIG_CARL9170FW_BUILD_TOOLS) + add_subdirectory(tools) +endif (CONFIG_CARL9170FW_BUILD_TOOLS) + diff --git a/carl9170fw/COPYRIGHT b/carl9170fw/COPYRIGHT new file mode 100644 index 0000000..b1d3e77 --- /dev/null +++ b/carl9170fw/COPYRIGHT @@ -0,0 +1,19 @@ +Atheros carl9170 firmware - used by the ar9170 wireless device + +Copyright (c) 2000-2005 ZyDAS Technology Corporation +Copyright (c) 2007-2009 Atheros Communications, Inc. +Copyright (c) 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/carl9170fw/GPL b/carl9170fw/GPL new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/carl9170fw/GPL @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/carl9170fw/Kconfig b/carl9170fw/Kconfig new file mode 100644 index 0000000..03fd2cd --- /dev/null +++ b/carl9170fw/Kconfig @@ -0,0 +1,5 @@ +mainmenu "CARL9170 Firmware Configuration" + +source "carlfw/Kconfig" +source "minifw/Kconfig" +source "tools/Kconfig" diff --git a/carl9170fw/README b/carl9170fw/README new file mode 100644 index 0000000..230b2db --- /dev/null +++ b/carl9170fw/README @@ -0,0 +1,43 @@ +Community AR9170 Linux firmware +----------------------- + +This is the firmware for the Atheros ar9170 802.11n devices. + +To build the firmware you will need an SH-2 toolchain. +You can build your own toolchain: + +make -C toolchain + +but be aware that this will take some time and requires +about 1.2 GiB disk space. + +The resulting firmware, carl9170.fw, can be used only +with the carl9170 Linux driver. + +After getting a toolchain, you will need to get more +tools & libs: + + * gcc 4.4+ + + * gperf, bison/flex + + * cmake 2.8.0+ + + * libusb 1.0+ + + * SDL SDK 1.2.13+ + +afterwards, simply execute: + +autogen.sh + +to start the configuration and build process. + +if you want to "install" your own firmware, you can either +do this manually, or by executing: + +autogen.sh install + +This will place a copy with the right filename [adds API rev] +into /lib/firmware/[the default path on most Distributions]. + diff --git a/carl9170fw/autogen.sh b/carl9170fw/autogen.sh new file mode 100755 index 0000000..6843f19 --- /dev/null +++ b/carl9170fw/autogen.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +set -e + +case "$1" in + config) + echo "Configuring..." + pushd config + cmake . + make + popd + config/conf Kconfig + cmake . + ;; + + compile) + echo "Compile time..." + make + ;; + + install) + if [ ! -e .config ]; then + exit 1 + fi + + . ./.config + make + + echo -n "Installing firmware..." + if [ "$CONFIG_CARL9170FW_BUILD_TOOLS" = "y" ] && + [ "$CONFIG_CARL9170FW_BUILD_MINIBOOT" = "y" ]; then + echo -n "Apply miniboot..." + tools/src/miniboot a carlfw/carl9170.fw minifw/miniboot.fw + fi + + install -m 644 carlfw/carl9170.fw \ + ../carl9170-$CONFIG_CARL9170FW_RELEASE_VERSION.fw + echo "done." + ;; + + *) + $0 config + $0 compile + ;; + + +esac diff --git a/carl9170fw/carlfw/CMakeLists.txt b/carl9170fw/carlfw/CMakeLists.txt new file mode 100644 index 0000000..caf9595 --- /dev/null +++ b/carl9170fw/carlfw/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 2.8) + +project(carl9170.fw) + +include("../extra/sh-elf-linux.cmake") +include("../config.cmake") + +set(CARLFW_CFLAGS_WARNING "-W -Wall -Wextra -Wunreachable-code -Winline -Wlogical-op -Wno-packed-bitfield-compat -Winit-self -Wshadow -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wformat=2 -Wcast-align -Wmissing-format-attribute -Wmissing-prototypes -Wtype-limits -Wmissing-declarations -Wmissing-noreturn -Wredundant-decls -Wnested-externs -Wdisabled-optimization -Wpointer-arith -Wvolatile-register-var -Waddress -Wbad-function-cast -Wunsafe-loop-optimizations") +set(CARLFW_CFLAGS_EXTRA "-mbitops -std=gnu99 -ffunction-sections -Wframe-larger-than=128 -Werror") +set(CARLFW_CFLAGS_DEF "-D__CARL9170FW__") +if (CONFIG_CARL9170FW_AGGRESSIVE_CFLAGS) + set(CARLFW_CFLAGS_AGGRESSIVE "-fomit-frame-pointer -fsee -frename-registers -ftree-vectorize -flto -fstrict-volatile-bitfields -fmodulo-sched") +endif (CONFIG_CARL9170FW_AGGRESSIVE_CFLAGS) + +include_directories (../include/linux ../include/shared ../include include) + +set(carl9170_main_src src/main.c src/wlan.c src/fw.c src/gpio.c + src/cmd.c src/uart.c src/dma.c src/hostif.c src/reboot.S + src/printf.c src/rf.c src/cam.c src/wol.c) + +set(carl9170_lib_src src/ashlsi3.S src/memcpy.S src/memset.S src/udivsi3_i4i-Os.S) +set(carl9170_usb_src usb/main.c usb/usb.c usb/fifo.c) + +set(carl9170_src ${carl9170_main_src} ${carl9170_lib_src} ${carl9170_usb_src}) + +set_source_files_properties(src/ashlsi3.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/memcpy.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/memset.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/reboot.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/udivsi3_i4i-Os.S PROPERTIES LANGUAGE C) + +add_executable(carl9170.elf ${carl9170_src}) + +set_target_properties(carl9170.elf PROPERTIES LINKER_LANGUAGE C) + +set_target_properties(carl9170.elf PROPERTIES COMPILE_FLAGS + " ${CARLFW_CFLAGS_DEF} ${CARLFW_CFLAGS_EXTRA} ${CARLFW_CFLAGS_AGGRESSIVE} ${CARLFW_CFLAGS_WARNING}") +set_target_properties(carl9170.elf PROPERTIES LINK_FLAGS "-Tcarl9170.lds") + + + +add_custom_target(firmware ALL) + +add_custom_command( + SOURCE carl9170.elf + COMMAND ${OBJCOPY} + ARGS --strip-unneeded -O binary -R .sram -R .eeprom -R .fwdsc carl9170.elf carl9170.bin + TARGET firmware + OUTPUTS carl9170.bin) + +add_custom_command( + SOURCE carl9170.elf + COMMAND ${OBJCOPY} + ARGS --strip-unneeded -O binary -j .fwdsc carl9170.elf carl9170.dsc + TARGET firmware + OUTPUTS carl9170.dsc) + +add_custom_command( + SOURCE firmware + TARGET firmware + COMMAND cat + ARGS "carl9170.bin" "carl9170.dsc" > "carl9170.fw" + DEPENDS carl9170.elf carl9170.bin carl9170.dsc + OUTPUTS carl9170.fw) + +SET_DIRECTORY_PROPERTIES( + PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "carl9170.fw") diff --git a/carl9170fw/carlfw/Kconfig b/carl9170fw/carlfw/Kconfig new file mode 100644 index 0000000..f42268c --- /dev/null +++ b/carl9170fw/carlfw/Kconfig @@ -0,0 +1,233 @@ +menu "General" + +config CARL9170FW_RELEASE_VERSION + int + default 1 + +menu "Selectable Hardware Options" + +choice + prompt "Receiver Max. Frame Length" + default CARL9170FW_RX_FRAME_LEN_8192 + + config CARL9170FW_RX_FRAME_LEN_4096 + bool "4096" + + config CARL9170FW_RX_FRAME_LEN_8192 + bool "8192" + + config CARL9170FW_RX_FRAME_LEN_16384 + bool "16384" + + config CARL9170FW_RX_FRAME_LEN_32768 + bool "32768" + +endchoice + +config CARL9170FW_RX_FRAME_LEN + int + default 4096 if CARL9170FW_RX_FRAME_LEN_4096 + default 8192 if CARL9170FW_RX_FRAME_LEN_8192 + default 16384 if CARL9170FW_RX_FRAME_LEN_16384 + default 32768 if CARL9170FW_RX_FRAME_LEN_32768 + +config CARL9170FW_GPIO_INTERRUPT + def_bool y + prompt "GPIO Software Interrupt" + ---help--- + When this option is enabled, the firmware will poll the GPIO + registers and reports to the driver whenever the GPIO state + has changed from a previous state. + + Note: This feature is necessary to monitor the WPS button, + if you have one on your device, then say Y. + +config CARL9170FW_SECURITY_ENGINE + def_bool y + prompt "Support Hardware Crypto Engine" + ---help--- + This options controls if the firmware will allow the driver + to program the security engine / CAM through a firmware + interface. + + Say Y. Unless you want to do the en- and decryption for + CCMP(AES), TKIP/WEP(RC4) in the application anyway. + +config CARL9170FW_RADIO_FUNCTIONS + def_bool y + prompt "Enable Firmware-supported Radio/RF functions" + ---help--- + Some PHY/RF functions (e.g.: AGC and Noise calibration) need + to be done in the firmware. + + Say Y, unless you really don't need the Radio/RF for + your project. + +endmenu + +menu "802.11 Firmware Features" + +config CARL9170FW_CAB_QUEUE + def_bool y + prompt "Support software-based Content after Beacon Queue" + ---help--- + This (software) queue is used to send any broad-/multi-cast buffered + frames after the next DTIM beacon. + + This feature is required for Accesspoint mode operation. + + Say Y. + +endmenu + +source "carlfw/usb/Kconfig" + +menu "Experimental, Unstable & Testing Extensions" + +config CARL9170FW_PRINTF + def_bool y + prompt "Advanced printf" + depends on CARL9170FW_DEBUG_UART || CARL9170FW_DEBUG_USB + ---help--- + Advanced printf (very useful for debugging purposes) + The formats supported by this implementation are: + 'd' 'u' 'c' 's' 'x' 'X' 'p'. + + Note: If this option is disabled, the firmware will be only + capable of reported _preformated_ string. + +config CARL9170FW_EXPERIMENTAL + def_bool y + prompt "Experimental Features" + +config CARL9170FW_WOL_OPTION + def_bool n + prompt "Wakeup on WLAN" + depends on CARL9170FW_EXPERIMENTAL + ---help--- + With this option enabled, the firmware can wake-up + suspended hosts... As long as they fully support + USB remote wakeup. + +config CARL9170FW_WOL + def_bool n + depends on CARL9170FW_WOL_OPTION + +config CARL9170FW_WOL_NL80211_TRIGGERS + def_bool n + prompt "Standard NL80211 wakeup triggers" + depends on CARL9170FW_WOL_OPTION + select CARL9170FW_WOL + ---help--- + Available triggers: + * Magic Packet(tm) pattern + * disconnect event + +config CARL9170FW_WOL_PROBE_REQUEST + def_bool n + prompt "Probe Request" + depends on CARL9170FW_WOL_OPTION + select CARL9170FW_WOL + ---help--- + Scan probe requests for a given SSID. + +config CARL9170FW_WOL_PROBE_REQUEST_SSID + string + prompt "Wakeup on WLAN SSID" + default "CARL9170_WAKEUP" + depends on CARL9170FW_WOL_PROBE_REQUEST + +config CARL9170FW_VIFS_NUM + default 1 + int + prompt "Number of additional pseudo virtual interfaces" + depends on CARL9170FW_EXPERIMENTAL + +config CARL9170FW_FW_MAC_RESET + def_bool y + prompt "Firmware MAC Chip recovery" + depends on CARL9170FW_EXPERIMENTAL + +config CARL9170FW_NOISY_MAC_RESET + def_bool n + prompt "Notify MAC RESET events" + depends on CARL9170FW_FW_MAC_RESET + +config CARL9170FW_BROKEN_FEATURES + def_bool n + prompt "Broken Featurs" + +config CARL9170FW_DEBUG + def_bool n + depends on CARL9170FW_BROKEN_FEATURES && CARL9170FW_PRINTF + prompt "Enable verbose debugging messages" + +config CARL9170FW_DEBUG_LED_HEARTBEAT + def_bool n + prompt "LED Heartbeat" + depends on CARL9170FW_BROKEN_FEATURES + ---help--- + This option conflicts with the application's LED code. + Also, it assumes that you have two LEDs, which is not + necessarily true. + +config CARL9170FW_DEBUG_UART + def_bool n + prompt "Pass debug messages through Highspeed UART" + depends on CARL9170FW_BROKEN_FEATURES + ---help--- + This option allows the firmware to send BUG/ERR/INFO/DBG and + hexdumps through the UART _as well_. However, first: you must + connect a working logger. + +config CARL9170FW_WATCHDOG_BUTTON + def_bool n + depends on CARL9170FW_BROKEN && CARL9170FW_GPIO_INTERRUPT + prompt "Trigger Watchdog by pressing the WPS button" + +choice CARL9170FW_UART_CLOCK + prompt "UART Clock" + depends on CARL9170FW_DEBUG_UART + default CARL9170FW_UART_CLOCK_40M + +config CARL9170FW_UART_CLOCK_25M + bool "25" + +config CARL9170FW_UART_CLOCK_40M + bool "40" + +endchoice + +config CARL9170FW_UNUSABLE + def_bool y + depends on CARL9170FW_BROKEN || CARL9170FW_DEBUG + +config CARL9170FW_USB_MODESWITCH + def_bool n + prompt "USB 1.1 / 2.0 switching support" + depends on CARL9170FW_BROKEN_FEATURES + ---help--- + Mostly implemented, but untested and some serious + doubts remain. + +config CARL9170FW_DMA_QUEUE_BUMP + def_bool n + prompt "Bump a stuck TX queue before doing a MAC reset" + depends on CARL9170FW_BROKEN_FEATURES + +menu "Build Options" +config CARL9170FW_AGGRESSIVE_CFLAGS + def_bool y + prompt "Enable aggressive size optimization" + ---help--- + This option adds several more optimization compiler flags, + which can greatly reduce the firmware size... at the expense + of machine-code readability. + + Say Y. Else the firmware might not fit onto the device! + +endmenu + +endmenu + +endmenu diff --git a/carl9170fw/carlfw/carl9170.lds b/carl9170fw/carlfw/carl9170.lds new file mode 100644 index 0000000..97dda68 --- /dev/null +++ b/carl9170fw/carlfw/carl9170.lds @@ -0,0 +1,59 @@ +/* + * The carl9170 firwmare gets copied into the device's + * Program RAM (pram), which has a size of 16K, but + * also has to accomodate the stack the device uses, + * which starts at the top of the 16k, so we pretend + * that we just have 16256 (16k - 128) of pram. + * + * This section documents some of the other areas + * mapped into the firmware processor's address space + * as well. + */ + +ENTRY(_start); + +MEMORY +{ + eeprom : ORIGIN = 0x000000, LENGTH = 1024k + sram : ORIGIN = 0x100000, LENGTH = 96k + uart : ORIGIN = 0x1c0000, LENGTH = 4k + timer : ORIGIN = 0x1c1000, LENGTH = 4k + vflash : ORIGIN = 0x1c2000, LENGTH = 4k + wlan : ORIGIN = 0x1c3000, LENGTH = 4k + pci2ahb : ORIGIN = 0x1c4000, LENGTH = 4k + security : ORIGIN = 0x1c5000, LENGTH = 4k + gpio : ORIGIN = 0x1d0000, LENGTH = 4k + memctl : ORIGIN = 0x1d1000, LENGTH = 4k + irqctl : ORIGIN = 0x1d2000, LENGTH = 4k + usb : ORIGIN = 0x1e1000, LENGTH = 4k + pta : ORIGIN = 0x1e2000, LENGTH = 4k + pram : ORIGIN = 0x200000, LENGTH = 16256 + bogus : ORIGIN = 0x300000, LENGTH = 8k +} + +SECTIONS +{ + .eeprom : { *(.eeprom*) } > eeprom + .sram : { *(.sram*) } > sram + + /* + * The ar9170 boot code will execute the code + * at address 0x04 from the loaded firmware as + * such we must ensure our starting routine + * is kept at that address. + */ + .padding : { + /* NOP NOP just in case */ + LONG(0x00090009) + } > pram + + .boot : { *(.boot) } > pram + /* anything else can be anywhere */ + + .text : { *(.text*) } > pram + .rodata : { *(.rodata*) } > pram + .bss : { *(.bss) } > pram + .data : { *(.data*) } > pram + + .fwdsc : { KEEP(*(.fwdsc)) } > bogus +} diff --git a/carl9170fw/carlfw/include/cam.h b/carl9170fw/carlfw/include/cam.h new file mode 100644 index 0000000..33afe03 --- /dev/null +++ b/carl9170fw/carlfw/include/cam.h @@ -0,0 +1,49 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * CAM (Security Engine) definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_CAM_H +#define __CARL9170FW_CAM_H + +#include "config.h" +#include "cmd.h" + +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE + +#define ENCRY_TYPE_START_ADDR 24 +#define DEFAULT_ENCRY_TYPE 26 +#define KEY_START_ADDR 27 +#define STA_KEY_START_ADDR 155 +#define COUNTER_START_ADDR 163 +#define STA_COUNTER_START_ADDR 165 + +/* CAM */ +#define MIC_FINISH 0x1 + +void set_key(const struct carl9170_set_key_cmd *key); +void disable_key(const struct carl9170_disable_key_cmd *key); + +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ + +#endif /* __CARL9170FW_CAM_H */ diff --git a/carl9170fw/carlfw/include/carl9170.h b/carl9170fw/carlfw/include/carl9170.h new file mode 100644 index 0000000..cb16415 --- /dev/null +++ b/carl9170fw/carlfw/include/carl9170.h @@ -0,0 +1,222 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware context definition + * + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_CARL9170_H +#define __CARL9170FW_CARL9170_H + +#include "generated/autoconf.h" +#include "version.h" +#include "config.h" +#include "types.h" +#include "compiler.h" +#include "fwcmd.h" +#include "hw.h" +#include "dma.h" +#include "usb.h" +#include "cmd.h" + +struct carl9170_bar_ctx { + uint8_t ta[6]; + uint8_t ra[6]; + __le16 start_seq_num; + __le16 control; +}; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE +enum carl9170_cab_trigger { + CARL9170_CAB_TRIGGER_EMPTY = 0, + CARL9170_CAB_TRIGGER_ARMED = BIT(0), + CARL9170_CAB_TRIGGER_DEFER = BIT(1), +}; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +enum carl9170_ep0_action { + CARL9170_EP0_NO_ACTION = 0, + CARL9170_EP0_STALL = BIT(0), + CARL9170_EP0_TRIGGER = BIT(1), +}; + +enum carl9170_mac_reset_state { + CARL9170_MAC_RESET_OFF = 0, + CARL9170_MAC_RESET_ARMED, + CARL9170_MAC_RESET_RESET, + CARL9170_MAC_RESET_FORCE, +}; + +enum carl9170_suspend_mode { + CARL9170_HOST_AWAKE = 0, + CARL9170_HOST_SUSPENDED, + CARL9170_AWAKE_HOST, +}; + +enum carl9170_phy_state { + CARL9170_PHY_OFF = 0, + CARL9170_PHY_ON +}; + +typedef void (*fw_desc_callback_t)(void *, const bool); + +/* + * This platform - being an odd 32-bit architecture - prefers to + * have 32-Bit variables. + */ + +struct firmware_context_struct { + /* timer / clocks */ + unsigned int ticks_per_usec; + unsigned int counter; /* main() cycles */ + + /* misc */ + unsigned int watchdog_enable; + unsigned int reboot; + unsigned int suspend_mode; + + struct { + /* Host Interface DMA queues */ + struct dma_queue up_queue; /* used to send frames to the host */ + struct dma_queue down_queue; /* stores incoming frames from the host */ + } pta; + + struct { + /* Hardware DMA queues */ + struct dma_queue tx_queue[__AR9170_NUM_TX_QUEUES]; /* wlan tx queue */ + struct dma_queue tx_retry; + struct dma_queue rx_queue; /* wlan rx queue */ + + /* tx aggregate scheduling */ + struct carl9170_tx_superframe *ampdu_prev[__AR9170_NUM_TX_QUEUES]; + + /* Hardware DMA queue unstuck/fix detection */ + unsigned int last_super_num[__AR9170_NUM_TX_QUEUES]; + struct carl9170_tx_superframe *last_super[__AR9170_NUM_TX_QUEUES]; + unsigned int mac_reset; + unsigned int soft_int; + + /* rx filter */ + unsigned int rx_filter; + + /* tx sequence control counters */ + unsigned int sequence[CARL9170_INTF_NUM]; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + /* CAB */ + struct dma_queue cab_queue[CARL9170_INTF_NUM]; + unsigned int cab_queue_len[CARL9170_INTF_NUM]; + unsigned int cab_flush_time; + enum carl9170_cab_trigger cab_flush_trigger[CARL9170_INTF_NUM]; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + /* tx status */ + unsigned int tx_status_pending, + tx_status_head_idx, + tx_status_tail_idx; + struct carl9170_tx_status tx_status_cache[CARL9170_TX_STATUS_NUM]; + + /* internal descriptor for use within the service routines */ + struct dma_desc *fw_desc; + unsigned int fw_desc_available; + void *fw_desc_data; + fw_desc_callback_t fw_desc_callback; + + /* BA(R) Request Handler */ + struct carl9170_bar_ctx ba_cache[CONFIG_CARL9170FW_BACK_REQS_NUM]; + unsigned int ba_tail_idx, + ba_head_idx, + queued_ba; + + unsigned int queued_bar; + } wlan; + + struct { + unsigned int config, + interface_setting, + alternate_interface_setting, + device_feature; + enum carl9170_ep0_action ep0_action; + + void *ep0_txrx_buffer; + unsigned int ep0_txrx_len, + ep0_txrx_pos; + + struct ar9170_usb_config *cfg_desc; + struct ar9170_usb_config *os_cfg_desc; + + /* + * special buffers for command & response handling + * + * the firmware uses a sort of ring-buffer to communicate + * to the host. + */ + unsigned int int_pending, + int_desc_available, + int_head_index, + int_tail_index; + struct dma_desc *int_desc; + struct carl9170_rsp int_buf[CARL9170_INT_RQ_CACHES]; + +#ifdef CONFIG_CARL9170FW_DEBUG_USB + /* USB printf */ + unsigned int put_index; + uint8_t put_buffer[CARL9170_MAX_CMD_PAYLOAD_LEN]; +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + + } usb; + + struct { +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + /* (cached) ar9170_rf_init */ + + /* PHY/RF state */ + unsigned int frequency; + unsigned int ht_settings; + + enum carl9170_phy_state state; + struct carl9170_psm psm; +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + } phy; + + unsigned int tally_clock; + struct carl9170_tally_rsp tally; + unsigned int tx_time; + +#ifdef CONFIG_CARL9170FW_WOL + struct { + struct carl9170_wol_cmd cmd; + unsigned int last_beacon; + unsigned int lost_null; + unsigned int last_null; + bool wake_up; + } wol; +#endif /* CONFIG_CARL9170FW_WOL */ + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + struct carl9170_gpio cached_gpio_state; +#endif /*CONFIG_CARL9170FW_GPIO_INTERRUPT */ +}; + +/* + * global firmware context struct. + * + * NOTE: This struct will zeroed out in start() + */ +extern struct firmware_context_struct fw; +#endif /* __CARL9170FW_CARL9170_H */ diff --git a/carl9170fw/carlfw/include/cmd.h b/carl9170fw/carlfw/include/cmd.h new file mode 100644 index 0000000..54dd350 --- /dev/null +++ b/carl9170fw/carlfw/include/cmd.h @@ -0,0 +1,55 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware command interface definition + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_CMD_H +#define __CARL9170FW_CMD_H + +#include "config.h" +#include "compiler.h" +#include "types.h" + +#include "fwcmd.h" + +static inline void __check(void) +{ + BUILD_BUG_ON(sizeof(struct carl9170_cmd) != CARL9170_MAX_CMD_LEN); + BUILD_BUG_ON(sizeof(struct carl9170_rsp) != CARL9170_MAX_CMD_LEN); + BUILD_BUG_ON(sizeof(struct carl9170_set_key_cmd) != CARL9170_SET_KEY_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_disable_key_cmd) != CARL9170_DISABLE_KEY_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_rf_init) != CARL9170_RF_INIT_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_rf_init_result) != CARL9170_RF_INIT_RESULT_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_psm) != CARL9170_PSM_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_tsf_rsp) != CARL9170_TSF_RSP_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_bcn_ctrl_cmd) != CARL9170_BCN_CTRL_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_tx_status) != CARL9170_TX_STATUS_SIZE); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_status) != CARL9170_TX_STATUS_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_gpio) != CARL9170_GPIO_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_rx_filter_cmd) != CARL9170_RX_FILTER_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_wol_cmd) != CARL9170_WOL_CMD_SIZE); +} + +void handle_cmd(struct carl9170_rsp *resp); + +#endif /* __CARL9170FW_CMD_H */ diff --git a/carl9170fw/carlfw/include/config.h b/carl9170fw/carlfw/include/config.h new file mode 100644 index 0000000..e7c358d --- /dev/null +++ b/carl9170fw/carlfw/include/config.h @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "generated/autoconf.h" +#include "version.h" +#include "types.h" +#include "compiler.h" +#include "fwcmd.h" +#include "hw.h" + +#ifndef __CARL9170FW_CONFIG_H +#define __CARL9170FW_CONFIG_H + +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +#if GCC_VERSION < 40400 +# error "This firmware will not work if it is compiled with gcc versions < 4.4" +# error "See: http://gcc.gnu.org/gcc-4.4/changes.html / Caveats No. 4" +#endif + +#if ((defined CONFIG_CARL9170FW_PRINTF) && \ + (!defined CONFIG_CARL9170FW_DEBUG_USB) && \ + (!defined CONFIG_CARL9170FW_DEBUG_UART)) +# warning "You have disabled all debug message transports." +# warning "However CONFIG_CARL9170FW_PRINTF is still set..." +# warning "Which is a waste of firmware space, if you ask me." +#endif + +#define CARL9170_TX_STATUS_NUM (CARL9170_RSP_TX_STATUS_NUM) +#define CARL9170_INT_RQ_CACHES 16 +#define AR9170_INT_MAGIC_HEADER_SIZE 12 +#define CARL9170_TBTT_DELTA (CARL9170_PRETBTT_KUS + 1) + +#define CARL9170_GPIO_MASK (AR9170_GPIO_PORT_WPS_BUTTON_PRESSED) + +#ifdef CONFIG_CARL9170FW_VIFS_NUM +#define CARL9170_INTF_NUM (1 + CONFIG_CARL9170FW_VIFS_NUM) +#else +#define CARL9170_INTF_NUM (1) +#endif /* CONFIG_CARL9170FW_VIFS_NUM */ + +#define CONFIG_CARL9170FW_BACK_REQS_NUM 4 + +static inline void __config_check(void) +{ + BUILD_BUG_ON(!CARL9170_TX_STATUS_NUM); + BUILD_BUG_ON(CARL9170_INTF_NUM < 1); + BUILD_BUG_ON(CARL9170_INTF_NUM >= AR9170_MAX_VIRTUAL_MAC); +} + +#endif /* __CARL9170FW_CONFIG_H */ diff --git a/carl9170fw/carlfw/include/dma.h b/carl9170fw/carlfw/include/dma.h new file mode 100644 index 0000000..8f3a9df --- /dev/null +++ b/carl9170fw/carlfw/include/dma.h @@ -0,0 +1,349 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * This module contains DMA descriptor related definitions. + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_DMA_H +#define __CARL9170FW_DMA_H + +#include "config.h" +#include "types.h" +#include "compiler.h" +#include "hw.h" +#include "ieee80211.h" +#include "wlan.h" + +struct dma_desc { + volatile uint16_t status; /* Descriptor status */ + volatile uint16_t ctrl; /* Descriptor control */ + volatile uint16_t dataSize; /* Data size */ + volatile uint16_t totalLen; /* Total length */ + struct dma_desc *lastAddr; /* Last address of this chain */ + union { + uint8_t *_dataAddr; /* Data buffer address */ + void *dataAddr; + } __packed; + struct dma_desc *nextAddr; /* Next TD address */ +} __packed __aligned(4); + +/* Up, Dn, 5x Tx, retry, Rx, [USB Int], (CAB), FW */ +#define AR9170_TERMINATOR_NUMBER_B 10 + +#define AR9170_TERMINATOR_NUMBER_INT 1 + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE +#define AR9170_TERMINATOR_NUMBER_CAB CARL9170_INTF_NUM +#else +#define AR9170_TERMINATOR_NUMBER_CAB 0 +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +#define AR9170_TERMINATOR_NUMBER (AR9170_TERMINATOR_NUMBER_B + \ + AR9170_TERMINATOR_NUMBER_INT + \ + AR9170_TERMINATOR_NUMBER_CAB) + +#define AR9170_BLOCK_SIZE (256 + 64) + +#define AR9170_DESCRIPTOR_SIZE (sizeof(struct dma_desc)) + +struct ar9170_tx_ba_frame { + struct ar9170_tx_hwdesc hdr; + struct ieee80211_ba ba; +} __packed; + +struct carl9170_tx_ba_superframe { + struct carl9170_tx_superdesc s; + struct ar9170_tx_ba_frame f; +} __packed; + +struct ar9170_tx_null_frame { + struct ar9170_tx_hwdesc hdr; + struct ieee80211_hdr null; +} __packed; + +struct carl9170_tx_null_superframe { + struct carl9170_tx_superdesc s; + struct ar9170_tx_null_frame f; +} __packed; + +#define CARL9170_BA_BUFFER_LEN (__roundup(sizeof(struct carl9170_tx_ba_superframe), 16)) +#define CARL9170_RSP_BUFFER_LEN AR9170_BLOCK_SIZE + +struct carl9170_sram_reserved { + union { + uint32_t buf[CARL9170_BA_BUFFER_LEN / sizeof(uint32_t)]; + struct carl9170_tx_ba_superframe ba; + } ba; + + union { + uint32_t buf[CARL9170_MAX_CMD_LEN / sizeof(uint32_t)]; + struct carl9170_cmd cmd; + +#ifdef CONFIG_CARL9170FW_WOL + struct carl9170_tx_null_superframe null; +#endif /* CONFIG_CARL9170FW_WOL */ + } cmd; + + union { + uint32_t buf[CARL9170_RSP_BUFFER_LEN / sizeof(uint32_t)]; + struct carl9170_rsp rsp; + } rsp; + + union { + uint32_t buf[CARL9170_INTF_NUM][AR9170_MAC_BCN_LENGTH_MAX / sizeof(uint32_t)]; + } bcn; +}; + +/* + * Memory layout in RAM: + * + * 0x100000 +-- + * | terminator descriptors (dma_desc) + * | - Up (to USB host) + * | - Down (from USB host) + * | - TX (5x, to wifi) + * | - AMPDU TX retry + * | - RX (from wifi) + * | - CAB Queue + * | - FW cmd & req descriptor + * | - BlockAck descriptor + * | total: AR9170_TERMINATOR_NUMBER + * +-- + * | block descriptors (dma_desc) + * | (AR9170_BLOCK_NUMBER) + * AR9170_BLOCK_BUFFER_BASE +-- align to multiple of 64 + * | block buffers (AR9170_BLOCK_SIZE each) + * | (AR9170_BLOCK_NUMBER) + * approx. 0x117c00 +-- + * | BA buffer (128 bytes) + * +-- + * | CMD buffer (128 bytes) + * | - used as NULLFRAME buffer (128 bytes) for WOL + * +-- + * | RSP buffer (320 bytes) + * +-- + * | BEACON buffer (256 bytes) + * +-- + * | unaccounted space / padding + * +-- + * 0x18000 + */ + +#define CARL9170_SRAM_RESERVED (sizeof(struct carl9170_sram_reserved)) + +#define AR9170_FRAME_MEMORY_SIZE (AR9170_SRAM_SIZE - CARL9170_SRAM_RESERVED) + +#define BLOCK_ALIGNMENT 64 + +#define NONBLOCK_DESCRIPTORS_SIZE \ + (AR9170_DESCRIPTOR_SIZE * (AR9170_TERMINATOR_NUMBER)) + +#define NONBLOCK_DESCRIPTORS_SIZE_ALIGNED \ + (ALIGN(NONBLOCK_DESCRIPTORS_SIZE, BLOCK_ALIGNMENT)) + +#define AR9170_BLOCK_NUMBER ((AR9170_FRAME_MEMORY_SIZE - NONBLOCK_DESCRIPTORS_SIZE_ALIGNED) / \ + (AR9170_BLOCK_SIZE + AR9170_DESCRIPTOR_SIZE)) + +struct ar9170_data_block { + uint8_t data[AR9170_BLOCK_SIZE]; +}; + +struct ar9170_dma_memory { + struct dma_desc terminator[AR9170_TERMINATOR_NUMBER]; + struct dma_desc block[AR9170_BLOCK_NUMBER]; + struct ar9170_data_block data[AR9170_BLOCK_NUMBER] __aligned(BLOCK_ALIGNMENT); + struct carl9170_sram_reserved reserved __aligned(BLOCK_ALIGNMENT); +}; + +extern struct ar9170_dma_memory dma_mem; + +#define AR9170_DOWN_BLOCK_RATIO 2 +#define AR9170_RX_BLOCK_RATIO 1 +/* Tx 16*2 = 32 packets => 32*(5*320) */ +#define AR9170_TX_BLOCK_NUMBER (AR9170_BLOCK_NUMBER * AR9170_DOWN_BLOCK_RATIO / \ + (AR9170_RX_BLOCK_RATIO + AR9170_DOWN_BLOCK_RATIO)) +#define AR9170_RX_BLOCK_NUMBER (AR9170_BLOCK_NUMBER - AR9170_TX_BLOCK_NUMBER) + +/* Error code */ +#define AR9170_ERR_FS_BIT 1 +#define AR9170_ERR_LS_BIT 2 +#define AR9170_ERR_OWN_BITS 3 +#define AR9170_ERR_DATA_SIZE 4 +#define AR9170_ERR_TOTAL_LEN 5 +#define AR9170_ERR_DATA 6 +#define AR9170_ERR_SEQ 7 +#define AR9170_ERR_LEN 8 + +/* Status bits definitions */ +/* Own bits definitions */ +#define AR9170_OWN_BITS 0x3 +#define AR9170_OWN_BITS_S 0 +#define AR9170_OWN_BITS_SW 0x0 +#define AR9170_OWN_BITS_HW 0x1 +#define AR9170_OWN_BITS_SE 0x2 + +/* Control bits definitions */ +#define AR9170_CTRL_TXFAIL 1 +#define AR9170_CTRL_BAFAIL 2 +#define AR9170_CTRL_FAIL (AR9170_CTRL_TXFAIL | AR9170_CTRL_BAFAIL) + +/* First segament bit */ +#define AR9170_CTRL_LS_BIT 0x100 +/* Last segament bit */ +#define AR9170_CTRL_FS_BIT 0x200 + +struct dma_queue { + struct dma_desc *head; + struct dma_desc *terminator; +}; + +#define DESC_PAYLOAD(a) ((void *)a->dataAddr) +#define DESC_PAYLOAD_OFF(a, offset) ((void *)((unsigned long)(a->_dataAddr) + offset)) + +struct dma_desc *dma_unlink_head(struct dma_queue *queue); +void dma_init_descriptors(void); +void dma_reclaim(struct dma_queue *q, struct dma_desc *desc); +void dma_put(struct dma_queue *q, struct dma_desc *desc); + +static inline __inline bool is_terminator(struct dma_queue *q, struct dma_desc *desc) +{ + return q->terminator == desc; +} + +static inline __inline bool queue_empty(struct dma_queue *q) +{ + return q->head == q->terminator; +} + +/* + * Get a completed packet with # descriptors. Return the first + * descriptor and pointer the head directly by lastAddr->nextAddr + */ +static inline __inline struct dma_desc *dma_dequeue_bits(struct dma_queue *q, + uint16_t bits) +{ + struct dma_desc *desc = NULL; + + if ((q->head->status & AR9170_OWN_BITS) == bits) + desc = dma_unlink_head(q); + + return desc; +} + +static inline __inline struct dma_desc *dma_dequeue_not_bits(struct dma_queue *q, + uint16_t bits) +{ + struct dma_desc *desc = NULL; + + /* AR9170_OWN_BITS_HW will be filtered out here too. */ + if ((q->head->status & AR9170_OWN_BITS) != bits) + desc = dma_unlink_head(q); + + return desc; +} + +#define for_each_desc_bits(desc, queue, bits) \ + while ((desc = dma_dequeue_bits(queue, bits))) + +#define for_each_desc_not_bits(desc, queue, bits) \ + while ((desc = dma_dequeue_not_bits(queue, bits))) + +#define for_each_desc(desc, queue) \ + while ((desc = dma_unlink_head(queue))) + +#define __for_each_desc_bits(desc, queue, bits) \ + for (desc = (queue)->head; \ + (desc != (queue)->terminator && \ + (desc->status & AR9170_OWN_BITS) == bits); \ + desc = desc->lastAddr->nextAddr) + +#define __while_desc_bits(desc, queue, bits) \ + for (desc = (queue)->head; \ + (!queue_empty(queue) && \ + (desc->status & AR9170_OWN_BITS) == bits); \ + desc = (queue)->head) + +#define __for_each_desc_continue(desc, queue) \ + for (; desc != (queue)->terminator; \ + desc = (desc)->lastAddr->nextAddr) + +#define __for_each_desc(desc, queue) \ + for (desc = (queue)->head; \ + desc != (queue)->terminator; \ + desc = (desc)->lastAddr->nextAddr) + +#define __for_each_desc_safe(desc, tmp, queue) \ + for (desc = (queue)->head, tmp = desc->lastAddr->nextAddr; \ + desc != (queue)->terminator; \ + desc = tmp, tmp = tmp->lastAddr->nextAddr) + +#define __while_subdesc(desc, queue) \ + for (desc = (queue)->head; \ + desc != (queue)->terminator; \ + desc = (desc)->nextAddr) + +static inline __inline unsigned int queue_len(struct dma_queue *q) +{ + struct dma_desc *desc; + unsigned int i = 0; + + __while_subdesc(desc, q) + i++; + + return i; +} + +/* + * rearm a completed packet, so it will be processed agian. + */ +static inline __inline void dma_rearm(struct dma_desc *desc) +{ + /* Set OWN bit to HW */ + desc->status = ((desc->status & (~AR9170_OWN_BITS)) | + AR9170_OWN_BITS_HW); +} + +static inline __inline void dma_fix_downqueue(struct dma_desc *desc) +{ + desc->status = AR9170_OWN_BITS_HW; + desc->ctrl = 0; + desc->dataSize = 0; + desc->totalLen = AR9170_BLOCK_SIZE; + desc->lastAddr = desc; +} + +static inline void __check_desc(void) +{ + struct ar9170_dma_memory mem; + BUILD_BUG_ON(sizeof(struct ar9170_data_block) != AR9170_BLOCK_SIZE); + BUILD_BUG_ON(sizeof(struct dma_desc) != 20); + + BUILD_BUG_ON(sizeof(mem) > AR9170_SRAM_SIZE); + + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, ba.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, cmd.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, rsp.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, bcn.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(sizeof(struct carl9170_tx_null_superframe) > CARL9170_MAX_CMD_LEN); +} + +#endif /* __CARL9170FW_DMA_H */ diff --git a/carl9170fw/carlfw/include/fwdsc.h b/carl9170fw/carlfw/include/fwdsc.h new file mode 100644 index 0000000..936bfed --- /dev/null +++ b/carl9170fw/carlfw/include/fwdsc.h @@ -0,0 +1,50 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware definition + * + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_FWDSC_H +#define __CARL9170FW_FWDSC_H + +#include "config.h" +#include "compiler.h" +#include "types.h" +#include "fwdesc.h" + +struct carl9170_firmware_descriptor { + struct carl9170fw_otus_desc otus; + struct carl9170fw_txsq_desc txsq; +#ifdef CONFIG_CARL9170FW_WOL + struct carl9170fw_wol_desc wol; +#endif /* CONFIG_CARL9170FW_WOL */ + struct carl9170fw_motd_desc motd; + struct carl9170fw_dbg_desc dbg; + struct carl9170fw_last_desc last; +} __packed; + +extern const struct carl9170_firmware_descriptor carl9170fw_desc; + +static inline void __check_fw(void) +{ + BUILD_BUG_ON(sizeof(carl9170fw_desc) & 0x3); + BUILD_BUG_ON(sizeof(carl9170fw_desc) > CARL9170FW_DESC_MAX_LENGTH); +} + +#endif /* __CARL9170FW_FWDSC_H */ diff --git a/carl9170fw/carlfw/include/gpio.h b/carl9170fw/carlfw/include/gpio.h new file mode 100644 index 0000000..97c1927 --- /dev/null +++ b/carl9170fw/carlfw/include/gpio.h @@ -0,0 +1,45 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * GPIO definitions + * + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_GPIO_H +#define __CARL9170FW_GPIO_H + +#include "config.h" +#include "hw.h" +#include "io.h" + +static inline __inline void led_init(void) +{ + set(AR9170_GPIO_REG_PORT_TYPE, 3); +} + +static inline __inline void led_set(const unsigned int ledstate) +{ + set(AR9170_GPIO_REG_PORT_DATA, ledstate); +} + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + +void gpio_timer(void); + +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ +#endif /* __CARL9170FW_GPIO_H */ diff --git a/carl9170fw/carlfw/include/hostif.h b/carl9170fw/carlfw/include/hostif.h new file mode 100644 index 0000000..8f9f139 --- /dev/null +++ b/carl9170fw/carlfw/include/hostif.h @@ -0,0 +1,47 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * HostIF definition + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_HOSTIF_H +#define __CARL9170FW_HOSTIF_H + +#include "config.h" +#include "compiler.h" +#include "types.h" +#include "hw.h" +#include "io.h" + +static inline __inline void down_trigger(void) +{ + set(AR9170_PTA_REG_DN_DMA_TRIGGER, 1); +} + +static inline __inline void up_trigger(void) +{ + set(AR9170_PTA_REG_UP_DMA_TRIGGER, 1); +} + +void handle_host_interface(void); + +#endif /* __CARL9170FW_HOSTIF_H */ diff --git a/carl9170fw/carlfw/include/io.h b/carl9170fw/carlfw/include/io.h new file mode 100644 index 0000000..4ccdc6e --- /dev/null +++ b/carl9170fw/carlfw/include/io.h @@ -0,0 +1,153 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_IO_H +#define __CARL9170FW_IO_H + +#include "config.h" +#include "types.h" +#include "compiler.h" + +static inline __inline uint8_t readb(const volatile void *addr) +{ + return *(const volatile uint8_t *) addr; +} + +static inline __inline uint16_t readw(const volatile void *addr) +{ + return *(const volatile uint16_t *) addr; +} + +static inline __inline volatile void *readp(const volatile void *addr) +{ + return *(volatile void **) addr; +} + +static inline __inline uint32_t readl(const volatile void *addr) +{ + return *(const volatile unsigned int *) addr; +} + +static inline __inline void writeb(volatile void *addr, const volatile uint8_t val) +{ + *(volatile uint8_t *) addr = val; +} + +static inline __inline void writew(volatile void *addr, const volatile uint16_t val) +{ + *(volatile uint16_t *) addr = val; +} + +static inline __inline void writel(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr = val; +} + +static inline __inline void __orl(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr |= val; +} + +static inline __inline void __andl(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr &= val; +} + +static inline __inline void __xorl(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr ^= val; +} + +static inline __inline void __incl(volatile void *addr) +{ + (*(volatile uint32_t *)addr)++; +} + +static inline __inline uint32_t readl_async(const volatile void *addr) +{ + uint32_t i = 0, read, tmp; + + read = readl(addr); + do { + tmp = read; + tmp = readl(addr); + i++; + } while (tmp != read && i <= 10); + + return read; +} + +static inline __inline void set(const volatile uint32_t addr, const volatile uint32_t val) +{ + writel((volatile void *) addr, val); +} + +static inline __inline void orl(volatile uint32_t addr, const volatile uint32_t val) +{ + __orl((volatile void *) addr, val); +} + +static inline __inline void xorl(const volatile uint32_t addr, const volatile uint32_t val) +{ + __xorl((volatile void *) addr, val); +} + +static inline __inline void andl(const volatile uint32_t addr, const volatile uint32_t val) +{ + __andl((volatile void *) addr, val); +} + +static inline __inline void incl(const volatile uint32_t addr) +{ + __incl((volatile void *) addr); +} + +static inline __inline uint32_t get(const volatile uint32_t addr) +{ + return readl((volatile void *) addr); +} + +static inline __inline volatile void *getp(const volatile uint32_t addr) +{ + return readp((const volatile void *) addr); +} + +static inline __inline uint32_t get_async(const volatile uint32_t addr) +{ + return readl_async((const volatile void *) addr); +} + +static inline __inline void setb(const volatile uint32_t addr, const volatile uint8_t val) +{ + writeb((volatile void *) addr, val); +} + +static inline __inline uint8_t getb(const volatile uint32_t addr) +{ + return readb((const volatile void *) addr); +} + +static inline __inline void andb(const volatile uint32_t addr, const volatile uint8_t val) +{ + setb(addr, getb(addr) & val); +} + +static inline __inline void orb(const volatile uint32_t addr, const volatile uint32_t val) +{ + setb(addr, getb(addr) | val); +} + +#endif /* __CARL9170FW_IO_H */ diff --git a/carl9170fw/carlfw/include/printf.h b/carl9170fw/carlfw/include/printf.h new file mode 100644 index 0000000..dd4559e --- /dev/null +++ b/carl9170fw/carlfw/include/printf.h @@ -0,0 +1,107 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * printf and his friends... + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_PRINTF_H +#define __CARL9170FW_PRINTF_H + +#include <stdarg.h> +#include <string.h> +#include "config.h" +#include "carl9170.h" +#include "uart.h" +#include "fwcmd.h" + +#ifdef CONFIG_CARL9170FW_PRINTF +void __attribute__((format (printf, 1, 2))) tfp_printf(const char *fmt, ...); + +#define printf tfp_printf + +#else +void __attribute__((format (printf, 1, 2))) min_printf(const char *fmt, ...); + +#define printf min_printf +#endif /* CONFIG_CARL9170FW_PRINTF */ + +#define PRINT(fmt, args...) \ + do { \ + printf(fmt, ## args); \ + } while (0) + +#define INFO(fmt, args...) PRINT(fmt, ## args) + +#define ERR(fmt, args...) PRINT(CARL9170_ERR_MAGIC fmt, ## args) + +#ifdef CONFIG_CARL9170FW_DEBUG +#define DBG(fmt, args...) PRINT(fmt, ## args) +#else +#define DBG(...) do { } while (0); +#endif + +/* + * NB: even though the MACRO is called "stall". It isn't supposed + * to stall since this will render the device unresponsive, until + * someone pulls the plug. + */ +#define STALL() + +#define BUG(fmt, args...) \ + do { \ + PRINT(CARL9170_BUG_MAGIC" %s()@%d \"" fmt "\"" , \ + __func__, __LINE__, ## args); \ + STALL() \ + } while (0); + +#define BUG_ON(condition) \ + ({ \ + int __ret = !!(condition); \ + if (unlikely(!!(__ret))) \ + BUG(#condition); \ + (__ret); \ + }) + +static inline __inline void putcharacter(const char c __unused) +{ +#ifdef CONFIG_CARL9170FW_DEBUG_USB + usb_putc(c); +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + +#ifdef CONFIG_CARL9170FW_DEBUG_UART + uart_putc(c); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ +} + +static inline __inline void print_hex_dump(const void *buf __unused, int len __unused) +{ +#ifdef CONFIG_CARL9170FW_DEBUG_USB + usb_print_hex_dump(buf, len); +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + +#ifdef CONFIG_CARL9170FW_DEBUG_UART + uart_print_hex_dump(buf, len); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ +} + +#endif /* __CARL9170FW_PRINTF_H */ + diff --git a/carl9170fw/carlfw/include/rf.h b/carl9170fw/carlfw/include/rf.h new file mode 100644 index 0000000..4419bcb --- /dev/null +++ b/carl9170fw/carlfw/include/rf.h @@ -0,0 +1,37 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * RF routine definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_RF_H +#define __CARL9170FW_RF_H + +#include "config.h" + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS +void rf_notify_set_channel(void); +void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp); +void rf_psm(void); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + +#endif /* __CARL9170FW_RF_H */ diff --git a/carl9170fw/carlfw/include/rom.h b/carl9170fw/carlfw/include/rom.h new file mode 100644 index 0000000..922c5c8 --- /dev/null +++ b/carl9170fw/carlfw/include/rom.h @@ -0,0 +1,82 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * ROM layout + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_ROM_H +#define __CARL9170FW_ROM_H + +#include "types.h" +#include "config.h" +#include "compiler.h" +#include "usb.h" +#include "eeprom.h" + +struct ar9170_hwtype { + /* 0x00001370 */ + uint8_t data[4]; + + /* 0x00001374 */ + struct ar9170_led_mode led_mode[AR9170_NUM_LEDS]; + + /* 0x00001378 */ + uint8_t nulldata[2]; + + struct { + /* 0x0000137a */ + struct usb_device_descriptor device_desc; + + /* 0x0000138c */ + uint8_t string0_desc[4]; + + /* 0x00001390 */ + uint8_t string1_desc[32]; + + /* 0x000013b0 */ + uint8_t string2_desc[48]; + + /* 0x000013e0 */ + uint8_t string3_desc[32]; + } usb; +} __packed; + +struct ar9170_rom { + /* 0x00000000 */ + uint32_t *irq_table[2]; + + /* 0x00000008 */ + uint8_t bootcode[4968]; + + /* 0x00001370 */ + struct ar9170_hwtype hw; + + /* 0x00001400 */ + uint8_t data[512]; + + /* eeprom */ + struct ar9170_eeprom sys; +} __packed; + +static const struct ar9170_rom rom __section(eeprom); + +#endif /* __CARL9170FW_ROM_H */ diff --git a/carl9170fw/carlfw/include/timer.h b/carl9170fw/carlfw/include/timer.h new file mode 100644 index 0000000..1c1c6cd --- /dev/null +++ b/carl9170fw/carlfw/include/timer.h @@ -0,0 +1,88 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Clock, Timer & Timing + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_TIMER_H +#define __CARL9170FW_TIMER_H + +#include "config.h" + +enum cpu_clock_t { + AHB_40MHZ_OSC = 0, + AHB_20_22MHZ = 1, + AHB_40_44MHZ = 2, + AHB_80_88MHZ = 3 +}; + +static inline __inline uint32_t get_clock_counter(void) +{ + return (get(AR9170_TIMER_REG_CLOCK_HIGH) << 16) | get(AR9170_TIMER_REG_CLOCK_LOW); +} + +/* + * works only up to 97 secs [44 MHz] or 107 secs for 40 MHz + * Also, the delay wait will be affected by 2.4GHz<->5GHz + * band changes. + */ +static inline __inline bool is_after_msecs(const uint32_t t0, const uint32_t msecs) +{ + return ((get_clock_counter() - t0) / 1000) > (msecs * fw.ticks_per_usec); +} + +/* + * Note: Be careful with [u]delay. They won't service the + * hardware watchdog timer. It might trigger if you + * wait long enough. Also they don't terminate if sec is + * above 97 sec [44MHz] or more than 107 sec [40MHz]. + */ +static inline __inline void delay(const uint32_t msec) +{ + uint32_t t1, t2, dt, wt; + + wt = msec * fw.ticks_per_usec; + + t1 = get_clock_counter(); + while (1) { + t2 = get_clock_counter(); + dt = (t2 - t1) / 1000; + if (dt >= wt) + break; + } +} + +static inline __inline void udelay(const uint32_t usec) +{ + uint32_t t1, t2, dt; + + t1 = get_clock_counter(); + while (1) { + t2 = get_clock_counter(); + dt = (t2 - t1); + if (dt >= (usec * fw.ticks_per_usec)) + break; + } +} + +void clock_set(enum cpu_clock_t _clock, bool on); +#endif /* __CARL9170FW_TIMER_H */ diff --git a/carl9170fw/carlfw/include/uart.h b/carl9170fw/carlfw/include/uart.h new file mode 100644 index 0000000..1cfa29d --- /dev/null +++ b/carl9170fw/carlfw/include/uart.h @@ -0,0 +1,37 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * UART functions definition + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_UART_H +#define __CARL9170FW_UART_H + +#include "config.h" + +#ifdef CONFIG_CARL9170FW_DEBUG_UART +void uart_putc(const char c); +void uart_print_hex_dump(const void *buf, const int len); +void uart_init(void); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ + +#endif /* __CARL9170FW_UART_H */ diff --git a/carl9170fw/carlfw/include/usb.h b/carl9170fw/carlfw/include/usb.h new file mode 100644 index 0000000..cea1d4d --- /dev/null +++ b/carl9170fw/carlfw/include/usb.h @@ -0,0 +1,191 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * USB definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_USB_H +#define __CARL9170FW_USB_H + +#include "config.h" +#include "types.h" +#include "io.h" +#include "hw.h" +#include "ch9.h" + +struct ar9170_usb_config { + struct usb_config_descriptor cfg; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep[AR9170_USB_NUM_EXTRA_EP]; +} __packed; + +static inline __inline bool usb_detect_highspeed(void) +{ + return !!(getb(AR9170_USB_REG_MAIN_CTRL) & + AR9170_USB_MAIN_CTRL_HIGHSPEED); +} + +static inline __inline bool usb_configured(void) +{ + return !!(getb(AR9170_USB_REG_DEVICE_ADDRESS) & + AR9170_USB_DEVICE_ADDRESS_CONFIGURE); +} + +static inline __inline void usb_enable_remote_wakeup(void) +{ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP); +} + +static inline __inline void usb_disable_remote_wakeup(void) +{ + andb(AR9170_USB_REG_MAIN_CTRL, ~AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP); +} + +static inline __inline void usb_enable_global_int(void) +{ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT); +} + +static inline __inline void usb_trigger_out(void) +{ + andb(AR9170_USB_REG_INTR_MASK_BYTE_4, + (uint8_t) ~AR9170_USB_INTR_DISABLE_OUT_INT); +} + +static inline __inline void usb_reset_out(void) +{ + orb(AR9170_USB_REG_INTR_MASK_BYTE_4, AR9170_USB_INTR_DISABLE_OUT_INT); +} + +static inline __inline void usb_trigger_in(void) +{ + andb(AR9170_USB_REG_INTR_MASK_BYTE_6, ~AR9170_USB_INTR_DISABLE_IN_INT); +} + +static inline __inline void usb_reset_in(void) +{ + orb(AR9170_USB_REG_INTR_MASK_BYTE_6, AR9170_USB_INTR_DISABLE_IN_INT); +} + +static inline __inline void usb_ep3_xfer_done(void) +{ + orb(AR9170_USB_REG_EP3_BYTE_COUNT_HIGH, 0x08); +} + +static inline __inline void usb_suspend_ack(void) +{ + /* + * uP must do-over everything it should handle + * and do before into the suspend mode + */ + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(2)); +} + +static inline __inline void usb_resume_ack(void) +{ + /* + * uP must do-over everything it should handle + * and do before into the suspend mode + */ + + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(3)); +} + +static inline __inline void usb_reset_ack(void) +{ + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(1)); +} + +static inline __inline void usb_data_out0Byte(void) +{ + andb(AR9170_USB_REG_INTR_SOURCE_7, (uint8_t) ~BIT(7)); +} + +static inline __inline void usb_data_in0Byte(void) +{ + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(6)); +} + +static inline __inline void usb_stop_down_queue(void) +{ + andl(AR9170_USB_REG_DMA_CTL, ~AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE); +} + +static inline __inline void usb_start_down_queue(void) +{ + orl(AR9170_USB_REG_DMA_CTL, AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE); +} + +static inline __inline void usb_clear_input_ep_toggle(unsigned int ep) +{ + andl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), + ~AR9170_USB_EP_IN_TOGGLE); +} + +static inline __inline void usb_set_input_ep_toggle(unsigned int ep) +{ + orl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), + AR9170_USB_EP_IN_TOGGLE); +} + +static inline __inline void usb_clear_output_ep_toggle(unsigned int ep) +{ + andl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1), + ~AR9170_USB_EP_OUT_TOGGLE); +} + +static inline __inline void usb_set_output_ep_toggle(unsigned int ep) +{ + orl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1), + AR9170_USB_EP_OUT_TOGGLE); +} + +static inline void usb_structure_check(void) +{ + BUILD_BUG_ON(sizeof(struct usb_config_descriptor) != USB_DT_CONFIG_SIZE); + BUILD_BUG_ON(sizeof(struct usb_device_descriptor) != USB_DT_DEVICE_SIZE); + BUILD_BUG_ON(sizeof(struct usb_endpoint_descriptor) != USB_DT_ENDPOINT_SIZE); + BUILD_BUG_ON(sizeof(struct usb_interface_descriptor) != USB_DT_INTERFACE_SIZE); +} + +void __noreturn jump_to_bootcode(void); + +void send_cmd_to_host(const uint8_t len, const uint8_t type, + const uint8_t ext, const uint8_t *body); + +void usb_init(void); +void usb_ep0rx(void); +void usb_ep0tx(void); +void usb_ep0setup(void); +void handle_usb(void); + +void usb_timer(void); +void usb_putc(const char c); +void usb_print_hex_dump(const void *buf, int len); + +void usb_init_highspeed_fifo_cfg(void); +void usb_init_fullspeed_fifo_cfg(void); + +void __noreturn start(void); +void __noreturn reboot(void); + +#endif /* __CARL9170FW_USB_H */ diff --git a/carl9170fw/carlfw/include/usb_fifo.h b/carl9170fw/carlfw/include/usb_fifo.h new file mode 100644 index 0000000..5d84c88 --- /dev/null +++ b/carl9170fw/carlfw/include/usb_fifo.h @@ -0,0 +1,244 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * USB definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_USB_FIFO_H +#define __CARL9170FW_USB_FIFO_H + +#include "config.h" + +#define MASK_F0 0xf0 + +/* Block Size define */ +#define BLK512BYTE 1 +#define BLK1024BYTE 2 + +#define BLK64BYTE 1 +#define BLK128BYTE 2 + +/* Block toggle number define */ +#define SINGLE_BLK 1 +#define DOUBLE_BLK 2 +#define TRIBLE_BLK 3 + +/* Endpoint transfer type */ +#define TF_TYPE_ISOCHRONOUS 1 +#define TF_TYPE_BULK 2 +#define TF_TYPE_INTERRUPT 3 + +/* Endpoint or FIFO direction define */ +#define DIRECTION_IN 0 +#define DIRECTION_OUT 1 + +#define HS_C1_I0_A0_EP1_MAX_PACKET 512 +#define HS_C1_I0_A0_EP1_bInterval 0 + +#define HS_C1_I0_A0_EP_NUMBER 0x04 +#define HS_C1_I0_A0_EP_LENGTH (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER) +#define HS_C1_I0_ALT_LENGTH (HS_C1_I0_A0_EP_LENGTH) +#define HS_C1_INTERFACE_LENGTH (HS_C1_I0_ALT_LENGTH) + +#define HS_C1_CONFIG_TOTAL_LENGTH (CONFIG_LENGTH + INTERFACE_LENGTH + HS_C1_INTERFACE_LENGTH) +#define FS_C1_CONFIG_TOTAL_LENGTH (CONFIG_LENGTH + INTERFACE_LENGTH + FS_C1_INTERFACE_LENGTH) + +#define FS_C1_I0_A0_EP1_MAX_PACKET 64 +/* #define FS_C1_I0_A0_EP1_bInterval HS_C1_I0_A0_EP1_bInterval */ + +#define HS_CONFIGURATION_NUMBER 1 +#define FS_CONFIGURATION_NUMBER 1 + +#define fDOUBLE_BUF 1 +#define fDOUBLE_BUF_IN 0 + +#define fFLASH_DISK 0 +#define fENABLE_ISO 0 + +#define HS_C1_INTERFACE_NUMBER 0x01 +#define HS_C1 0x01 +#define HS_C1_iConfiguration 0x00 +#define HS_C1_bmAttribute 0x80 + +#define HS_C1_iMaxPower 0xFA + +/* Interface 0 */ +#define HS_C1_I0_ALT_NUMBER 0X01 +/* AlternateSetting 0 */ +#define HS_C1_I0_A0_bInterfaceNumber 0x00 +#define HS_C1_I0_A0_bAlternateSetting 0x00 +/* JWEI 2003/07/14 */ +#define HS_C1_I0_A0_EP_NUMBER 0x04 +#define HS_C1_I0_A0_bInterfaceClass 0xff +#define HS_C1_I0_A0_bInterfaceSubClass 0x00 +#define HS_C1_I0_A0_bInterfaceProtocol 0x00 +#define HS_C1_I0_A0_iInterface 0x00 + +/* EP 1 */ +#define HS_C1_I0_A0_EP1_BLKSIZE 512 +#define HS_C1_I0_A0_EP1_BLKNO DOUBLE_BLK +#define HS_C1_I0_A0_EP1_DIRECTION DIRECTION_OUT +#define HS_C1_I0_A0_EP1_TYPE TF_TYPE_BULK + +#define HS_C1_I0_A0_EP1_MAX_PACKET 512 +#define HS_C1_I0_A0_EP1_bInterval 0 + +/* EP 2 */ +#define HS_C1_I0_A0_EP2_BLKSIZE 512 +/* JWEI 2003/08/20 */ +#define HS_C1_I0_A0_EP2_BLKNO SINGLE_BLK +#define HS_C1_I0_A0_EP2_DIRECTION DIRECTION_IN +#define HS_C1_I0_A0_EP2_TYPE TF_TYPE_BULK +#define HS_C1_I0_A0_EP2_MAX_PACKET 512 +#define HS_C1_I0_A0_EP2_bInterval 0 + +/* EP 3 */ +#define HS_C1_I0_A0_EP3_BLKSIZE 64 +#define HS_C1_I0_A0_EP3_BLKNO SINGLE_BLK +#define HS_C1_I0_A0_EP3_DIRECTION DIRECTION_IN +#define HS_C1_I0_A0_EP3_TYPE TF_TYPE_INTERRUPT +#define HS_C1_I0_A0_EP3_MAX_PACKET 0x0040 +#define HS_C1_I0_A0_EP3_bInterval 01 + +/* + * Note: HS Bulk type require max pkt size = 512 + * ==> must use Interrupt type for max pkt size = 64 + */ + +/* EP 4 */ +#define HS_C1_I0_A0_EP4_BLKSIZE 64 +#define HS_C1_I0_A0_EP4_BLKNO SINGLE_BLK +#define HS_C1_I0_A0_EP4_DIRECTION DIRECTION_OUT +#define HS_C1_I0_A0_EP4_TYPE TF_TYPE_INTERRUPT +#define HS_C1_I0_A0_EP4_MAX_PACKET 0x0040 +#define HS_C1_I0_A0_EP4_bInterval 01 + +#define HS_C1_I0_A0_EP_LENGTH (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER) +/* EP 1 */ +#define HS_C1_I0_A0_EP1_FIFO_START 0 +#define HS_C1_I0_A0_EP1_FIFO_NO (HS_C1_I0_A0_EP1_BLKNO * HS_C1_I0_A0_EP1_BLKSIZE) +#define HS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP1_BLKNO - 1) << 2) | HS_C1_I0_A0_EP1_TYPE) +#define HS_C1_I0_A0_EP1_FIFO_MAP (((1 - HS_C1_I0_A0_EP1_DIRECTION) << 4) | 1) +#define HS_C1_I0_A0_EP1_MAP (HS_C1_I0_A0_EP1_FIFO_START | (HS_C1_I0_A0_EP1_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP1_DIRECTION))) + +/* EP 2 */ +#define HS_C1_I0_A0_EP2_FIFO_START (uint8_t)(HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO) +#define HS_C1_I0_A0_EP2_FIFO_NO (uint8_t)(HS_C1_I0_A0_EP2_BLKNO * HS_C1_I0_A0_EP2_BLKSIZE) +#define HS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP2_BLKNO - 1) << 2) | HS_C1_I0_A0_EP2_TYPE) +#define HS_C1_I0_A0_EP2_FIFO_MAP (uint8_t)(((1 - HS_C1_I0_A0_EP2_DIRECTION) << 4) | 2) +#define HS_C1_I0_A0_EP2_MAP (uint8_t)(HS_C1_I0_A0_EP2_FIFO_START | (HS_C1_I0_A0_EP2_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP2_DIRECTION))) + +/* EP 3 */ +#define HS_C1_I0_A0_EP3_FIFO_START 14 +#define HS_C1_I0_A0_EP3_FIFO_NO (HS_C1_I0_A0_EP3_BLKNO * HS_C1_I0_A0_EP3_BLKSIZE) +#define HS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP3_BLKNO - 1) << 2) | HS_C1_I0_A0_EP3_TYPE) +#define HS_C1_I0_A0_EP3_FIFO_MAP (uint8_t)(((1 - HS_C1_I0_A0_EP3_DIRECTION) << 4) | 3) +#define HS_C1_I0_A0_EP3_MAP (uint8_t)(HS_C1_I0_A0_EP3_FIFO_START | (HS_C1_I0_A0_EP3_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP3_DIRECTION))) + +/* EP 4 */ +#define HS_C1_I0_A0_EP4_FIFO_START (HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO) +#define HS_C1_I0_A0_EP4_FIFO_NO (HS_C1_I0_A0_EP4_BLKNO * HS_C1_I0_A0_EP4_BLKSIZE) +#define HS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP4_BLKNO - 1) << 2) | HS_C1_I0_A0_EP4_TYPE) +#define HS_C1_I0_A0_EP4_FIFO_MAP (((1 - HS_C1_I0_A0_EP4_DIRECTION) << 4) | 4) +#define HS_C1_I0_A0_EP4_MAP (uint8_t)(HS_C1_I0_A0_EP4_FIFO_START | (HS_C1_I0_A0_EP4_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP4_DIRECTION))) + +/* Configuration 1 */ +#define FS_C1_INTERFACE_NUMBER 0x01 +#define FS_C1 0x01 +#define FS_C1_iConfiguration 0x00 +#define FS_C1_bmAttribute 0x80 +#define FS_C1_iMaxPower 0xfa + +/* Interface 0 */ +#define FS_C1_I0_ALT_NUMBER 0x01 +/* AlternateSetting 0x00 */ +#define FS_C1_I0_A0_bInterfaceNumber 0x00 +#define FS_C1_I0_A0_bAlternateSetting 0x00 +#define FS_C1_I0_A0_EP_NUMBER 0x04 +#define FS_C1_I0_A0_bInterfaceClass 0xff +#define FS_C1_I0_A0_bInterfaceSubClass 0x00 +#define FS_C1_I0_A0_bInterfaceProtocol 0x00 + +/* EP 1 */ +#define FS_C1_I0_A0_EP1_BLKSIZE 512 +/* JWEI 2003/05/19 */ +#define FS_C1_I0_A0_EP1_BLKNO DOUBLE_BLK +#define FS_C1_I0_A0_EP1_DIRECTION DIRECTION_OUT +#define FS_C1_I0_A0_EP1_TYPE TF_TYPE_BULK +#define FS_C1_I0_A0_EP1_MAX_PACKET 64 +#define FS_C1_I0_A0_EP1_bInterval 0 + +/* EP 2 */ +#define FS_C1_I0_A0_EP2_BLKSIZE 512 +/* JWEI 2003/08/20 */ +#define FS_C1_I0_A0_EP2_BLKNO SINGLE_BLK +#define FS_C1_I0_A0_EP2_DIRECTION DIRECTION_IN +#define FS_C1_I0_A0_EP2_TYPE TF_TYPE_BULK +#define FS_C1_I0_A0_EP2_MAX_PACKET 64 +#define FS_C1_I0_A0_EP2_bInterval 0 + +/* EP 3 */ +#define FS_C1_I0_A0_EP3_BLKSIZE 64 +#define FS_C1_I0_A0_EP3_BLKNO SINGLE_BLK +#define FS_C1_I0_A0_EP3_DIRECTION DIRECTION_IN +#define FS_C1_I0_A0_EP3_TYPE TF_TYPE_INTERRUPT +#define FS_C1_I0_A0_EP3_MAX_PACKET 0x0040 +#define FS_C1_I0_A0_EP3_bInterval 1 + +/* EP 4 */ +#define FS_C1_I0_A0_EP4_BLKSIZE 64 +#define FS_C1_I0_A0_EP4_BLKNO SINGLE_BLK +#define FS_C1_I0_A0_EP4_DIRECTION DIRECTION_OUT +#define FS_C1_I0_A0_EP4_TYPE TF_TYPE_BULK +#define FS_C1_I0_A0_EP4_MAX_PACKET 0x0040 +#define FS_C1_I0_A0_EP4_bInterval 0 + +#define FS_C1_I0_A0_EP_LENGTH (EP_LENGTH * FS_C1_I0_A0_EP_NUMBER) +/* EP 1 */ +#define FS_C1_I0_A0_EP1_FIFO_START 0 +#define FS_C1_I0_A0_EP1_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP1_BLKNO * FS_C1_I0_A0_EP1_BLKSIZE) +#define FS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP1_BLKNO - 1) << 2) | FS_C1_I0_A0_EP1_TYPE) +#define FS_C1_I0_A0_EP1_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP1_DIRECTION) << 4) | 1) +#define FS_C1_I0_A0_EP1_MAP (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START | (FS_C1_I0_A0_EP1_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP1_DIRECTION))) + +/* EP 2 */ +#define FS_C1_I0_A0_EP2_FIFO_START (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO) +#define FS_C1_I0_A0_EP2_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP2_BLKNO * FS_C1_I0_A0_EP2_BLKSIZE) +#define FS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP2_BLKNO - 1) << 2) | FS_C1_I0_A0_EP2_TYPE) +#define FS_C1_I0_A0_EP2_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP2_DIRECTION) << 4) | 2) +#define FS_C1_I0_A0_EP2_MAP (uint8_t)(FS_C1_I0_A0_EP2_FIFO_START | (FS_C1_I0_A0_EP2_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP2_DIRECTION))) + +/* EP 3 */ +#define FS_C1_I0_A0_EP3_FIFO_START 14 +#define FS_C1_I0_A0_EP3_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP3_BLKNO * FS_C1_I0_A0_EP3_BLKSIZE) +#define FS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP3_BLKNO - 1) << 2) | FS_C1_I0_A0_EP3_TYPE) +#define FS_C1_I0_A0_EP3_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP3_DIRECTION) << 4) | 3) +#define FS_C1_I0_A0_EP3_MAP (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START | (FS_C1_I0_A0_EP3_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP3_DIRECTION))) + +/* EP 4 */ +#define FS_C1_I0_A0_EP4_FIFO_START (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO) +#define FS_C1_I0_A0_EP4_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP4_BLKNO * FS_C1_I0_A0_EP4_BLKSIZE) +#define FS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP4_BLKNO - 1) << 2) | FS_C1_I0_A0_EP4_TYPE) +#define FS_C1_I0_A0_EP4_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP4_DIRECTION) << 4) | 4) +#define FS_C1_I0_A0_EP4_MAP (uint8_t)(FS_C1_I0_A0_EP4_FIFO_START | (FS_C1_I0_A0_EP4_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP4_DIRECTION))) + +#endif /* __CARL9170FW_USB_FIFO_H */ diff --git a/carl9170fw/carlfw/include/wl.h b/carl9170fw/carlfw/include/wl.h new file mode 100644 index 0000000..dd5b0a8 --- /dev/null +++ b/carl9170fw/carlfw/include/wl.h @@ -0,0 +1,296 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WLAN + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_WLAN_H +#define __CARL9170FW_WLAN_H + +#include "config.h" +#include "carl9170.h" +#include "io.h" + +struct ieee80211_hdr; + +static inline __inline void set_wlan_txq_dma_addr(const unsigned int q, const uint32_t v) +{ + set(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3), v); +} + +static inline __inline void set_wlan_txq_dma_curr_addr(const unsigned int q, const uint32_t v) +{ + set(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3), v); +} + +static inline __inline volatile struct dma_desc *get_wlan_txq_dma_addr(const unsigned int q) +{ + return getp(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3)); +} + +static inline __inline volatile struct dma_desc *get_wlan_txq_addr(const unsigned int q) +{ + return getp(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3)); +} + +static inline __inline volatile struct dma_desc *get_wlan_txq_last_addr(const unsigned int q) +{ + return getp(AR9170_MAC_REG_DMA_TXQ_LAST_ADDR + (q << 2)); +} + +static inline __inline void wlan_trigger(const uint32_t queue_bit) +{ + set(AR9170_MAC_REG_DMA_TRIGGER, queue_bit); +} + +static inline __inline uint8_t ar9170_get_rx_macstatus_status(struct dma_desc *desc) +{ + return *((uint8_t *) DESC_PAYLOAD_OFF(desc->lastAddr, + (unsigned int) desc->lastAddr->dataSize - 1)); +} + +static inline __inline uint8_t ar9170_get_rx_macstatus_error(struct dma_desc *desc) +{ + unsigned int offset; + + if (desc->lastAddr->dataSize == 1) { + while (desc->lastAddr != desc->nextAddr) + desc = desc->nextAddr; + + offset = (unsigned int) (desc->dataSize - 1); + } else { + desc = desc->lastAddr; + offset = desc->dataSize - + (sizeof(struct ar9170_rx_macstatus) - + offsetof(struct ar9170_rx_macstatus, error)); + } + + return *((uint8_t *) DESC_PAYLOAD_OFF(desc, offset)); +} + +static inline __inline struct ieee80211_hdr *ar9170_get_rx_i3e(struct dma_desc *desc) +{ + if (!((ar9170_get_rx_macstatus_status(desc) & + AR9170_RX_STATUS_MPDU) & AR9170_RX_STATUS_MPDU_LAST)) { + return (void *)(DESC_PAYLOAD_OFF(desc, + offsetof(struct ar9170_rx_frame_head, i3e))); + } else { + return (void *)(DESC_PAYLOAD_OFF(desc, + offsetof(struct ar9170_rx_frame_tail, i3e))); + } +} + +static inline __inline struct ar9170_rx_head *ar9170_get_rx_head(struct dma_desc *desc) +{ + if (!((ar9170_get_rx_macstatus_status(desc) & + AR9170_RX_STATUS_MPDU) & AR9170_RX_STATUS_MPDU_LAST)) { + return (void *)((uint8_t *)DESC_PAYLOAD(desc) + + offsetof(struct ar9170_rx_frame_head, phy_head)); + } else { + return (void *) NULL; + } +} + +static inline __inline uint32_t ar9170_rx_to_phy(struct dma_desc *rx) +{ + struct ar9170_tx_hw_phy_control phy; + struct ar9170_rx_head *head; + uint8_t mac_status; + + phy.set = 0; + + head = ar9170_get_rx_head(rx); + if (!head) + return le32_to_cpu(phy.set); + + mac_status = ar9170_get_rx_macstatus_status(rx); + + phy.modulation = mac_status & AR9170_RX_STATUS_MODULATION; + phy.chains = AR9170_TX_PHY_TXCHAIN_1; + + switch (phy.modulation) { + case AR9170_RX_STATUS_MODULATION_CCK: + if (mac_status & AR9170_RX_STATUS_SHORT_PREAMBLE) + phy.preamble = 1; + + switch (head->plcp[0]) { + case AR9170_RX_PHY_RATE_CCK_2M: + phy.mcs = AR9170_TX_PHY_RATE_CCK_2M; + break; + + case AR9170_RX_PHY_RATE_CCK_5M: + phy.mcs = AR9170_TX_PHY_RATE_CCK_5M; + break; + + case AR9170_RX_PHY_RATE_CCK_11M: + phy.mcs = AR9170_TX_PHY_RATE_CCK_11M; + break; + + case AR9170_RX_PHY_RATE_CCK_1M: + default: + phy.mcs = AR9170_TX_PHY_RATE_CCK_1M; + break; + + } + break; + + case AR9170_RX_STATUS_MODULATION_DUPOFDM: + case AR9170_RX_STATUS_MODULATION_OFDM: + phy.mcs = head->plcp[0] & 0xf; + break; + + case AR9170_RX_STATUS_MODULATION_HT: + if (head->plcp[3] & 0x80) + phy.bandwidth = 2; + + if (head->plcp[6] & 0x80) + phy.short_gi = 1; + + /* TODO: Enable both chains for MCS > 7 */ + phy.mcs = head->plcp[6] & 0x7; + break; + } + + return le32_to_cpu(phy.set); +} + +static inline __inline unsigned int ar9170_get_rx_mpdu_len(struct dma_desc *desc) +{ + /* + * WARNING: you have to check the error bits in macstatus first! + */ + + unsigned int mpdu_len = desc->totalLen; + + mpdu_len -= sizeof(struct ar9170_rx_macstatus); + + switch (ar9170_get_rx_macstatus_status(desc) & AR9170_RX_STATUS_MPDU) { + case AR9170_RX_STATUS_MPDU_LAST: + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + break; + + case AR9170_RX_STATUS_MPDU_SINGLE: + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + + case AR9170_RX_STATUS_MPDU_FIRST: + mpdu_len -= sizeof(struct ar9170_rx_head); + break; + + case AR9170_RX_STATUS_MPDU_MIDDLE: + default: + break; + } + + return mpdu_len; +} + +static inline __inline bool ar9170_tx_length_check(const uint16_t len) +{ + return len > (sizeof(struct carl9170_tx_superframe) + 24 + + FCS_LEN); +} + +static inline __inline struct carl9170_tx_superframe *get_super(struct dma_desc *desc) +{ + return container_of(DESC_PAYLOAD(desc), struct carl9170_tx_superframe, + f); +} + +static inline __inline struct carl9170_tx_superframe *__get_super(struct dma_desc *desc) +{ + return DESC_PAYLOAD(desc); +} + +static inline __inline void hide_super(struct dma_desc *desc) +{ + desc->dataAddr = (uint8_t *) + (((unsigned long)(DESC_PAYLOAD(desc)) + + offsetof(struct carl9170_tx_superframe, f))); + + desc->dataSize -= sizeof(struct carl9170_tx_superdesc); + desc->totalLen -= sizeof(struct carl9170_tx_superdesc); +} + +static inline __inline void unhide_super(struct dma_desc *desc) +{ + desc->dataAddr = (uint8_t *) get_super(desc); + desc->dataSize += sizeof(struct carl9170_tx_superdesc); + desc->totalLen += sizeof(struct carl9170_tx_superdesc); +} + +static inline __inline __hot void read_tsf(uint32_t *tsf) +{ + /* + * "According to the [hardware] documentation: + * > when TSF_LOW is read, TSF_HI is automatically concurrently + * > copied into a temporary register so that an immediate read + * > of TSF_HI will get the value that was present when TSF_LOW + * > was read. " + * + * (David H. Lynch Jr. - mail from 2010-05-22) + * http://permalink.gmane.org/gmane.linux.kernel.wireless.general/51249 + */ + + tsf[0] = get(AR9170_MAC_REG_TSF_L); + tsf[1] = get(AR9170_MAC_REG_TSF_H); +} + +/* This function will only work on uint32_t-aligned pointers! */ +static inline bool compare_ether_address(const void *_d0, const void *_d1) +{ + const uint32_t *d0 = _d0; + const uint32_t *d1 = _d1; + + /* BUG_ON((unsigned long)d0 & 3 || (unsigned long)d1 & 3)) */ + return !((d0[0] ^ d1[0]) | (unsigned short)(d0[1] ^ d1[1])); +} + +void wlan_tx(struct dma_desc *desc); +void wlan_tx_fw(struct carl9170_tx_superdesc *super, fw_desc_callback_t cb); +void wlan_timer(void); +void handle_wlan(void); + +void wlan_cab_flush_queue(const unsigned int vif); +void wlan_modify_beacon(const unsigned int vif, + const unsigned int bcn_addr, + const unsigned int bcn_len); + +void wlan_tx_complete(struct carl9170_tx_superframe *super, bool txs); +void wlan_prepare_wol(void); + +static inline void __check_wlantx(void) +{ + BUILD_BUG_ON(CARL9170_TX_SUPERDESC_LEN & 3); + BUILD_BUG_ON(sizeof(struct carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_superframe) != CARL9170_TX_SUPERFRAME_LEN); + BUILD_BUG_ON((offsetof(struct carl9170_tx_superframe, f) & 3) != 0); + BUILD_BUG_ON(offsetof(struct _carl9170_tx_superframe, f) != + (offsetof(struct _carl9170_tx_superframe, f))); + BUILD_BUG_ON(sizeof(struct ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN); + BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN); + BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != AR9170_RX_HEAD_LEN); + BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != AR9170_RX_PHYSTATUS_LEN); + BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != AR9170_RX_MACSTATUS_LEN); +} + +#endif /* __CARL9170FW_WLAN_H */ diff --git a/carl9170fw/carlfw/include/wol.h b/carl9170fw/carlfw/include/wol.h new file mode 100644 index 0000000..829400a --- /dev/null +++ b/carl9170fw/carlfw/include/wol.h @@ -0,0 +1,67 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WakeUp on WLAN definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CARL9170FW_WOL_H +#define __CARL9170FW_WOL_H + +#include "config.h" +#include "compiler.h" +#include "types.h" + +#include "fwcmd.h" + +#ifdef CONFIG_CARL9170FW_WOL + +struct ieee80211_hdr; + +void wol_prepare(void); +void wol_janitor(void); +void wol_rx(const unsigned int rx_filter __unused, + const struct ieee80211_hdr *hdr __unused, + const unsigned int len __unused); +void wol_cmd(const struct carl9170_wol_cmd *cmd); + +#else + +static inline void wol_cmd(const struct carl9170_wol_cmd *cmd __unused) +{ +} + +static inline void wol_prepare(void) +{ +} + +static inline void wol_janitor(void) +{ +} + +static inline void wol_rx(const unsigned int rx_filter __unused, + const struct ieee80211_hdr *hdr __unused, + const unsigned int len __unused) +{ +} +#endif /* CONFIG_CARL9170FW_WOL */ + +#endif /* __CARL9170FW_CMD_H */ diff --git a/carl9170fw/carlfw/src/ashlsi3.S b/carl9170fw/carlfw/src/ashlsi3.S new file mode 100644 index 0000000..593d902 --- /dev/null +++ b/carl9170fw/carlfw/src/ashlsi3.S @@ -0,0 +1,193 @@ +/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file 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; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +!! libgcc routines for the Renesas / SuperH SH CPUs. +!! Contributed by Steve Chamberlain. +!! sac@cygnus.com + +!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines +!! recoded in assembly by Toshiyasu Morita +!! tm@netcom.com + +/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and + ELF local label prefixes by J"orn Rennecke + amylaar@cygnus.com */ + +! +! __ashlsi3 +! +! Entry: +! +! r4: Value to shift +! r5: Shifts +! +! Exit: +! +! r0: Result +! +! Destroys: +! +! (none) +! + .global ___ashlsi3 + + .align 2 +___ashlsi3: + mov #31,r0 + and r0,r5 + mova ashlsi3_table,r0 + mov.b @(r0,r5),r5 +#ifdef __sh1__ + add r5,r0 + jmp @r0 +#else + braf r5 +#endif + mov r4,r0 + + .align 2 +ashlsi3_table: + .byte ashlsi3_0-ashlsi3_table + .byte ashlsi3_1-ashlsi3_table + .byte ashlsi3_2-ashlsi3_table + .byte ashlsi3_3-ashlsi3_table + .byte ashlsi3_4-ashlsi3_table + .byte ashlsi3_5-ashlsi3_table + .byte ashlsi3_6-ashlsi3_table + .byte ashlsi3_7-ashlsi3_table + .byte ashlsi3_8-ashlsi3_table + .byte ashlsi3_9-ashlsi3_table + .byte ashlsi3_10-ashlsi3_table + .byte ashlsi3_11-ashlsi3_table + .byte ashlsi3_12-ashlsi3_table + .byte ashlsi3_13-ashlsi3_table + .byte ashlsi3_14-ashlsi3_table + .byte ashlsi3_15-ashlsi3_table + .byte ashlsi3_16-ashlsi3_table + .byte ashlsi3_17-ashlsi3_table + .byte ashlsi3_18-ashlsi3_table + .byte ashlsi3_19-ashlsi3_table + .byte ashlsi3_20-ashlsi3_table + .byte ashlsi3_21-ashlsi3_table + .byte ashlsi3_22-ashlsi3_table + .byte ashlsi3_23-ashlsi3_table + .byte ashlsi3_24-ashlsi3_table + .byte ashlsi3_25-ashlsi3_table + .byte ashlsi3_26-ashlsi3_table + .byte ashlsi3_27-ashlsi3_table + .byte ashlsi3_28-ashlsi3_table + .byte ashlsi3_29-ashlsi3_table + .byte ashlsi3_30-ashlsi3_table + .byte ashlsi3_31-ashlsi3_table + +ashlsi3_6: + shll2 r0 +ashlsi3_4: + shll2 r0 +ashlsi3_2: + rts + shll2 r0 + +ashlsi3_7: + shll2 r0 +ashlsi3_5: + shll2 r0 +ashlsi3_3: + shll2 r0 +ashlsi3_1: + rts + shll r0 + +ashlsi3_14: + shll2 r0 +ashlsi3_12: + shll2 r0 +ashlsi3_10: + shll2 r0 +ashlsi3_8: + rts + shll8 r0 + +ashlsi3_15: + shll2 r0 +ashlsi3_13: + shll2 r0 +ashlsi3_11: + shll2 r0 +ashlsi3_9: + shll8 r0 + rts + shll r0 + +ashlsi3_22: + shll2 r0 +ashlsi3_20: + shll2 r0 +ashlsi3_18: + shll2 r0 +ashlsi3_16: + rts + shll16 r0 + +ashlsi3_23: + shll2 r0 +ashlsi3_21: + shll2 r0 +ashlsi3_19: + shll2 r0 +ashlsi3_17: + shll16 r0 + rts + shll r0 + +ashlsi3_30: + shll2 r0 +ashlsi3_28: + shll2 r0 +ashlsi3_26: + shll2 r0 +ashlsi3_24: + shll16 r0 + rts + shll8 r0 + +ashlsi3_31: + shll2 r0 +ashlsi3_29: + shll2 r0 +ashlsi3_27: + shll2 r0 +ashlsi3_25: + shll16 r0 + shll8 r0 + rts + shll r0 + +ashlsi3_0: + rts + nop diff --git a/carl9170fw/carlfw/src/cam.c b/carl9170fw/carlfw/src/cam.c new file mode 100644 index 0000000..7c0e61c --- /dev/null +++ b/carl9170fw/carlfw/src/cam.c @@ -0,0 +1,161 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Security Engine + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "cam.h" + +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE +static void disable_cam_user(const uint16_t userId) +{ + if (userId <= 31) + andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (~((uint32_t) 1 << userId))); + else if (userId <= 63) + andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (~((uint32_t) 1 << (userId - 32)))); +} + +static void enable_cam_user(const uint16_t userId) +{ + if (userId <= 31) + orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (((uint32_t) 1) << userId)); + else if (userId <= 63) + orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (((uint32_t) 1) << (userId - 32))); +} + +static void wait_for_cam_read_ready(void) +{ + while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_READ_PENDING) == 0) { + /* + * wait + */ + } +} + +static void wait_for_cam_write_ready(void) +{ + while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_WRITE_PENDING) == 0) { + /* + * wait some more + */ + } +} + +static void HW_CAM_Avail(void) +{ + uint32_t tmpValue; + + do { + tmpValue = get(AR9170_MAC_REG_CAM_MODE); + } while (tmpValue & AR9170_MAC_CAM_HOST_PENDING); +} + +static void HW_CAM_Write128(const uint32_t address, const uint32_t *data) +{ + HW_CAM_Avail(); + + set(AR9170_MAC_REG_CAM_DATA0, data[0]); + set(AR9170_MAC_REG_CAM_DATA1, data[1]); + set(AR9170_MAC_REG_CAM_DATA2, data[2]); + set(AR9170_MAC_REG_CAM_DATA3, data[3]); + + set(AR9170_MAC_REG_CAM_ADDR, address | AR9170_MAC_CAM_ADDR_WRITE); + + wait_for_cam_write_ready(); +} + +static void HW_CAM_Read128(const uint32_t address, uint32_t *data) +{ + + HW_CAM_Avail(); + set(AR9170_MAC_REG_CAM_ADDR, address); + + wait_for_cam_read_ready(); + HW_CAM_Avail(); + data[0] = get(AR9170_MAC_REG_CAM_DATA0); + data[1] = get(AR9170_MAC_REG_CAM_DATA1); + data[2] = get(AR9170_MAC_REG_CAM_DATA2); + data[3] = get(AR9170_MAC_REG_CAM_DATA3); +} + +void set_key(const struct carl9170_set_key_cmd *key) +{ + uint32_t data[4]; + uint16_t row, wordId, nibbleId, i; + + if (key->user > (AR9170_CAM_MAX_USER + 3)) + return ; + + if (key->keyId > 1) + return ; + + /* Disable Key */ + disable_cam_user(key->user); + + /* Set encrypt type */ + if (key->user >= AR9170_CAM_MAX_USER) { + /* default */ + row = DEFAULT_ENCRY_TYPE; + wordId = 0; + nibbleId = (key->user - AR9170_CAM_MAX_USER) & 0x7; + } else { + row = ENCRY_TYPE_START_ADDR + (key->user >> 5); + wordId = (key->user >> 3) & 0x3; + nibbleId = key->user & 0x7; + } + + HW_CAM_Read128(row, data); + data[wordId] &= (~(0xf << ((uint32_t) nibbleId * 4))); + data[wordId] |= (key->type << ((uint32_t) nibbleId * 4)); + HW_CAM_Write128(row, data); + + /* Set MAC address */ + if (key->user < AR9170_CAM_MAX_USER) { + uint16_t byteId; + wordId = (key->user >> 2) & 0x3; + byteId = key->user & 0x3; + row = (key->user >> 4) * 6; + + for (i = 0; i < 6; i++) { + HW_CAM_Read128(row + i, data); + data[wordId] &= (~(0xff << ((uint32_t) byteId * 8))); + data[wordId] |= (key->macAddr[i] << ((uint32_t) byteId * 8)); + HW_CAM_Write128(row + i, data); + } + } + + /* Set key */ + row = KEY_START_ADDR + (key->user * 2) + key->keyId; + + HW_CAM_Write128(row, key->key); + + /* Enable Key */ + enable_cam_user(key->user); +} + +void disable_key(const struct carl9170_disable_key_cmd *key) +{ + disable_cam_user(key->user); +} + +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ diff --git a/carl9170fw/carlfw/src/cmd.c b/carl9170fw/carlfw/src/cmd.c new file mode 100644 index 0000000..5382083 --- /dev/null +++ b/carl9170fw/carlfw/src/cmd.c @@ -0,0 +1,154 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Code to handle commands from the host driver. + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "io.h" +#include "cam.h" +#include "rf.h" +#include "printf.h" +#include "timer.h" +#include "wl.h" +#include "wol.h" + +void handle_cmd(struct carl9170_rsp *resp) +{ + struct carl9170_cmd *cmd = &dma_mem.reserved.cmd.cmd; + unsigned int i; + + /* copies cmd, len and extra fields */ + resp->hdr.len = cmd->hdr.len; + resp->hdr.cmd = cmd->hdr.cmd; + resp->hdr.ext = cmd->hdr.ext; + resp->hdr.seq |= cmd->hdr.seq; + + switch (cmd->hdr.cmd & ~CARL9170_CMD_ASYNC_FLAG) { + case CARL9170_CMD_RREG: + for (i = 0; i < (cmd->hdr.len / 4); i++) + resp->rreg_res.vals[i] = get(cmd->rreg.regs[i]); + break; + + case CARL9170_CMD_WREG: + resp->hdr.len = 0; + for (i = 0; i < (cmd->hdr.len / 8); i++) + set(cmd->wreg.regs[i].addr, cmd->wreg.regs[i].val); + break; + + case CARL9170_CMD_ECHO: + memcpy(resp->echo.vals, cmd->echo.vals, cmd->hdr.len); + break; + + case CARL9170_CMD_SWRST: +#ifdef CONFIG_CARL9170FW_FW_MAC_RESET + /* + * Command has no payload, so the response + * has no payload either. + * resp->hdr.len = 0; + */ + fw.wlan.mac_reset = CARL9170_MAC_RESET_FORCE; +#endif /* CONFIG_CARL9170FW_FW_MAC_RESET */ + break; + + case CARL9170_CMD_REBOOT: + /* + * resp->len = 0; + */ + fw.reboot = 1; + break; + + case CARL9170_CMD_READ_TSF: + resp->hdr.len = 8; + read_tsf((uint32_t *)resp->tsf.tsf); + break; + + case CARL9170_CMD_RX_FILTER: + resp->hdr.len = 0; + fw.wlan.rx_filter = cmd->rx_filter.rx_filter; + break; + + case CARL9170_CMD_WOL: + wol_cmd(&cmd->wol); + break; + + case CARL9170_CMD_TALLY: + resp->hdr.len = sizeof(struct carl9170_tally_rsp); + memcpy(&resp->tally, &fw.tally, sizeof(struct carl9170_tally_rsp)); + resp->tally.tick = fw.ticks_per_usec; + memset(&fw.tally, 0, sizeof(struct carl9170_tally_rsp)); + break; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + case CARL9170_CMD_BCN_CTRL: + resp->hdr.len = 0; + + if (cmd->bcn_ctrl.mode & CARL9170_BCN_CTRL_CAB_TRIGGER) { + wlan_modify_beacon(cmd->bcn_ctrl.vif_id, + cmd->bcn_ctrl.bcn_addr, cmd->bcn_ctrl.bcn_len); + set(AR9170_MAC_REG_BCN_ADDR, cmd->bcn_ctrl.bcn_addr); + set(AR9170_MAC_REG_BCN_LENGTH, cmd->bcn_ctrl.bcn_len); + set(AR9170_MAC_REG_BCN_CTRL, AR9170_BCN_CTRL_READY); + } else { + wlan_cab_flush_queue(cmd->bcn_ctrl.vif_id); + fw.wlan.cab_flush_trigger[cmd->bcn_ctrl.vif_id] = CARL9170_CAB_TRIGGER_EMPTY; + } + break; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE + case CARL9170_CMD_EKEY: + resp->hdr.len = 0; + set_key(&cmd->setkey); + break; + + case CARL9170_CMD_DKEY: + resp->hdr.len = 0; + disable_key(&cmd->disablekey); + break; +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + case CARL9170_CMD_FREQUENCY: + case CARL9170_CMD_RF_INIT: + rf_cmd(cmd, resp); + break; + + case CARL9170_CMD_FREQ_START: + /* + * resp->hdr.len = 0; + */ + rf_notify_set_channel(); + break; + + case CARL9170_CMD_PSM: + resp->hdr.len = 0; + fw.phy.psm.state = le32_to_cpu(cmd->psm.state); + rf_psm(); + break; +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + default: + BUG("Unknown command %x\n", cmd->hdr.cmd); + break; + } +} diff --git a/carl9170fw/carlfw/src/dma.c b/carl9170fw/carlfw/src/dma.c new file mode 100644 index 0000000..9b83fcf --- /dev/null +++ b/carl9170fw/carlfw/src/dma.c @@ -0,0 +1,255 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * DMA descriptor handling functions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "wl.h" +#include "printf.h" + +struct ar9170_dma_memory dma_mem __section(sram); + +static void copy_dma_desc(struct dma_desc *dst, + struct dma_desc *src) +{ + memcpy(dst, src, sizeof(struct dma_desc)); +} + +static void clear_descriptor(struct dma_desc *d) +{ + d->status = AR9170_OWN_BITS_SW; + d->ctrl = 0; + d->dataSize = 0; + d->totalLen = 0; + d->lastAddr = d; + d->dataAddr = NULL; + d->nextAddr = d; +} + +static void fill_descriptor(struct dma_desc *d, uint16_t size, uint8_t *data) +{ + d->status = AR9170_OWN_BITS_SW; + d->ctrl = 0; + d->dataSize = size; + d->totalLen = 0; + d->lastAddr = d; + d->dataAddr = data; + d->nextAddr = NULL; +} + +static void init_queue(struct dma_queue *q, struct dma_desc *d) +{ + q->head = q->terminator = d; +} + +/* + * - Init up_queue, down_queue, tx_queue[5], rx_queue. + * - Setup descriptors and data buffer address. + * - Ring descriptors rx_queue and down_queue by dma_reclaim(). + * + * NOTE: LastAddr tempary point (same) to nextAddr after initialize. + * Because LastAddr is don't care in function dma_reclaim(). + */ +void dma_init_descriptors(void) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(dma_mem.terminator); i++) + clear_descriptor(&dma_mem.terminator[i]); + + /* Assign terminators to DMA queues */ + i = 0; + init_queue(&fw.pta.up_queue, &dma_mem.terminator[i++]); + init_queue(&fw.pta.down_queue, &dma_mem.terminator[i++]); + for (j = 0; j < __AR9170_NUM_TX_QUEUES; j++) + init_queue(&fw.wlan.tx_queue[j], &dma_mem.terminator[i++]); + init_queue(&fw.wlan.tx_retry, &dma_mem.terminator[i++]); + init_queue(&fw.wlan.rx_queue, &dma_mem.terminator[i++]); + fw.usb.int_desc = &dma_mem.terminator[i++]; + fw.wlan.fw_desc = &dma_mem.terminator[i++]; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + for (j = 0; j < CARL9170_INTF_NUM; j++) + init_queue(&fw.wlan.cab_queue[j], &dma_mem.terminator[i++]); +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + BUG_ON(AR9170_TERMINATOR_NUMBER != i); + + DBG("Blocks:%d [tx:%d, rx:%d] Terminators:%d/%d\n", + AR9170_BLOCK_NUMBER, AR9170_TX_BLOCK_NUMBER, + AR9170_RX_BLOCK_NUMBER, AR9170_TERMINATOR_NUMBER, i); + + /* Init descriptors and memory blocks */ + for (i = 0; i < AR9170_BLOCK_NUMBER; i++) { + fill_descriptor(&dma_mem.block[i], AR9170_BLOCK_SIZE, dma_mem.data[i].data); + + if (i < AR9170_TX_BLOCK_NUMBER) + dma_reclaim(&fw.pta.down_queue, &dma_mem.block[i]); + else + dma_reclaim(&fw.wlan.rx_queue, &dma_mem.block[i]); + } + + /* Set DMA address registers */ + set(AR9170_PTA_REG_DN_DMA_ADDRH, (uint32_t) fw.pta.down_queue.head >> 16); + set(AR9170_PTA_REG_DN_DMA_ADDRL, (uint32_t) fw.pta.down_queue.head & 0xffff); + set(AR9170_PTA_REG_UP_DMA_ADDRH, (uint32_t) fw.pta.up_queue.head >> 16); + set(AR9170_PTA_REG_UP_DMA_ADDRL, (uint32_t) fw.pta.up_queue.head & 0xffff); + + for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) + set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head); + + set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head); + fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE; + fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp; + + memset(DESC_PAYLOAD(fw.usb.int_desc), 0xff, + AR9170_INT_MAGIC_HEADER_SIZE); + memset(DESC_PAYLOAD_OFF(fw.usb.int_desc, AR9170_INT_MAGIC_HEADER_SIZE), + 0, AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE); + + /* rsp is now available for use */ + fw.usb.int_desc_available = 1; + + memset(DESC_PAYLOAD(fw.wlan.fw_desc), 0, 128); + fw.wlan.fw_desc_available = 1; +} + +/* + * Free descriptor. + * + * Exchange the terminator and the first descriptor of the packet + * for hardware ascy... + */ +void dma_reclaim(struct dma_queue *q, struct dma_desc *desc) +{ + struct dma_desc *tmpDesc, *last; + struct dma_desc tdesc; + + /* 1. Set OWN bit to HW for all TDs to be added, clear ctrl and size */ + tmpDesc = desc; + last = desc->lastAddr; + + while (1) { + tmpDesc->status = AR9170_OWN_BITS_HW; + tmpDesc->ctrl = 0; + tmpDesc->totalLen = 0; + tmpDesc->dataSize = AR9170_BLOCK_SIZE; + + /* TODO : Exception handle */ + + tmpDesc->lastAddr = tmpDesc; + + if (tmpDesc == last) + break; + + tmpDesc = tmpDesc->nextAddr; + } + + /* 2. Next address of Last TD to be added = first TD */ + tmpDesc->nextAddr = desc; + + /* Link first TD to self */ + desc->lastAddr = q->terminator; + + /* 3. Copy first TD to be added to TTD */ + copy_dma_desc(&tdesc, desc); + + /* 4. Initialize new terminator */ + clear_descriptor(desc); + + /* 5. Copy TTD to last TD */ + tdesc.status = 0; + copy_dma_desc((void *)q->terminator, (void *)&tdesc); + q->terminator->status |= AR9170_OWN_BITS_HW; + + /* Update terminator pointer */ + q->terminator = desc; +} + +/* + * Put a complete packet into the tail of the Queue q. + * Exchange the terminator and the first descriptor of the packet + * for hardware ascy... + */ +void dma_put(struct dma_queue *q, struct dma_desc *desc) +{ + struct dma_desc *tmpDesc; + struct dma_desc tdesc; + + tmpDesc = desc; + + while (1) { + /* update totalLen */ + tmpDesc->totalLen = desc->totalLen; + + /* 1. Set OWN bit to HW for all TDs to be added */ + tmpDesc->status = AR9170_OWN_BITS_HW; + /* TODO : Exception handle */ + + tmpDesc->lastAddr = desc->lastAddr; + + if (desc->lastAddr == tmpDesc) + break; + + tmpDesc = tmpDesc->nextAddr; + } + + /* 2. Next address of Last TD to be added = first TD */ + desc->lastAddr->nextAddr = desc; + + /* If there is only one descriptor, update pointer of last descriptor */ + if (desc->lastAddr == desc) + desc->lastAddr = q->terminator; + + /* 3. Copy first TD to be added to TTD */ + copy_dma_desc(&tdesc, desc); + + /* 4. Initialize new terminator */ + clear_descriptor(desc); + + /* 5. Copy TTD to last TD */ + tdesc.status &= (~AR9170_OWN_BITS); + copy_dma_desc((void *)q->terminator, (void *)&tdesc); + q->terminator->status |= AR9170_OWN_BITS_HW; + + /* Update terminator pointer */ + q->terminator = desc; +} + +struct dma_desc *dma_unlink_head(struct dma_queue *queue) +{ + struct dma_desc *desc; + + if (queue_empty(queue)) + return NULL; + + desc = queue->head; + + queue->head = desc->lastAddr->nextAddr; + + /* poison nextAddr address */ + desc->lastAddr->nextAddr = desc->lastAddr; + desc->lastAddr->lastAddr = desc->lastAddr; + + return desc; +} diff --git a/carl9170fw/carlfw/src/fw.c b/carl9170fw/carlfw/src/fw.c new file mode 100644 index 0000000..5bde675 --- /dev/null +++ b/carl9170fw/carlfw/src/fw.c @@ -0,0 +1,116 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware descriptor + * + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "carl9170.h" +#include "fwdsc.h" + +#define FILL(small, big, more...) \ + .small = { \ + CARL9170FW_FILL_DESC(big##_MAGIC, \ + sizeof(struct carl9170fw_## small##_desc), \ + CARL9170FW_## big##_DESC_MIN_VER, \ + CARL9170FW_## big##_DESC_CUR_VER), \ + more \ + } + +const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = { + FILL(otus, OTUS, + .feature_set = cpu_to_le32(BIT(CARL9170FW_DUMMY_FEATURE) | + BIT(CARL9170FW_USB_RESP_EP2) | + BIT(CARL9170FW_HANDLE_BACK_REQ) | + BIT(CARL9170FW_RX_FILTER) | + BIT(CARL9170FW_HW_COUNTERS) | + BIT(CARL9170FW_RX_BA_FILTER) | + BIT(CARL9170FW_USB_INIT_FIRMWARE) | +#ifdef CONFIG_CARL9170FW_USB_UP_STREAM + BIT(CARL9170FW_USB_UP_STREAM) | +#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ +#ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM + BIT(CARL9170FW_USB_DOWN_STREAM) | +#endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */ +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + BIT(CARL9170FW_COMMAND_PHY) | + BIT(CARL9170FW_PSM) | + BIT(CARL9170FW_FIXED_5GHZ_PSM) | +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE + BIT(CARL9170FW_COMMAND_CAM) | +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + BIT(CARL9170FW_WLANTX_CAB) | +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ +#ifdef CONFIG_CARL9170FW_UNUSABLE + BIT(CARL9170FW_UNUSABLE) | +#endif /* CONFIG_CARL9170FW_UNUSABLE */ +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + BIT(CARL9170FW_GPIO_INTERRUPT) | +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ +#ifdef CONFIG_CARL9170FW_WOL + BIT(CARL9170FW_WOL) | +#endif /* CONFIG_CARL9170FW_WOL */ + (0)), + + .miniboot_size = cpu_to_le16(0), + .tx_descs = AR9170_TX_BLOCK_NUMBER, + .cmd_bufs = CARL9170_INT_RQ_CACHES, + .rx_max_frame_len = cpu_to_le16(CONFIG_CARL9170FW_RX_FRAME_LEN), + .tx_frag_len = cpu_to_le16(AR9170_BLOCK_SIZE), + .fw_address = cpu_to_le32(AR9170_PRAM_OFFSET), + .bcn_addr = (__le32) cpu_to_le32(&dma_mem.reserved.bcn), + .bcn_len = (__le16) cpu_to_le16(sizeof(dma_mem.reserved.bcn)), + .vif_num = CARL9170_INTF_NUM, + .api_ver = CONFIG_CARL9170FW_RELEASE_VERSION, + ), + + FILL(txsq, TXSQ, + .seq_table_addr = cpu_to_le32(&fw.wlan.sequence), + ), + +#ifdef CONFIG_CARL9170FW_WOL + FILL(wol, WOL, + .supported_triggers = BIT(CARL9170_WOL_DISCONNECT) | + BIT(CARL9170_WOL_MAGIC_PKT), + ), +#endif /* CONFIG_CARL9170FW_WOL */ + + + FILL(motd, MOTD, + .fw_year_month_day = cpu_to_le32( + CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) + + CARL9170FW_SET_MONTH(CARL9170FW_VERSION_MONTH) + + CARL9170FW_SET_YEAR(CARL9170FW_VERSION_YEAR)), + .desc = "Community AR9170 Linux", + .release = CARL9170FW_VERSION_GIT), + + FILL(dbg, DBG, + .bogoclock_addr = cpu_to_le32(0), + .counter_addr = cpu_to_le32(&fw.counter), + .rx_total_addr = cpu_to_le32(0), + .rx_overrun_addr = cpu_to_le32(0), + .rx_filter = cpu_to_le32(&fw.wlan.rx_filter), + ), + + FILL(last, LAST), +}; + +#undef FILL + +struct firmware_context_struct fw; diff --git a/carl9170fw/carlfw/src/gpio.c b/carl9170fw/carlfw/src/gpio.c new file mode 100644 index 0000000..85cd81f --- /dev/null +++ b/carl9170fw/carlfw/src/gpio.c @@ -0,0 +1,52 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * GPIO interrupt service + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "gpio.h" + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT +void gpio_timer(void) +{ + uint32_t cur; + + cur = get(AR9170_GPIO_REG_PORT_DATA) & CARL9170_GPIO_MASK; + + if (cur != fw.cached_gpio_state.gpio) { + fw.cached_gpio_state.gpio = cur; + + send_cmd_to_host(sizeof(struct carl9170_gpio), + CARL9170_RSP_GPIO, 0x00, + (uint8_t *)&fw.cached_gpio_state); + +# ifdef CONFIG_CARL9170FW_WATCHDOG_BUTTON + for (;;) { + /* + * Loop forever... Until the watchdog triggers. + */ + } +# endif /* CONFIG_CARL9170FW_WATCHDOG_BUTTON */ + } +} +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ diff --git a/carl9170fw/carlfw/src/hostif.c b/carl9170fw/carlfw/src/hostif.c new file mode 100644 index 0000000..e445d5f --- /dev/null +++ b/carl9170fw/carlfw/src/hostif.c @@ -0,0 +1,165 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Host interface routines + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "hostif.h" +#include "printf.h" +#include "wl.h" + +static bool length_check(struct dma_desc *desc) +{ + volatile struct carl9170_tx_superframe *super = __get_super(desc); + + if (unlikely(desc->totalLen < sizeof(struct carl9170_tx_superdesc))) + return false; + + /* + * check if the DMA is complete, or clipped. + * + * NB: The hardware aligns the descriptor length to + * a 4 byte boundary. This makes the direct comparison + * difficult, or unnecessary complex for a hot-path. + */ + if (unlikely(super->s.len > desc->totalLen)) + return false; + + return true; +} + +static void handle_download(void) +{ + struct dma_desc *desc; + + /* + * Under normal conditions, all completed descs should have + * the AR9170_OWN_BITS_SE status flag set. + * However there seems to be a undocumented case where the flag + * is _SW ( handle_download_exception ) + */ + + for_each_desc_not_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) { + if (unlikely((length_check(desc) == false))) { + /* + * There is no easy way of telling what was lost. + * + * Therefore we just reclaim the data. + * The driver has to have some sort frame + * timeout mechanism. + */ + + wlan_tx_complete(__get_super(desc), false); + dma_reclaim(&fw.pta.down_queue, desc); + down_trigger(); + } else { + wlan_tx(desc); + } + } + +#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT + xorl(AR9170_GPIO_REG_PORT_DATA, 2); +#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ +} + +static void handle_upload(void) +{ + struct dma_desc *desc; + + for_each_desc_not_bits(desc, &fw.pta.up_queue, AR9170_OWN_BITS_HW) { + /* + * BIG FAT NOTE: + * + * DO NOT compare the descriptor addresses. + */ + if (DESC_PAYLOAD(desc) == (void *) &dma_mem.reserved.rsp) { + fw.usb.int_desc = desc; + fw.usb.int_desc_available = 1; + } else { + dma_reclaim(&fw.wlan.rx_queue, desc); + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } + } + +#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT + xorl(AR9170_GPIO_REG_PORT_DATA, 2); +#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ +} + +static void handle_download_exception(void) +{ + struct dma_desc *desc, *target; + + /* actually, the queue should be stopped by now? */ + usb_stop_down_queue(); + + target = (void *)((get(AR9170_PTA_REG_DN_CURR_ADDRH) << 16) | + get(AR9170_PTA_REG_DN_CURR_ADDRL)); + + /* + * Put "forgotten" packets from the head of the queue, back + * to the current position + */ + __while_desc_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) { + if (desc == target) + break; + + dma_reclaim(&fw.pta.down_queue, + dma_unlink_head(&fw.pta.down_queue)); + } + + __for_each_desc_continue(desc, &fw.pta.down_queue) { + if ((desc->status & AR9170_OWN_BITS) == AR9170_OWN_BITS_SW) + dma_fix_downqueue(desc); + } + + + usb_start_down_queue(); + + down_trigger(); +} + +/* handle interrupts from DMA chip */ +void handle_host_interface(void) +{ + uint32_t pta_int; + + pta_int = get(AR9170_PTA_REG_INT_FLAG); + +#define HANDLER(intr, flag, func) \ + do { \ + if ((intr & flag) != 0) { \ + func(); \ + } \ + } while (0) + + HANDLER(pta_int, AR9170_PTA_INT_FLAG_DN, handle_download); + + HANDLER(pta_int, AR9170_PTA_INT_FLAG_UP, handle_upload); + + /* This is just guesswork and MAGIC */ + pta_int = get(AR9170_PTA_REG_DMA_STATUS); + HANDLER(pta_int, 0x1, handle_download_exception); + +#undef HANDLER +} diff --git a/carl9170fw/carlfw/src/main.c b/carl9170fw/carlfw/src/main.c new file mode 100644 index 0000000..17cbaf9 --- /dev/null +++ b/carl9170fw/carlfw/src/main.c @@ -0,0 +1,256 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * initialization and main() loop + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "timer.h" +#include "hostif.h" +#include "printf.h" +#include "gpio.h" +#include "wl.h" +#include "rf.h" +#include "usb.h" + +#define AR9170_WATCH_DOG_TIMER 0x100 + +static void timer_init(const unsigned int timer, const unsigned int interval) +{ + /* Set timer to periodic mode */ + orl(AR9170_TIMER_REG_CONTROL, BIT(timer)); + + /* Set time interval */ + set(AR9170_TIMER_REG_TIMER0 + (timer << 2), interval - 1); + + /* Clear timer interrupt flag */ + orl(AR9170_TIMER_REG_INTERRUPT, BIT(timer)); +} + +void clock_set(enum cpu_clock_t clock_, bool on) +{ + /* + * Word of Warning! + * This setting does more than just mess with the CPU Clock. + * So watch out, if you need _stable_ timer interrupts. + */ +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + if (fw.phy.frequency < 3000000) + set(AR9170_PWR_REG_PLL_ADDAC, 0x5163); + else + set(AR9170_PWR_REG_PLL_ADDAC, 0x5143); +#else + set(AR9170_PWR_REG_PLL_ADDAC, 0x5163); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + fw.ticks_per_usec = GET_VAL(AR9170_PWR_PLL_ADDAC_DIV, + get(AR9170_PWR_REG_PLL_ADDAC)); + + set(AR9170_PWR_REG_CLOCK_SEL, (uint32_t) ((on ? 0x70 : 0x600) | clock_)); + + switch (clock_) { + case AHB_20_22MHZ: + fw.ticks_per_usec >>= 1; + case AHB_40MHZ_OSC: + case AHB_40_44MHZ: + fw.ticks_per_usec >>= 1; + case AHB_80_88MHZ: + break; + } +} + +static void init(void) +{ + led_init(); + +#ifdef CONFIG_CARL9170FW_DEBUG_UART + uart_init(); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ + + /* 25/50/100ms timer (depends on cpu clock) */ + timer_init(0, 50000); + + /* USB init */ + usb_init(); + + /* initialize DMA memory */ + memset(&dma_mem, 0, sizeof(dma_mem)); + + /* fill DMA rings */ + dma_init_descriptors(); + + /* clear all interrupt */ + set(AR9170_MAC_REG_INT_CTRL, 0xffff); + + orl(AR9170_MAC_REG_AFTER_PNP, 1); + + /* Init watch dog control flag */ + fw.watchdog_enable = 1; + + set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER); + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + fw.cached_gpio_state.gpio = get(AR9170_GPIO_REG_PORT_DATA) & + CARL9170_GPIO_MASK; +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ + + /* this will get the downqueue moving. */ + down_trigger(); +} + +static void handle_fw(void) +{ + if (fw.watchdog_enable == 1) + set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER); + + if (fw.reboot) + reboot(); +} + +static void timer0_isr(void) +{ + wlan_timer(); + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + gpio_timer(); +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ + +#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT + set(AR9170_GPIO_REG_PORT_DATA, get(AR9170_GPIO_REG_PORT_DATA) ^ 1); +#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ +} + +static void handle_timer(void) +{ + uint32_t intr; + + intr = get(AR9170_TIMER_REG_INTERRUPT); + + /* ACK timer interrupt */ + set(AR9170_TIMER_REG_INTERRUPT, intr); + +#define HANDLER(intr, flag, func) \ + do { \ + if ((intr & flag) != 0) { \ + intr &= ~flag; \ + func(); \ + } \ + } while (0) + + HANDLER(intr, BIT(0), timer0_isr); + + if (intr) + DBG("Unhandled Timer Event %x", (unsigned int) intr); + +#undef HANDLER +} + +static void tally_update(void) +{ + unsigned int boff, time, delta; + + time = get_clock_counter(); + if (fw.phy.state == CARL9170_PHY_ON) { + delta = (time - fw.tally_clock); + + fw.tally.active += delta; + + boff = get(AR9170_MAC_REG_BACKOFF_STATUS); + if (boff & AR9170_MAC_BACKOFF_TX_PE) + fw.tally.tx_time += delta; + if (boff & AR9170_MAC_BACKOFF_CCA) + fw.tally.cca += delta; + } + + fw.tally_clock = time; + fw.counter++; +} + +static void __noreturn main_loop(void) +{ + /* main loop */ + while (1) { + handle_fw(); + + /* + * Due to frame order persevation, the wlan subroutines + * must be executed before handle_host_interface. + */ + handle_wlan(); + + handle_host_interface(); + + handle_usb(); + + handle_timer(); + + tally_update(); + } +} + +/* + * The bootcode will work with the device driver to load the firmware + * onto the device's Program SRAM. The Program SRAM has a size of 16 KB + * and also contains the stack, which grows down from 0x204000. + * + * The Program SRAM starts at address 0x200000 on the device. + * The firmware entry point (0x200004) is located in boot.S. + * we put _start() there with the linker script carl9170.lds. + */ + +void __section(boot) start(void) +{ + clock_set(AHB_40MHZ_OSC, true); + + /* watchdog magic pattern check */ + if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x12340000) { + /* watch dog warm start */ + incl(AR9170_PWR_REG_WATCH_DOG_MAGIC); + usb_trigger_out(); + } else if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x98760000) { + /* suspend/resume */ + } + + /* write the magic pattern for watch dog */ + andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xFFFF); + orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x12340000); + + init(); + +#ifdef CONFIG_CARL9170FW_DEBUG + + BUG("TEST BUG"); + BUG_ON(0x2b || !0x2b); + INFO("INFO MESSAGE"); + + /* a set of unique characters to detect transfer data corruptions */ + DBG("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" + " ~`!1@2#3$4%%5^6&7*8(9)0_-+={[}]|\\:;\"'<,>.?/"); +#endif /* CONFIG_CARL9170FW_DEBUG */ + + /* + * Tell the host, that the firmware has booted and is + * now ready to process requests. + */ + send_cmd_to_host(0, CARL9170_RSP_BOOT, 0x00, NULL); + main_loop(); +} diff --git a/carl9170fw/carlfw/src/memcpy.S b/carl9170fw/carlfw/src/memcpy.S new file mode 100644 index 0000000..9539a6b --- /dev/null +++ b/carl9170fw/carlfw/src/memcpy.S @@ -0,0 +1,228 @@ +/* $Id: memcpy.S,v 1.3 2001/07/27 11:50:52 gniibe Exp $ + * + * "memcpy" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +/* + * void *memcpy(void *dst, const void *src, size_t n); + * No overlap between the memory of DST and of SRC are assumed. + */ + + .globl _memcpy + .align 2 + _memcpy: + tst r6,r6 + bt/s 9f ! if n=0, do nothing + mov r4,r0 + sub r4,r5 ! From here, r5 has the distance to r0 + add r6,r0 ! From here, r0 points the end of copying point + mov #12,r1 + cmp/gt r6,r1 + bt/s 7f ! if it's too small, copy a byte at once + add #-1,r5 + add #1,r5 + ! From here, r6 is free + ! + ! r4 --> [ ... ] DST [ ... ] SRC + ! [ ... ] [ ... ] + ! : : + ! r0 --> [ ... ] r0+r5 --> [ ... ] + ! + ! + mov r5,r1 + mov #3,r2 + and r2,r1 + shll2 r1 + mov r0,r3 ! Save the value on R0 to R3 + mova jmptable,r0 + add r1,r0 + mov.l @r0,r1 + jmp @r1 + mov r3,r0 ! and back to R0 + .balign 4 +jmptable: + .long case0 + .long case1 + .long case2 + .long case3 + + ! copy a byte at once +7: mov r4,r2 + add #1,r2 +8: + cmp/hi r2,r0 + mov.b @(r0,r5),r1 + bt/s 8b ! while (r0>r2) + mov.b r1,@-r0 +9: + rts + nop + +case0: + ! + ! GHIJ KLMN OPQR --> GHIJ KLMN OPQR + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-4,r5 + add #3,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! + add #-3,r5 +2: ! Second, copy a long word at once + mov r4,r2 + add #7,r2 +3: mov.l @(r0,r5),r1 + cmp/hi r2,r0 + bt/s 3b + mov.l r1,@-r0 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #3,r5 + bra 8b + add #-6,r2 + +case1: + ! + ! GHIJ KLMN OPQR --> ...G HIJK LMNO PQR. + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! +2: ! Second, read a long word and write a long word at once + mov.l @(r0,r5),r1 + add #-4,r5 + mov r4,r2 + add #7,r2 + ! +#ifdef __LITTLE_ENDIAN__ +3: mov r1,r3 ! RQPO + shll16 r3 + shll8 r3 ! Oxxx + mov.l @(r0,r5),r1 ! NMLK + mov r1,r6 + shlr8 r6 ! xNML + or r6,r3 ! ONML + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#else +3: mov r1,r3 ! OPQR + shlr16 r3 + shlr8 r3 ! xxxO + mov.l @(r0,r5),r1 ! KLMN + mov r1,r6 + shll8 r6 ! LMNx + or r6,r3 ! LMNO + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#endif + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #4,r5 + bra 8b + add #-6,r2 + +case2: + ! + ! GHIJ KLMN OPQR --> ..GH IJKL MNOP QR.. + ! + ! First, align to word boundary + tst #1,r0 + bt/s 2f + add #-1,r5 + mov.b @(r0,r5),r1 + mov.b r1,@-r0 + ! +2: ! Second, read a word and write a word at once + add #-1,r5 + mov r4,r2 + add #3,r2 + ! +3: mov.w @(r0,r5),r1 + cmp/hi r2,r0 + bt/s 3b + mov.w r1,@-r0 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #1,r5 + mov.b @(r0,r5),r1 + rts + mov.b r1,@-r0 + +case3: + ! + ! GHIJ KLMN OPQR --> .GHI JKLM NOPQ R... + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! +2: ! Second, read a long word and write a long word at once + add #-2,r5 + mov.l @(r0,r5),r1 + add #-4,r5 + mov r4,r2 + add #7,r2 + ! +#ifdef __LITTLE_ENDIAN__ +3: mov r1,r3 ! RQPO + shll8 r3 ! QPOx + mov.l @(r0,r5),r1 ! NMLK + mov r1,r6 + shlr16 r6 + shlr8 r6 ! xxxN + or r6,r3 ! QPON + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#else +3: mov r1,r3 ! OPQR + shlr8 r3 ! xOPQ + mov.l @(r0,r5),r1 ! KLMN + mov r1,r6 + shll16 r6 + shll8 r6 ! Nxxx + or r6,r3 ! NOPQ + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#endif + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #6,r5 + bra 8b + add #-6,r2 diff --git a/carl9170fw/carlfw/src/memset.S b/carl9170fw/carlfw/src/memset.S new file mode 100644 index 0000000..d39c8a6 --- /dev/null +++ b/carl9170fw/carlfw/src/memset.S @@ -0,0 +1,58 @@ +/* $Id: memset.S,v 1.1 2000/04/14 16:49:01 mjd Exp $ + * + * "memset" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +/* + * void *memset(void *s, int c, size_t n); + */ + + .globl _memset + .align 2 + _memset: + tst r6,r6 + bt/s 5f ! if n=0, do nothing + add r6,r4 + mov #12,r0 + cmp/gt r6,r0 + bt/s 4f ! if it's too small, set a byte at once + mov r4,r0 + and #3,r0 + cmp/eq #0,r0 + bt/s 2f ! It's aligned + sub r0,r6 +1: + dt r0 + bf/s 1b + mov.b r5,@-r4 +2: ! make VVVV + extu.b r5,r5 + swap.b r5,r0 ! V0 + or r0,r5 ! VV + swap.w r5,r0 ! VV00 + or r0,r5 ! VVVV + ! + mov r6,r0 + shlr2 r0 + shlr r0 ! r0 = r6 >> 3 +3: + dt r0 + mov.l r5,@-r4 ! set 8-byte at once + bf/s 3b + mov.l r5,@-r4 + ! + mov #7,r0 + and r0,r6 + tst r6,r6 + bt 5f + ! fill bytes +4: + dt r6 + bf/s 4b + mov.b r5,@-r4 +5: + rts + mov r4,r0 diff --git a/carl9170fw/carlfw/src/printf.c b/carl9170fw/carlfw/src/printf.c new file mode 100644 index 0000000..3ff05c4 --- /dev/null +++ b/carl9170fw/carlfw/src/printf.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2004,2008 Kustaa Nyholm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "carl9170.h" +#include "printf.h" + +#ifdef CONFIG_CARL9170FW_PRINTF +static char *bf; +static char buf[12]; +static unsigned int num; +static char uc; +static char zs; + +static void out(const char c) +{ + *bf++ = c; +} + +static void outDgt(const char dgt) +{ + out(dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10)); + zs = 1; +} + +static void divOut(const unsigned int d) +{ + unsigned char dgt = 0; + + while (num >= d) { + num -= d; + dgt++; + } + + if (zs || dgt > 0) + outDgt(dgt); +} + +void tfp_printf(const char *fmt, ...) +{ + va_list va; + char *p; + unsigned int i; + char ch; + + va_start(va, fmt); + + while ((ch = *(fmt++))) { + if (ch != '%') { + putcharacter(ch); + } else { + char lz = 0; + char w = 0; + ch = *(fmt++); + + if (ch == '0') { + ch = *(fmt++); + lz = 1; + } + + if (ch >= '0' && ch <= '9') { + w = 0; + while (ch >= '0' && ch <= '9') { + w = (((w << 2) + w) << 1) + ch - '0'; + ch = *fmt++; + } + } + + bf = buf; + p = bf; + zs = 0; + + switch (ch) { + case 0: + goto abort; + + case 'u': + case 'd': + num = va_arg(va, unsigned int); + if (ch == 'd' && (int) num < 0) { + num = -(int)num; + out('-'); + } + + for (i = 100000000; i != 1; i /= 10) + divOut(i); + + outDgt(num); + break; + + case 'p': + case 'x': + case 'X': + uc = ch == 'X'; + num = va_arg(va, unsigned int); + for (i = 0x10000000; i != 0x1; i >>= 4) + divOut(i); + + outDgt(num); + break; + + case 'c': + out((char)(va_arg(va, int))); + break; + + case 's': + p = va_arg(va, char*); + break; + case '%': + out('%'); + break; + + default: + break; + } + + *bf = 0; + bf = p; + while (*bf++ && w > 0) + w--; + + while (w-- > 0) + putcharacter(lz ? '0' : ' '); + + while ((ch = *p++)) + putcharacter(ch); + } + } + +abort: + putcharacter('\0'); + va_end(va); +} + +#else + +void min_printf(const char *fmt, ...) +{ + char ch; + + do { + ch = *(fmt++); + putcharacter(ch); + } while (ch); +} + +#endif /* CONFIG_CARL9170FW_PRINTF */ diff --git a/carl9170fw/carlfw/src/reboot.S b/carl9170fw/carlfw/src/reboot.S new file mode 100644 index 0000000..14c1813 --- /dev/null +++ b/carl9170fw/carlfw/src/reboot.S @@ -0,0 +1,11 @@ + .globl _jump_to_bootcode + .type _jump_to_bootcode, @function + _jump_to_bootcode: + mov.l stack_start, r0 + mov.l @r0, sp + mov.l eeprom_start, r0 + mov.l @r0, r0 + jmp @r0 + .align 4 +stack_start: .long 0x00000004 +eeprom_start: .long 0x00000000 diff --git a/carl9170fw/carlfw/src/rf.c b/carl9170fw/carlfw/src/rf.c new file mode 100644 index 0000000..e031dd8 --- /dev/null +++ b/carl9170fw/carlfw/src/rf.c @@ -0,0 +1,277 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * PHY and RF functions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "timer.h" +#include "printf.h" +#include "rf.h" +#include "shared/phy.h" + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS +static void set_channel_end(void) +{ + /* Manipulate CCA threshold to resume transmission */ + set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0); + /* Disable Virtual CCA */ + andl(AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA, + ~AR9170_MAC_VIRTUAL_CCA_ALL); + + fw.phy.state = CARL9170_PHY_ON; +} + +void rf_notify_set_channel(void) +{ + /* Manipulate CCA threshold to stop transmission */ + set(AR9170_PHY_REG_CCA_THRESHOLD, 0x300); + /* Enable Virtual CCA */ + orl(AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA, + AR9170_MAC_VIRTUAL_CCA_ALL); + + /* reset CCA stats */ + fw.tally.active = 0; + fw.tally.cca = 0; + fw.tally.tx_time = 0; + fw.phy.state = CARL9170_PHY_OFF; +} + +/* + * Update delta slope coeff man and exp + */ +static void hw_turn_off_dyn(const uint32_t delta_slope_coeff_exp, + const uint32_t delta_slope_coeff_man, + const uint32_t delta_slope_coeff_exp_shgi, + const uint32_t delta_slope_coeff_man_shgi) +{ + uint32_t tmp; + + tmp = get_async(AR9170_PHY_REG_TIMING3) & 0x00001fff; + tmp |= (delta_slope_coeff_man << AR9170_PHY_TIMING3_DSC_MAN_S) & + AR9170_PHY_TIMING3_DSC_MAN; + tmp |= (delta_slope_coeff_exp << AR9170_PHY_TIMING3_DSC_EXP_S) & + AR9170_PHY_TIMING3_DSC_EXP; + + set(AR9170_PHY_REG_TIMING3, tmp); + + tmp = (delta_slope_coeff_man_shgi << AR9170_PHY_HALFGI_DSC_MAN_S) & + AR9170_PHY_HALFGI_DSC_MAN; + + tmp |= (delta_slope_coeff_exp_shgi << AR9170_PHY_HALFGI_DSC_EXP_S) & + AR9170_PHY_HALFGI_DSC_EXP; + + set(AR9170_PHY_REG_HALFGI, tmp); +} + +static void program_ADDAC(void) +{ + /* ??? Select Internal ADDAC ??? (is external radio) */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO); + + delay(10); + + set(0x1c589c, 0x00000000); /*# 7-0 */ + set(0x1c589c, 0x00000000); /*# 15-8 */ + set(0x1c589c, 0x00000000); /*# 23-16 */ + set(0x1c589c, 0x00000000); /*# 31- */ + + set(0x1c589c, 0x00000000); /*# 39- */ + set(0x1c589c, 0x00000000); /*# 47- */ + set(0x1c589c, 0x00000000); /*# 55- [48]:doubles the xtalosc bias current */ + set(0x1c589c, 0x00000000); /*# 63- */ + + set(0x1c589c, 0x00000000); /*# 71- */ + set(0x1c589c, 0x00000000); /*# 79- */ + set(0x1c589c, 0x00000000); /*# 87- */ + set(0x1c589c, 0x00000000); /*# 95- */ + + set(0x1c589c, 0x00000000); /*# 103- */ + set(0x1c589c, 0x00000000); /*# 111- */ + set(0x1c589c, 0x00000000); /*# 119- */ + set(0x1c589c, 0x00000000); /*# 127- */ + + set(0x1c589c, 0x00000000); /*# 135- */ + set(0x1c589c, 0x00000000); /*# 143- */ + set(0x1c589c, 0x00000000); /*# 151- */ + set(0x1c589c, 0x00000030); /*# 159- #[158:156]=xlnabufmode */ + + set(0x1c589c, 0x00000004); /*# 167- [162]:disable clkp_driver to flow */ + set(0x1c589c, 0x00000000); /*# 175- */ + set(0x1c589c, 0x00000000); /*# 183-176 */ + set(0x1c589c, 0x00000000); /*# 191-184 */ + + set(0x1c589c, 0x00000000); /*# 199- */ + set(0x1c589c, 0x00000000); /*# 207- */ + set(0x1c589c, 0x00000000); /*# 215- */ + set(0x1c589c, 0x00000000); /*# 223- */ + + set(0x1c589c, 0x00000000); /*# 231- */ + set(0x1c58c4, 0x00000000); /*# 233-232 */ + + delay(10); + + /* Select External Flow ???? (is internal addac??) */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC); +} + +static uint32_t AGC_calibration(uint32_t loop) +{ + uint32_t wrdata; + uint32_t ret; + +#define AGC_CAL_NF (AR9170_PHY_AGC_CONTROL_CAL | AR9170_PHY_AGC_CONTROL_NF) + + wrdata = get_async(AR9170_PHY_REG_AGC_CONTROL) | AGC_CAL_NF; + set(AR9170_PHY_REG_AGC_CONTROL, wrdata); + + ret = get_async(AR9170_PHY_REG_AGC_CONTROL) & AGC_CAL_NF; + + /* sitesurvey : 100 ms / current connected 200 ms */ + while ((ret != 0) && loop--) { + udelay(100); + + ret = get_async(AR9170_PHY_REG_AGC_CONTROL) & AGC_CAL_NF; + } + + /* return the AGC/Noise calibration state to the driver */ + return ret; +} + +#define EIGHTY_FLAG (CARL9170FW_PHY_HT_ENABLE | CARL9170FW_PHY_HT_DYN2040) + +static uint32_t rf_init(const uint32_t delta_slope_coeff_exp, + const uint32_t delta_slope_coeff_man, + const uint32_t delta_slope_coeff_exp_shgi, + const uint32_t delta_slope_coeff_man_shgi, + const uint32_t finiteLoopCount, + const bool initialize) +{ + uint32_t ret; + + hw_turn_off_dyn(delta_slope_coeff_exp, + delta_slope_coeff_man, + delta_slope_coeff_exp_shgi, + delta_slope_coeff_man_shgi); + + if (initialize) { + /* Real Chip */ + program_ADDAC(); + + /* inverse chain 0 <-> chain 2 */ + set(AR9170_PHY_REG_ANALOG_SWAP, AR9170_PHY_ANALOG_SWAP_AB); + + /* swap chain 0 and chain 2 */ + set(AR9170_PHY_REG_ANALOG_SWAP, AR9170_PHY_ANALOG_SWAP_AB | + AR9170_PHY_ANALOG_SWAP_ALT_CHAIN); + + /* Activate BB */ + set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_EN); + delay(10); + } + + ret = AGC_calibration(finiteLoopCount); + + set_channel_end(); + return ret; +} + +void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp) +{ + uint32_t ret; + + fw.phy.ht_settings = cmd->rf_init.ht_settings; + fw.phy.frequency = cmd->rf_init.freq; + + /* + * Is the clock controlled by the PHY? + */ + if ((fw.phy.ht_settings & EIGHTY_FLAG) == EIGHTY_FLAG) + clock_set(AHB_80_88MHZ, true); + else + clock_set(AHB_40_44MHZ, true); + + ret = rf_init(le32_to_cpu(cmd->rf_init.delta_slope_coeff_exp), + le32_to_cpu(cmd->rf_init.delta_slope_coeff_man), + le32_to_cpu(cmd->rf_init.delta_slope_coeff_exp_shgi), + le32_to_cpu(cmd->rf_init.delta_slope_coeff_man_shgi), + le32_to_cpu(cmd->rf_init.finiteLoopCount), + cmd->hdr.cmd == CARL9170_CMD_RF_INIT); + + resp->hdr.len = sizeof(struct carl9170_rf_init_result); + resp->rf_init_res.ret = cpu_to_le32(ret); +} + +void rf_psm(void) +{ + u32 bank3; + + if (fw.phy.psm.state == CARL9170_PSM_SOFTWARE) { + /* not enabled by the driver */ + return; + } + + if (fw.phy.psm.state & CARL9170_PSM_SLEEP) { + fw.phy.psm.state &= ~CARL9170_PSM_SLEEP; + + /* disable all agc gain and offset updates to a2 */ + set(AR9170_PHY_REG_TEST2, 0x8000000); + + /* power down ADDAC */ + set(AR9170_PHY_REG_ADC_CTL, + AR9170_PHY_ADC_CTL_OFF_PWDDAC | + AR9170_PHY_ADC_CTL_OFF_PWDADC | + 0xa0000000); + + /* Synthesizer off + RX off */ + bank3 = 0x00400018; + + fw.phy.state = CARL9170_PHY_OFF; + } else { + /* advance to the next PSM step */ + fw.phy.psm.state--; + + if (fw.phy.psm.state == CARL9170_PSM_WAKE) { + /* wake up ADDAC */ + set(AR9170_PHY_REG_ADC_CTL, + AR9170_PHY_ADC_CTL_OFF_PWDDAC | + AR9170_PHY_ADC_CTL_OFF_PWDADC); + + /* enable all agc gain and offset updates to a2 */ + set(AR9170_PHY_REG_TEST2, 0x0); + + /* Synthesizer on + RX on */ + bank3 = 0x01420098; + + fw.phy.state = CARL9170_PHY_ON; + } else { + return ; + } + } + + if (fw.phy.frequency < 3000000) + bank3 |= 0x00800000; + + set(0x1c58f0, bank3); +} + +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ diff --git a/carl9170fw/carlfw/src/uart.c b/carl9170fw/carlfw/src/uart.c new file mode 100644 index 0000000..58f6486 --- /dev/null +++ b/carl9170fw/carlfw/src/uart.c @@ -0,0 +1,76 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * UART debug interface functions. + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "uart.h" +#include "io.h" + +#ifdef CONFIG_CARL9170FW_DEBUG_UART +void uart_putc(const char c) +{ + set(AR9170_UART_REG_TX_HOLDING, c); + + while (get(AR9170_UART_REG_LINE_STATUS) & + AR9170_UART_LINE_STS_TX_FIFO_ALMOST_EMPTY) { + /* + * wait until the byte has made it + */ + } +} + +void uart_print_hex_dump(const void *buf, const int len) +{ + unsigned int offset = 0; + + uart_putc('H'); + uart_putc('D'); + uart_putc(':'); + + while (len > 0) { + uart_putc(*((uint8_t *) buf + offset)); + offset++; + } +} + +void uart_init(void) +{ + unsigned int timeout = 0; + +#ifdef CONFIG_CARL9170FW_UART_CLOCK_25M + set(AR9170_UART_REG_DIVISOR_LSB, 0xc); +#elif CONFIG_CARL9170FW_UART_CLOCK_40M + set(AR9170_UART_REG_DIVISOR_LSB, 0x14); /* 40 MHz */ + set(AR9170_UART_REG_REMAINDER, 0xb38e); +#else +#error "Unsupported UART clock" +#endif /* CARL9170FW_UART_CLOCK_25M */ + + while (get(AR9170_UART_REG_LINE_STATUS) & + AR9170_UART_LINE_STS_TRANSMITTER_EMPTY) { + if (timeout++ >= 10000) + break; + } +} +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ diff --git a/carl9170fw/carlfw/src/udivsi3_i4i-Os.S b/carl9170fw/carlfw/src/udivsi3_i4i-Os.S new file mode 100644 index 0000000..7bbec4c --- /dev/null +++ b/carl9170fw/carlfw/src/udivsi3_i4i-Os.S @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file 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; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* Moderately Space-optimized libgcc routines for the Renesas SH / + STMicroelectronics ST40 CPUs. + Contributed by J"orn Rennecke joern.rennecke@st.com. */ + +/* Size: 186 bytes jointly for udivsi3_i4i and sdivsi3_i4i + sh4-200 run times: + udiv small divisor: 55 cycles + udiv large divisor: 52 cycles + sdiv small divisor, positive result: 59 cycles + sdiv large divisor, positive result: 56 cycles + sdiv small divisor, negative result: 65 cycles (*) + sdiv large divisor, negative result: 62 cycles (*) + (*): r2 is restored in the rts delay slot and has a lingering latency + of two more cycles. */ + .balign 4 + .global ___udivsi3_i4i + .global ___udivsi3_i4 + .set ___udivsi3_i4, ___udivsi3_i4i + .type ___udivsi3_i4i, @function + .type ___sdivsi3_i4i, @function +___udivsi3_i4i: + sts pr,r1 + mov.l r4,@-r15 + extu.w r5,r0 + cmp/eq r5,r0 + swap.w r4,r0 + shlr16 r4 + bf/s large_divisor + div0u + mov.l r5,@-r15 + shll16 r5 +sdiv_small_divisor: + div1 r5,r4 + bsr div6 + div1 r5,r4 + div1 r5,r4 + bsr div6 + div1 r5,r4 + xtrct r4,r0 + xtrct r0,r4 + bsr div7 + swap.w r4,r4 + div1 r5,r4 + bsr div7 + div1 r5,r4 + xtrct r4,r0 + mov.l @r15+,r5 + swap.w r0,r0 + mov.l @r15+,r4 + jmp @r1 + rotcl r0 +div7: + div1 r5,r4 +div6: + div1 r5,r4; div1 r5,r4; div1 r5,r4 + div1 r5,r4; div1 r5,r4; rts; div1 r5,r4 + +divx3: + rotcl r0 + div1 r5,r4 + rotcl r0 + div1 r5,r4 + rotcl r0 + rts + div1 r5,r4 + +large_divisor: + mov.l r5,@-r15 +sdiv_large_divisor: + xor r4,r0 + .rept 4 + rotcl r0 + bsr divx3 + div1 r5,r4 + .endr + mov.l @r15+,r5 + mov.l @r15+,r4 + jmp @r1 + rotcl r0 + + .global __sdivsi3_i4i + .global __sdivsi3_i4 + .global __sdivsi3 + .set __sdivsi3_i4, __sdivsi3_i4i + .set __sdivsi3, __sdivsi3_i4i +__sdivsi3_i4i: + mov.l r4,@-r15 + cmp/pz r5 + mov.l r5,@-r15 + bt/s pos_divisor + cmp/pz r4 + neg r5,r5 + extu.w r5,r0 + bt/s neg_result + cmp/eq r5,r0 + neg r4,r4 +pos_result: + swap.w r4,r0 + bra sdiv_check_divisor + sts pr,r1 +pos_divisor: + extu.w r5,r0 + bt/s pos_result + cmp/eq r5,r0 + neg r4,r4 +neg_result: + mova negate_result,r0 + ; + mov r0,r1 + swap.w r4,r0 + lds r2,macl + sts pr,r2 +sdiv_check_divisor: + shlr16 r4 + bf/s sdiv_large_divisor + div0u + bra sdiv_small_divisor + shll16 r5 + .balign 4 +negate_result: + neg r0,r0 + jmp @r2 + sts macl,r2 diff --git a/carl9170fw/carlfw/src/wlan.c b/carl9170fw/carlfw/src/wlan.c new file mode 100644 index 0000000..d3328d6 --- /dev/null +++ b/carl9170fw/carlfw/src/wlan.c @@ -0,0 +1,1211 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Interface to the WLAN part of the chip + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "shared/phy.h" +#include "hostif.h" +#include "timer.h" +#include "wl.h" +#include "printf.h" +#include "rf.h" +#include "linux/ieee80211.h" +#include "wol.h" + +static void wlan_txunstuck(unsigned int queue) +{ + set_wlan_txq_dma_addr(queue, ((uint32_t) fw.wlan.tx_queue[queue].head) | 1); +} + +#ifdef CONFIG_CARL9170FW_DMA_QUEUE_BUMP +static void wlan_txupdate(unsigned int queue) +{ + set_wlan_txq_dma_addr(queue, ((uint32_t) fw.wlan.tx_queue[queue].head)); +} + +static void wlan_dma_bump(unsigned int qidx) +{ + unsigned int offset = qidx; + uint32_t status, trigger; + + status = get(AR9170_MAC_REG_DMA_STATUS) >> 12; + trigger = get(AR9170_MAC_REG_DMA_TRIGGER) >> 12; + + while (offset != 0) { + status >>= 4; + trigger >>= 4; + offset--; + } + + status &= 0xf; + trigger &= 0xf; + + if ((trigger == 0xa) && (status == 0x8)) { + DBG("UNSTUCK"); + wlan_txunstuck(qidx); + } else { + DBG("UPDATE"); + wlan_txupdate(qidx); + } +} +#endif /* CONFIG_CARL9170FW_DMA_QUEUE_BUMP */ + +#ifdef CONFIG_CARL9170FW_DEBUG +static void wlan_dump_queue(unsigned int qidx) +{ + + struct dma_desc *desc; + struct carl9170_tx_superframe *super; + int entries = 0; + + __for_each_desc(desc, &fw.wlan.tx_queue[qidx]) { + super = get_super(desc); + DBG("%d: %p s:%x c:%x tl:%x ds:%x n:%p l:%p ", entries, desc, + desc->status, desc->ctrl, desc->totalLen, + desc->dataSize, desc->nextAddr, desc->lastAddr); + + DBG("c:%x tr:%d ri:%d l:%x m:%x p:%x fc:%x", + super->s.cookie, super->s.cnt, super->s.rix, + super->f.hdr.length, super->f.hdr.mac.set, + (unsigned int) le32_to_cpu(super->f.hdr.phy.set), + super->f.data.i3e.frame_control); + + entries++; + } + + desc = get_wlan_txq_addr(qidx); + + DBG("Queue: %d: te:%d td:%d h:%p c:%p t:%p", + qidx, entries, queue_len(&fw.wlan.tx_queue[qidx]), + fw.wlan.tx_queue[qidx].head, + desc, fw.wlan.tx_queue[qidx].terminator); + + DBG("HW: t:%x s:%x ac:%x c:%x", + (unsigned int) get(AR9170_MAC_REG_DMA_TRIGGER), + (unsigned int) get(AR9170_MAC_REG_DMA_STATUS), + (unsigned int) get(AR9170_MAC_REG_AMPDU_COUNT), + (unsigned int) get(AR9170_MAC_REG_DMA_TXQX_ADDR_CURR)); +} +#endif /* CONFIG_CARL9170FW_DEBUG */ + +static void wlan_send_buffered_tx_status(void) +{ + unsigned int len; + + while (fw.wlan.tx_status_pending) { + len = min((unsigned int)fw.wlan.tx_status_pending, + CARL9170_RSP_TX_STATUS_NUM); + len = min(len, CARL9170_TX_STATUS_NUM - fw.wlan.tx_status_head_idx); + + /* + * rather than memcpy each individual request into a large buffer, + * we _splice_ them all together. + * + * The only downside is however that we have to be careful around + * the edges of the tx_status_cache. + * + * Note: + * Each tx_status is about 2 bytes. However every command package + * must have a size which is a multiple of 4. + */ + + send_cmd_to_host((len * sizeof(struct carl9170_tx_status) + 3) & ~3, + CARL9170_RSP_TXCOMP, len, (void *) + &fw.wlan.tx_status_cache[fw.wlan.tx_status_head_idx]); + + fw.wlan.tx_status_pending -= len; + fw.wlan.tx_status_head_idx += len; + fw.wlan.tx_status_head_idx %= CARL9170_TX_STATUS_NUM; + } +} + +static struct carl9170_tx_status *wlan_get_tx_status_buffer(void) +{ + struct carl9170_tx_status *tmp; + + tmp = &fw.wlan.tx_status_cache[fw.wlan.tx_status_tail_idx++]; + fw.wlan.tx_status_tail_idx %= CARL9170_TX_STATUS_NUM; + + if (fw.wlan.tx_status_pending == CARL9170_TX_STATUS_NUM) + wlan_send_buffered_tx_status(); + + fw.wlan.tx_status_pending++; + + return tmp; +} + +/* generate _aggregated_ tx_status for the host */ +void wlan_tx_complete(struct carl9170_tx_superframe *super, + bool txs) +{ + struct carl9170_tx_status *status; + + status = wlan_get_tx_status_buffer(); + + /* + * The *unique* cookie and AC_ID is used by the driver for + * frame lookup. + */ + status->cookie = super->s.cookie; + status->queue = super->s.queue; + super->s.cookie = 0; + + /* + * This field holds the number of tries of the rate in + * the rate index field (rix). + */ + status->rix = super->s.rix; + status->tries = super->s.cnt; + status->success = (txs) ? 1 : 0; +} + +static bool wlan_tx_consume_retry(struct carl9170_tx_superframe *super) +{ + /* check if this was the last possible retry with this rate */ + if (unlikely(super->s.cnt >= super->s.ri[super->s.rix].tries)) { + /* end of the road - indicate tx failure */ + if (unlikely(super->s.rix == CARL9170_TX_MAX_RETRY_RATES)) + return false; + + /* check if there are alternative rates available */ + if (!super->s.rr[super->s.rix].set) + return false; + + /* try next retry rate */ + super->f.hdr.phy.set = super->s.rr[super->s.rix].set; + + /* finally - mark the old rate as USED */ + super->s.rix++; + + /* update MAC flags */ + super->f.hdr.mac.erp_prot = super->s.ri[super->s.rix].erp_prot; + super->f.hdr.mac.ampdu = super->s.ri[super->s.rix].ampdu; + + /* reinitialize try counter */ + super->s.cnt = 1; + } else { + /* just increase retry counter */ + super->s.cnt++; + } + + return true; +} + +static inline u16 get_tid(struct ieee80211_hdr *hdr) +{ + return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; +} + +/* This function will only work on uint32_t-aligned pointers! */ +static bool same_hdr(const void *_d0, const void *_d1) +{ + const uint32_t *d0 = _d0; + const uint32_t *d1 = _d1; + + /* BUG_ON((unsigned long)d0 & 3 || (unsigned long)d1 & 3)) */ + return !((d0[0] ^ d1[0]) | /* FC + DU */ + (d0[1] ^ d1[1]) | /* addr1 */ + (d0[2] ^ d1[2]) | (d0[3] ^ d1[3]) | /* addr2 + addr3 */ + (d0[4] ^ d1[4])); /* addr3 */ +} + +static inline bool same_aggr(struct ieee80211_hdr *a, struct ieee80211_hdr *b) +{ + return (get_tid(a) == get_tid(b)) || same_hdr(a, b); +} + +static void wlan_tx_ampdu_reset(unsigned int qidx) +{ + fw.wlan.ampdu_prev[qidx] = NULL; +} + +static void wlan_tx_ampdu_end(unsigned int qidx) +{ + struct carl9170_tx_superframe *ht_prev = fw.wlan.ampdu_prev[qidx]; + + if (ht_prev) + ht_prev->f.hdr.mac.ba_end = 1; + + wlan_tx_ampdu_reset(qidx); +} + +static void wlan_tx_ampdu(struct carl9170_tx_superframe *super) +{ + unsigned int qidx = super->s.queue; + struct carl9170_tx_superframe *ht_prev = fw.wlan.ampdu_prev[qidx]; + + if (super->f.hdr.mac.ampdu) { + if (ht_prev && + !same_aggr(&super->f.data.i3e, &ht_prev->f.data.i3e)) + ht_prev->f.hdr.mac.ba_end = 1; + else + super->f.hdr.mac.ba_end = 0; + + fw.wlan.ampdu_prev[qidx] = super; + } else { + wlan_tx_ampdu_end(qidx); + } +} + +/* for all tries */ +static void __wlan_tx(struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = get_super(desc); + + if (unlikely(super->s.fill_in_tsf)) { + struct ieee80211_mgmt *mgmt = (void *) &super->f.data.i3e; + uint32_t *tsf = (uint32_t *) &mgmt->u.probe_resp.timestamp; + + /* + * Truth be told: this is a hack. + * + * The *real* TSF is definitely going to be higher/older. + * But this hardware emulation code is head and shoulders + * above anything a driver can possibly do. + * + * (even, if it's got an accurate atomic clock source). + */ + + read_tsf(tsf); + } + + wlan_tx_ampdu(super); + +#ifdef CONFIG_CARL9170FW_DEBUG + BUG_ON(fw.phy.psm.state != CARL9170_PSM_WAKE); +#endif /* CONFIG_CARL9170FW_DEBUG */ + + /* insert desc into the right queue */ + dma_put(&fw.wlan.tx_queue[super->s.queue], desc); +} + +static void wlan_assign_seq(struct ieee80211_hdr *hdr, unsigned int vif) +{ + hdr->seq_ctrl &= cpu_to_le16(~IEEE80211_SCTL_SEQ); + hdr->seq_ctrl |= cpu_to_le16(fw.wlan.sequence[vif]); + + if (ieee80211_is_first_frag(hdr->seq_ctrl)) + fw.wlan.sequence[vif] += 0x10; +} + +/* prepares frame for the first transmission */ +static void _wlan_tx(struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = get_super(desc); + + if (unlikely(super->s.assign_seq)) + wlan_assign_seq(&super->f.data.i3e, super->s.vif_id); + + if (unlikely(super->s.ampdu_commit_density)) { + set(AR9170_MAC_REG_AMPDU_DENSITY, + MOD_VAL(AR9170_MAC_AMPDU_DENSITY, + get(AR9170_MAC_REG_AMPDU_DENSITY), + super->s.ampdu_density)); + } + + if (unlikely(super->s.ampdu_commit_factor)) { + set(AR9170_MAC_REG_AMPDU_FACTOR, + MOD_VAL(AR9170_MAC_AMPDU_FACTOR, + get(AR9170_MAC_REG_AMPDU_FACTOR), + 8 << super->s.ampdu_factor)); + } +} + +/* propagate transmission status back to the driver */ +static bool wlan_tx_status(struct dma_queue *queue, + struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = get_super(desc); + unsigned int qidx = super->s.queue; + bool txfail = false, success; + + success = true; + + /* update hangcheck */ + fw.wlan.last_super_num[qidx] = 0; + + /* + * Note: + * There could be a corner case when the TXFAIL is set + * even though the frame was properly ACKed by the peer: + * a BlockAckReq with the immediate policy will cause + * the receiving peer to produce a BlockACK unfortunately + * the MAC in this chip seems to be expecting a legacy + * ACK and marks the BAR as failed! + */ + + if (!!(desc->ctrl & AR9170_CTRL_FAIL)) { + txfail = !!(desc->ctrl & AR9170_CTRL_TXFAIL); + + /* reset retry indicator flags */ + desc->ctrl &= ~(AR9170_CTRL_TXFAIL | AR9170_CTRL_BAFAIL); + + /* + * Note: wlan_tx_consume_retry will override the old + * phy [CCK,OFDM, HT, BW20/40, MCS...] and mac vectors + * [AMPDU,RTS/CTS,...] therefore be careful when they + * are used. + */ + if (wlan_tx_consume_retry(super)) { + /* + * retry for simple and aggregated 802.11 frames. + * + * Note: We must not mess up the original frame + * order. + */ + + if (!super->f.hdr.mac.ampdu) { + /* + * 802.11 - 7.1.3.1.5. + * set "Retry Field" for consecutive attempts + * + * Note: For AMPDU see: + * 802.11n 9.9.1.6 "Retransmit Procedures" + */ + super->f.data.i3e.frame_control |= + cpu_to_le16(IEEE80211_FCTL_RETRY); + } + + if (txfail) { + /* Normal TX Failure */ + + /* demise descriptor ownership back to the hardware */ + dma_rearm(desc); + + /* + * And this will get the queue going again. + * To understand why: you have to get the HW + * specs... But sadly I never saw them. + */ + wlan_txunstuck(qidx); + + /* abort cycle - this is necessary due to HW design */ + return false; + } else { + /* (HT-) BlockACK failure */ + + /* + * Unlink the failed attempt and put it into + * the retry queue. The caller routine must + * be aware of this so the frames don't get lost. + */ + +#ifndef CONFIG_CARL9170FW_DEBUG + dma_unlink_head(queue); +#else /* CONFIG_CARL9170FW_DEBUG */ + BUG_ON(dma_unlink_head(queue) != desc); +#endif /* CONFIG_CARL9170FW_DEBUG */ + dma_put(&fw.wlan.tx_retry, desc); + return true; + } + } else { + /* out of frame attempts - discard frame */ + success = false; + } + } + +#ifndef CONFIG_CARL9170FW_DEBUG + dma_unlink_head(queue); +#else /* CONFIG_CARL9170FW_DEBUG */ + BUG_ON(dma_unlink_head(queue) != desc); +#endif /* CONFIG_CARL9170FW_DEBUG */ + if (txfail) { + /* + * Issue the queue bump, + * We need to do this in case this was the frame's last + * possible retry attempt and it unfortunately: it failed. + */ + + wlan_txunstuck(qidx); + } + + unhide_super(desc); + + if (unlikely(super == fw.wlan.fw_desc_data)) { + fw.wlan.fw_desc = desc; + fw.wlan.fw_desc_available = 1; + + if (fw.wlan.fw_desc_callback) + fw.wlan.fw_desc_callback(super, success); + + return true; + } + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + if (unlikely(super->s.cab)) + fw.wlan.cab_queue_len[super->s.vif_id]--; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + wlan_tx_complete(super, success); + + if (ieee80211_is_back_req(super->f.data.i3e.frame_control)) { + fw.wlan.queued_bar--; + } + + /* recycle freed descriptors */ + dma_reclaim(&fw.pta.down_queue, desc); + down_trigger(); + return true; +} + +static void handle_tx_completion(void) +{ + struct dma_desc *desc; + int i; + + for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) { + __while_desc_bits(desc, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW) { + if (!wlan_tx_status(&fw.wlan.tx_queue[i], desc)) { + /* termination requested. */ + break; + } + } + + wlan_tx_ampdu_reset(i); + + for_each_desc(desc, &fw.wlan.tx_retry) + __wlan_tx(desc); + + wlan_tx_ampdu_end(i); + if (!queue_empty(&fw.wlan.tx_queue[i])) + wlan_trigger(BIT(i)); + } +} + +void __hot wlan_tx(struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = DESC_PAYLOAD(desc); + + if (ieee80211_is_back_req(super->f.data.i3e.frame_control)) { + fw.wlan.queued_bar++; + } + + /* initialize rate control struct */ + super->s.rix = 0; + super->s.cnt = 1; + hide_super(desc); + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + if (unlikely(super->s.cab)) { + fw.wlan.cab_queue_len[super->s.vif_id]++; + dma_put(&fw.wlan.cab_queue[super->s.vif_id], desc); + return; + } +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + _wlan_tx(desc); + __wlan_tx(desc); + wlan_trigger(BIT(super->s.queue)); +} + +void wlan_tx_fw(struct carl9170_tx_superdesc *super, fw_desc_callback_t cb) +{ + if (!fw.wlan.fw_desc_available) + return; + + fw.wlan.fw_desc_available = 0; + + /* Format BlockAck */ + fw.wlan.fw_desc->ctrl = AR9170_CTRL_FS_BIT | AR9170_CTRL_LS_BIT; + fw.wlan.fw_desc->status = AR9170_OWN_BITS_SW; + + fw.wlan.fw_desc->totalLen = fw.wlan.fw_desc->dataSize = super->len; + fw.wlan.fw_desc_data = fw.wlan.fw_desc->dataAddr = super; + fw.wlan.fw_desc->nextAddr = fw.wlan.fw_desc->lastAddr = + fw.wlan.fw_desc; + fw.wlan.fw_desc_callback = cb; + wlan_tx(fw.wlan.fw_desc); +} + +static void wlan_send_buffered_ba(void) +{ + struct carl9170_tx_ba_superframe *baf = &dma_mem.reserved.ba.ba; + struct ieee80211_ba *ba = (struct ieee80211_ba *) &baf->f.ba; + struct carl9170_bar_ctx *ctx; + + if (likely(!fw.wlan.queued_ba)) + return; + + /* there's no point to continue when the ba_desc is not available. */ + if (!fw.wlan.fw_desc_available) + return; + + ctx = &fw.wlan.ba_cache[fw.wlan.ba_head_idx]; + fw.wlan.ba_head_idx++; + fw.wlan.ba_head_idx %= CONFIG_CARL9170FW_BACK_REQS_NUM; + fw.wlan.queued_ba--; + + baf->s.len = sizeof(struct carl9170_tx_superdesc) + + sizeof(struct ar9170_tx_hwdesc) + + sizeof(struct ieee80211_ba); + baf->s.ri[0].tries = 1; + baf->s.cookie = 0; + baf->s.queue = AR9170_TXQ_VO; + baf->f.hdr.length = sizeof(struct ieee80211_ba) + FCS_LEN; + + baf->f.hdr.mac.no_ack = 1; + + baf->f.hdr.phy.modulation = 1; /* OFDM */ + baf->f.hdr.phy.tx_power = 34; /* 17 dBm */ + baf->f.hdr.phy.chains = 1; + baf->f.hdr.phy.mcs = AR9170_TXRX_PHY_RATE_OFDM_6M; + + /* format outgoing BA */ + ba->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); + ba->duration = cpu_to_le16(0); + + /* the BAR contains all necessary MACs. All we need is to swap them */ + memcpy(ba->ra, ctx->ta, 6); + memcpy(ba->ta, ctx->ra, 6); + + /* + * Unfortunately, we cannot look into the hardware's scoreboard. + * Therefore we have to proceed as described in 802.11n 9.10.7.5 + * and send a null BlockAck. + */ + memset(ba->bitmap, 0x0, sizeof(ba->bitmap)); + + /* + * Both, the original firmare and ath9k set the NO ACK flag in + * the BA Ack Policy subfield. + */ + ba->control = ctx->control | cpu_to_le16(1); + ba->start_seq_num = ctx->start_seq_num; + wlan_tx_fw(&baf->s, NULL); +} + +static struct carl9170_bar_ctx *wlan_get_bar_cache_buffer(void) +{ + struct carl9170_bar_ctx *tmp; + + tmp = &fw.wlan.ba_cache[fw.wlan.ba_tail_idx]; + fw.wlan.ba_tail_idx++; + fw.wlan.ba_tail_idx %= CONFIG_CARL9170FW_BACK_REQS_NUM; + if (fw.wlan.queued_ba < CONFIG_CARL9170FW_BACK_REQS_NUM) + fw.wlan.queued_ba++; + + return tmp; +} + +static void handle_bar(struct dma_desc *desc __unused, struct ieee80211_hdr *hdr, + unsigned int len, unsigned int mac_err) +{ + struct ieee80211_bar *bar; + struct carl9170_bar_ctx *ctx; + + if (unlikely(mac_err)) { + /* + * This check does a number of things: + * 1. checks if the frame is in good nick + * 2. checks if the RA (MAC) matches + */ + return ; + } + + if (unlikely(len < (sizeof(struct ieee80211_bar) + FCS_LEN))) { + /* + * Sneaky, corrupted BARs... but not with us! + */ + + return ; + } + + bar = (void *) hdr; + + if ((bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_MULTI_TID)) || + !(bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA))) { + /* not implemented yet */ + + return ; + } + + ctx = wlan_get_bar_cache_buffer(); + + memcpy(ctx->ra, bar->ra, 6); + memcpy(ctx->ta, bar->ta, 6); + ctx->control = bar->control; + ctx->start_seq_num = bar->start_seq_num; +} + +static void wlan_check_rx_overrun(void) +{ + uint32_t overruns, total; + + fw.tally.rx_total += total = get(AR9170_MAC_REG_RX_TOTAL); + fw.tally.rx_overrun += overruns = get(AR9170_MAC_REG_RX_OVERRUN); + if (unlikely(overruns)) { + if (overruns == total) { + DBG("RX Overrun"); + fw.wlan.mac_reset++; + } + + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } +} + +static unsigned int wlan_rx_filter(struct dma_desc *desc) +{ + struct ieee80211_hdr *hdr; + unsigned int data_len; + unsigned int rx_filter; + unsigned int mac_err; + + data_len = ar9170_get_rx_mpdu_len(desc); + mac_err = ar9170_get_rx_macstatus_error(desc); + +#define AR9170_RX_ERROR_BAD (AR9170_RX_ERROR_FCS | AR9170_RX_ERROR_PLCP) + + if (unlikely(data_len < (4 + 6 + FCS_LEN) || + desc->totalLen > CONFIG_CARL9170FW_RX_FRAME_LEN) || + mac_err & AR9170_RX_ERROR_BAD) { + /* + * This frame is too damaged to do anything + * useful with it. + */ + + return CARL9170_RX_FILTER_BAD; + } + + rx_filter = 0; + if (mac_err & AR9170_RX_ERROR_WRONG_RA) + rx_filter |= CARL9170_RX_FILTER_OTHER_RA; + + if (mac_err & AR9170_RX_ERROR_DECRYPT) + rx_filter |= CARL9170_RX_FILTER_DECRY_FAIL; + + hdr = ar9170_get_rx_i3e(desc); + if (likely(ieee80211_is_data(hdr->frame_control))) { + rx_filter |= CARL9170_RX_FILTER_DATA; + } else if (ieee80211_is_ctl(hdr->frame_control)) { + switch (le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BACK_REQ: + handle_bar(desc, hdr, data_len, mac_err); + rx_filter |= CARL9170_RX_FILTER_CTL_BACKR; + break; + case IEEE80211_STYPE_PSPOLL: + rx_filter |= CARL9170_RX_FILTER_CTL_PSPOLL; + break; + case IEEE80211_STYPE_BACK: + if (fw.wlan.queued_bar) { + /* + * Don't filter block acks when the application + * has queued BARs. This is because the firmware + * can't do the accouting and the application + * has to sort out if the BA belongs to any BARs. + */ + break; + } + /* otherwise fall through */ + default: + rx_filter |= CARL9170_RX_FILTER_CTL_OTHER; + break; + } + } else { + /* ieee80211_is_mgmt */ + rx_filter |= CARL9170_RX_FILTER_MGMT; + } + + if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) { + wol_rx(rx_filter, hdr, min(data_len, + (unsigned int)AR9170_BLOCK_SIZE)); + } + +#undef AR9170_RX_ERROR_BAD + + return rx_filter; +} + +static void handle_rx(void) +{ + struct dma_desc *desc; + + for_each_desc_not_bits(desc, &fw.wlan.rx_queue, AR9170_OWN_BITS_HW) { + if (!(wlan_rx_filter(desc) & fw.wlan.rx_filter)) { + dma_put(&fw.pta.up_queue, desc); + up_trigger(); + } else { + dma_reclaim(&fw.wlan.rx_queue, desc); + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } + } +} + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE +void wlan_cab_flush_queue(const unsigned int vif) +{ + struct dma_queue *cab_queue = &fw.wlan.cab_queue[vif]; + struct dma_desc *desc; + + /* move queued frames into the main tx queues */ + for_each_desc(desc, cab_queue) { + struct carl9170_tx_superframe *super = get_super(desc); + if (!queue_empty(cab_queue)) { + /* + * Set MOREDATA flag for all, + * but the last queued frame. + * see: 802.11-2007 11.2.1.5 f) + * + * This is actually the reason to why + * we need to prevent the reentry. + */ + + super->f.data.i3e.frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else { + super->f.data.i3e.frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + } + + /* ready to roll! */ + _wlan_tx(desc); + __wlan_tx(desc); + wlan_trigger(BIT(super->s.queue)); + } +} + +static uint8_t *beacon_find_ie(uint8_t ie, void *addr, + const unsigned int len) +{ + struct ieee80211_mgmt *mgmt = addr; + uint8_t *pos, *end; + + pos = mgmt->u.beacon.variable; + end = (uint8_t *) ((unsigned long)mgmt + (len - FCS_LEN)); + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += pos[1] + 2; + } + + return NULL; +} + +void wlan_modify_beacon(const unsigned int vif, + const unsigned int addr, const unsigned int len) +{ + uint8_t *_ie; + struct ieee80211_tim_ie *ie; + + _ie = beacon_find_ie(WLAN_EID_TIM, (void *)addr, len); + if (likely(_ie)) { + ie = (struct ieee80211_tim_ie *) &_ie[2]; + + if (!queue_empty(&fw.wlan.cab_queue[vif]) && (ie->dtim_count == 0)) { + /* schedule DTIM transfer */ + fw.wlan.cab_flush_trigger[vif] = CARL9170_CAB_TRIGGER_ARMED; + } else if ((fw.wlan.cab_queue_len[vif] == 0) && (fw.wlan.cab_flush_trigger[vif])) { + /* undo all chances to the beacon structure */ + ie->bitmap_ctrl &= ~0x1; + fw.wlan.cab_flush_trigger[vif] = CARL9170_CAB_TRIGGER_EMPTY; + } + + /* Triggered by CARL9170_CAB_TRIGGER_ARMED || CARL9170_CAB_TRIGGER_DEFER */ + if (fw.wlan.cab_flush_trigger[vif]) { + /* Set the almighty Multicast Traffic Indication Bit. */ + ie->bitmap_ctrl |= 0x1; + } + } + + /* + * Ideally, the sequence number should be assigned by the TX arbiter + * hardware. But AFAIK that's not possible, so we have to go for the + * next best thing and write it into the beacon fifo during the open + * beacon update window. + */ + + wlan_assign_seq((struct ieee80211_hdr *)addr, vif); +} + +static void wlan_send_buffered_cab(void) +{ + unsigned int i; + + for (i = 0; i < CARL9170_INTF_NUM; i++) { + if (unlikely(fw.wlan.cab_flush_trigger[i] == CARL9170_CAB_TRIGGER_ARMED)) { + /* + * This is hardcoded into carl9170usb driver. + * + * The driver must set the PRETBTT event to beacon_interval - + * CARL9170_PRETBTT_KUS (usually 6) Kus. + * + * But still, we can only do so much about 802.11-2007 9.3.2.1 & + * 11.2.1.6. Let's hope the current solution is adequate enough. + */ + + if (is_after_msecs(fw.wlan.cab_flush_time, (CARL9170_TBTT_DELTA))) { + wlan_cab_flush_queue(i); + + /* + * This prevents the code from sending new BC/MC frames + * which were queued after the previous buffered traffic + * has been sent out... They will have to wait until the + * next DTIM beacon comes along. + */ + fw.wlan.cab_flush_trigger[i] = CARL9170_CAB_TRIGGER_DEFER; + } + } + + } +} +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +static void handle_beacon_config(void) +{ + uint32_t bcn_count; + + bcn_count = get(AR9170_MAC_REG_BCN_COUNT); + send_cmd_to_host(4, CARL9170_RSP_BEACON_CONFIG, 0x00, + (uint8_t *) &bcn_count); +} + +static void handle_pretbtt(void) +{ +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + fw.wlan.cab_flush_time = get_clock_counter(); +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + rf_psm(); + + send_cmd_to_host(4, CARL9170_RSP_PRETBTT, 0x00, + (uint8_t *) &fw.phy.psm.state); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ +} + +static void handle_atim(void) +{ + send_cmd_to_host(0, CARL9170_RSP_ATIM, 0x00, NULL); +} + +#ifdef CONFIG_CARL9170FW_DEBUG +static void handle_qos(void) +{ + /* + * What is the QoS Bit used for? + * Is it only an indicator for TXOP & Burst, or + * should we do something here? + */ +} + +static void handle_radar(void) +{ + send_cmd_to_host(0, CARL9170_RSP_RADAR, 0x00, NULL); +} +#endif /* CONFIG_CARL9170FW_DEBUG */ + +static void wlan_janitor(void) +{ +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + wlan_send_buffered_cab(); +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + wlan_send_buffered_tx_status(); + + wlan_send_buffered_ba(); + + wol_janitor(); +} + +void handle_wlan(void) +{ + uint32_t intr; + + intr = get(AR9170_MAC_REG_INT_CTRL); + /* ACK Interrupt */ + set(AR9170_MAC_REG_INT_CTRL, intr); + +#define HANDLER(intr, flag, func) \ + do { \ + if ((intr & flag) != 0) { \ + func(); \ + } \ + } while (0) + + intr |= fw.wlan.soft_int; + fw.wlan.soft_int = 0; + + HANDLER(intr, AR9170_MAC_INT_PRETBTT, handle_pretbtt); + + HANDLER(intr, AR9170_MAC_INT_ATIM, handle_atim); + + HANDLER(intr, AR9170_MAC_INT_RXC, handle_rx); + + HANDLER(intr, (AR9170_MAC_INT_TXC | AR9170_MAC_INT_RETRY_FAIL), + handle_tx_completion); + +#ifdef CONFIG_CARL9170FW_DEBUG + HANDLER(intr, AR9170_MAC_INT_QOS, handle_qos); + + HANDLER(intr, AR9170_MAC_INT_RADAR, handle_radar); +#endif /* CONFIG_CARL9170FW_DEBUG */ + + HANDLER(intr, AR9170_MAC_INT_CFG_BCN, handle_beacon_config); + + if (unlikely(intr)) + DBG("Unhandled Interrupt %x\n", (unsigned int) intr); + + wlan_janitor(); + +#undef HANDLER +} + +enum { + CARL9170FW_TX_MAC_BUMP = 4, + CARL9170FW_TX_MAC_DEBUG = 6, + CARL9170FW_TX_MAC_RESET = 7, +}; + +static void wlan_check_hang(void) +{ + struct dma_desc *desc; + int i; + + for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) { + if (queue_empty(&fw.wlan.tx_queue[i])) { + /* Nothing to do here... move along */ + continue; + } + + /* fetch the current DMA queue position */ + desc = (struct dma_desc *)get_wlan_txq_addr(i); + + /* Stuck frame detection */ + if (unlikely(DESC_PAYLOAD(desc) == fw.wlan.last_super[i])) { + fw.wlan.last_super_num[i]++; + + if (unlikely(fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_RESET)) { + /* + * schedule MAC reset (aka OFF/ON => dead) + * + * This will almost certainly kill + * the device for good, but it's the + * recommended thing to do... + */ + + fw.wlan.mac_reset++; + } + +#ifdef CONFIG_CARL9170FW_DEBUG + if (unlikely(fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_DEBUG)) { + /* + * Sigh, the queue is almost certainly + * dead. Dump the queue content to the + * user, maybe we find out why it got + * so stuck. + */ + + wlan_dump_queue(i); + } +#endif /* CONFIG_CARL9170FW_DEBUG */ + +#ifdef CONFIG_CARL9170FW_DMA_QUEUE_BUMP + if (unlikely(fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_BUMP)) { + /* + * Hrrm, bump the queue a bit. + * maybe this will get it going again. + */ + + wlan_dma_bump(i); + wlan_trigger(BIT(i)); + } +#endif /* CONFIG_CARL9170FW_DMA_QUEUE_BUMP */ + } else { + /* Nothing stuck */ + fw.wlan.last_super[i] = DESC_PAYLOAD(desc); + fw.wlan.last_super_num[i] = 0; + } + } +} + +#ifdef CONFIG_CARL9170FW_FW_MAC_RESET +/* + * NB: Resetting the MAC is a two-edged sword. + * On most occasions, it does what it is supposed to do. + * But there is a chance that this will make it + * even worse and the radio dies silently. + */ +static void wlan_mac_reset(void) +{ + uint32_t val; + uint32_t agg_wait_counter; + uint32_t agg_density; + uint32_t bcn_start_addr; + uint32_t rctl, rcth; + uint32_t cam_mode; + uint32_t ack_power; + uint32_t rts_cts_tpc; + uint32_t rts_cts_rate; + int i; + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + uint32_t rx_BB; +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + +#ifdef CONFIG_CARL9170FW_NOISY_MAC_RESET + INFO("MAC RESET"); +#endif /* CONFIG_CARL9170FW_NOISY_MAC_RESET */ + + /* Save aggregation parameters */ + agg_wait_counter = get(AR9170_MAC_REG_AMPDU_FACTOR); + agg_density = get(AR9170_MAC_REG_AMPDU_DENSITY); + + bcn_start_addr = get(AR9170_MAC_REG_BCN_ADDR); + + cam_mode = get(AR9170_MAC_REG_CAM_MODE); + rctl = get(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L); + rcth = get(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H); + + ack_power = get(AR9170_MAC_REG_ACK_TPC); + rts_cts_tpc = get(AR9170_MAC_REG_RTS_CTS_TPC); + rts_cts_rate = get(AR9170_MAC_REG_RTS_CTS_RATE); + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + /* 0x1c8960 write only */ + rx_BB = get(AR9170_PHY_REG_SWITCH_CHAIN_0); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + /* TX/RX must be stopped by now */ + val = get(AR9170_MAC_REG_POWER_STATE_CTRL); + + val |= AR9170_MAC_POWER_STATE_CTRL_RESET; + + /* + * Manipulate CCA threshold to stop transmission + * + * set(AR9170_PHY_REG_CCA_THRESHOLD, 0x300); + */ + + /* + * check Rx state in 0(idle) 9(disable) + * + * chState = (get(AR9170_MAC_REG_MISC_684) >> 16) & 0xf; + * while( (chState != 0) && (chState != 9)) { + * chState = (get(AR9170_MAC_REG_MISC_684) >> 16) & 0xf; + * } + */ + + set(AR9170_MAC_REG_POWER_STATE_CTRL, val); + + delay(2); + + /* Restore aggregation parameters */ + set(AR9170_MAC_REG_AMPDU_FACTOR, agg_wait_counter); + set(AR9170_MAC_REG_AMPDU_DENSITY, agg_density); + + set(AR9170_MAC_REG_BCN_ADDR, bcn_start_addr); + set(AR9170_MAC_REG_CAM_MODE, cam_mode); + set(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, rctl); + set(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, rcth); + + set(AR9170_MAC_REG_RTS_CTS_TPC, rts_cts_tpc); + set(AR9170_MAC_REG_ACK_TPC, ack_power); + set(AR9170_MAC_REG_RTS_CTS_RATE, rts_cts_rate); + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + set(AR9170_PHY_REG_SWITCH_CHAIN_2, rx_BB); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + /* + * Manipulate CCA threshold to resume transmission + * + * set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0); + */ + + val = AR9170_DMA_TRIGGER_RXQ; + /* Reinitialize all WLAN TX DMA queues. */ + for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) { + struct dma_desc *iter; + + __for_each_desc_bits(iter, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW); + + /* kill the stuck frame */ + if (!is_terminator(&fw.wlan.tx_queue[i], iter) && + fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_RESET && + fw.wlan.last_super[i] == DESC_PAYLOAD(iter)) { + struct carl9170_tx_superframe *super = get_super(iter); + + iter->status = AR9170_OWN_BITS_SW; + /* + * Mark the frame as failed. + * The BAFAIL flag allows the frame to sail through + * wlan_tx_status without much "unstuck" trouble. + */ + iter->ctrl &= ~(AR9170_CTRL_FAIL); + iter->ctrl |= AR9170_CTRL_BAFAIL; + + super->s.cnt = CARL9170_TX_MAX_RATE_TRIES; + super->s.rix = CARL9170_TX_MAX_RETRY_RATES; + + fw.wlan.last_super_num[i] = 0; + fw.wlan.last_super[i] = NULL; + iter = iter->lastAddr->nextAddr; + } + + set_wlan_txq_dma_addr(i, (uint32_t) iter); + if (!is_terminator(&fw.wlan.tx_queue[i], iter)) + val |= BIT(i); + + DBG("Q:%d l:%d h:%p t:%p cu:%p it:%p ct:%x st:%x\n", i, queue_len(&fw.wlan.tx_queue[i]), + fw.wlan.tx_queue[i].head, fw.wlan.tx_queue[i].terminator, + get_wlan_txq_addr(i), iter, iter->ctrl, iter->status); + } + + fw.wlan.soft_int |= AR9170_MAC_INT_RXC | AR9170_MAC_INT_TXC | + AR9170_MAC_INT_RETRY_FAIL; + + set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head); + wlan_trigger(val); +} +#else +static void wlan_mac_reset(void) +{ + /* The driver takes care of reinitializing the device */ + BUG("MAC RESET"); +} +#endif /* CONFIG_CARL9170FW_FW_MAC_RESET */ + +void __cold wlan_timer(void) +{ + unsigned int cached_mac_reset; + + cached_mac_reset = fw.wlan.mac_reset; + + /* TX Queue Hang check */ + wlan_check_hang(); + + /* RX Overrun check */ + wlan_check_rx_overrun(); + + if (unlikely(fw.wlan.mac_reset >= CARL9170_MAC_RESET_RESET)) { + wlan_mac_reset(); + fw.wlan.mac_reset = CARL9170_MAC_RESET_OFF; + } else { + if (fw.wlan.mac_reset && cached_mac_reset == fw.wlan.mac_reset) + fw.wlan.mac_reset--; + } +} diff --git a/carl9170fw/carlfw/src/wol.c b/carl9170fw/carlfw/src/wol.c new file mode 100644 index 0000000..7afb8e5 --- /dev/null +++ b/carl9170fw/carlfw/src/wol.c @@ -0,0 +1,287 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WakeUp on WLAN functions + * + * Copyright 2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "shared/phy.h" +#include "timer.h" +#include "wl.h" +#include "printf.h" +#include "rf.h" +#include "wol.h" +#include "linux/ieee80211.h" + +#ifdef CONFIG_CARL9170FW_WOL + +void wol_cmd(const struct carl9170_wol_cmd *cmd) +{ + memcpy(&fw.wol.cmd, cmd, sizeof(cmd)); +} + +void wol_prepare(void) +{ + /* set MAC filter */ + memcpy((void *)AR9170_MAC_REG_MAC_ADDR_L, fw.wol.cmd.mac, 6); + memcpy((void *)AR9170_MAC_REG_BSSID_L, fw.wol.cmd.bssid, 6); + set(AR9170_MAC_REG_RX_CONTROL, AR9170_MAC_RX_CTRL_DEAGG); + + /* set filter policy to: discard everything */ + fw.wlan.rx_filter = CARL9170_RX_FILTER_EVERYTHING; + + /* reenable rx dma */ + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + + /* initialize the last_beacon timer */ + fw.wol.last_null = fw.wol.last_beacon = get_clock_counter(); +} + +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS +static bool wlan_rx_wol_magic_packet(const struct ieee80211_hdr *hdr, const unsigned int len) +{ + const unsigned char *data, *end, *mac; + unsigned int found = 0; + + /* + * LIMITATION: + * We can only scan the first AR9170_BLOCK_SIZE [=~320] bytes + * for MAGIC patterns! + */ + + mac = (const unsigned char *) AR9170_MAC_REG_MAC_ADDR_L; + + data = (u8 *)((unsigned long)hdr + ieee80211_hdrlen(hdr->frame_control)); + end = (u8 *)((unsigned long)hdr + len); + + /* + * scan for standard WOL Magic frame + * + * "A physical WakeOnLAN (Magic Packet) will look like this: + * --------------------------------------------------------------- + * | Synchronization Stream | Target MAC | Password (optional) | + * | 6 octets | 96 octets | 0, 4 or 6 | + * --------------------------------------------------------------- + * + * The Synchronization Stream is defined as 6 bytes of FFh. + * The Target MAC block contains 16 duplications of the IEEEaddress + * of the target, with no breaks or interruptions. + * + * The Password field is optional, but if present, contains either + * 4 bytes or 6 bytes. The WakeOnLAN dissector was implemented to + * dissect the password, if present, according to the command-line + * format that ether-wake uses, therefore, if a 4-byte password is + * present, it will be dissected as an IPv4 address and if a 6-byte + * password is present, it will be dissected as an Ethernet address. + * + * <http://wiki.wireshark.org/WakeOnLAN> + */ + + while (data < end) { + if (found >= 6) { + if (*data == mac[found % 6]) + found++; + else + found = 0; + } + + /* previous check might reset found counter */ + if (found < 6) { + if (*data == 0xff) + found++; + else + found = 0; + } + + if (found == (6 + 16 * 6)) + return true; + + data++; + } + + return false; +} + +static void wlan_wol_connect_callback(void __unused *dummy, bool success) +{ + if (success) + fw.wol.lost_null = 0; + else + fw.wol.lost_null++; +} + +static void wlan_wol_connection_monitor(void) +{ + struct carl9170_tx_null_superframe *nullf = &dma_mem.reserved.cmd.null; + struct ieee80211_hdr *null = (struct ieee80211_hdr *) &nullf->f.null; + + if (!fw.wlan.fw_desc_available) + return; + + memset(nullf, 0, sizeof(*nullf)); + + nullf->s.len = sizeof(struct carl9170_tx_superdesc) + + sizeof(struct ar9170_tx_hwdesc) + + sizeof(struct ieee80211_hdr); + nullf->s.ri[0].tries = 3; + nullf->s.assign_seq = true; + nullf->s.queue = AR9170_TXQ_VO; + nullf->f.hdr.length = sizeof(struct ieee80211_hdr) + FCS_LEN; + + nullf->f.hdr.mac.backoff = 1; + nullf->f.hdr.mac.hw_duration = 1; + nullf->f.hdr.mac.erp_prot = AR9170_TX_MAC_PROT_RTS; + + nullf->f.hdr.phy.modulation = AR9170_TX_PHY_MOD_OFDM; + nullf->f.hdr.phy.bandwidth = AR9170_TX_PHY_BW_20MHZ; + nullf->f.hdr.phy.chains = AR9170_TX_PHY_TXCHAIN_2; + nullf->f.hdr.phy.tx_power = 29; /* 14.5 dBm */ + nullf->f.hdr.phy.mcs = AR9170_TXRX_PHY_RATE_OFDM_6M; + + /* format outgoing nullfunc */ + null->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS); + + memcpy(null->addr1, fw.wol.cmd.bssid, 6); + memcpy(null->addr2, fw.wol.cmd.mac, 6); + memcpy(null->addr3, fw.wol.cmd.bssid, 6); + + wlan_tx_fw(&nullf->s, wlan_wol_connect_callback); +} + +static bool wlan_rx_wol_disconnect(const unsigned int rx_filter, + const struct ieee80211_hdr *hdr, + const unsigned int __unused len) +{ + const unsigned char *bssid; + bssid = (const unsigned char *) AR9170_MAC_REG_BSSID_L; + + /* should catch both broadcast and unicast MLMEs */ + if (!(rx_filter & CARL9170_RX_FILTER_OTHER_RA)) { + if (ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) + return true; + } + + if (ieee80211_is_beacon(hdr->frame_control) && + compare_ether_address(hdr->addr3, bssid)) { + fw.wol.last_beacon = get_clock_counter(); + } + + return false; +} + +#endif /* CARL9170FW_WOL_NL80211_TRIGGERS */ + +#ifdef CONFIG_CARL9170FW_WOL_PROBE_REQUEST + +/* + * Note: CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID is not a real + * string. We have to be careful not to add a \0 at the end. + */ +static const struct { + u8 ssid_ie; + u8 ssid_len; + u8 ssid[sizeof(CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID) - 1]; +} __packed probe_req = { + .ssid_ie = WLAN_EID_SSID, + .ssid_len = sizeof(CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID) - 1, + .ssid = CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID, +}; + +static bool wlan_rx_wol_probe_ssid(const struct ieee80211_hdr *hdr, const unsigned int len) +{ + const unsigned char *data, *end, *scan = (void *) &probe_req; + + /* + * IEEE 802.11-2007 7.3.2.1 specifies that the SSID is no + * longer than 32 octets. + */ + BUILD_BUG_ON((sizeof(CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID) - 1) > 32); + + if (ieee80211_is_probe_req(hdr->frame_control)) { + unsigned int i; + end = (u8 *)((unsigned long)hdr + len); + + /* + * The position of the SSID information element inside + * a probe request frame is more or less "fixed". + */ + data = (u8 *)((struct ieee80211_mgmt *)hdr)->u.probe_req.variable; + for (i = 0; i < (unsigned int)(probe_req.ssid_len + 1); i++) { + if (data > end || scan[i] != data[i]) + return false; + } + + return true; + } + + return false; +} +#endif /* CONFIG_CARL9170FW_WOL_PROBE_REQUEST */ + +void wol_rx(const unsigned int rx_filter __unused, const struct ieee80211_hdr *hdr __unused, const unsigned int len __unused) +{ +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS + /* Disconnect is always enabled */ + if (fw.wol.cmd.flags & CARL9170_WOL_DISCONNECT && + rx_filter & CARL9170_RX_FILTER_MGMT) + fw.wol.wake_up |= wlan_rx_wol_disconnect(rx_filter, hdr, len); + + if (fw.wol.cmd.flags & CARL9170_WOL_MAGIC_PKT && + rx_filter & CARL9170_RX_FILTER_DATA) + fw.wol.wake_up |= wlan_rx_wol_magic_packet(hdr, len); +#endif /* CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS */ + +#ifdef CONFIG_CARL9170FW_WOL_PROBE_REQUEST + if (rx_filter & CARL9170_RX_FILTER_MGMT) + fw.wol.wake_up |= wlan_rx_wol_probe_ssid(hdr, len); +#endif /* CONFIG_CARL9170FW_WOL_PROBE_REQUEST */ +} + +void wol_janitor(void) +{ + if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) { +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS + if (fw.wol.cmd.flags & CARL9170_WOL_DISCONNECT) { + /* + * connection lost after 10sec without receiving + * a beacon + */ + if (is_after_msecs(fw.wol.last_beacon, 10000)) + fw.wol.wake_up |= true; + + if (fw.wol.cmd.null_interval && + is_after_msecs(fw.wol.last_null, fw.wol.cmd.null_interval)) + wlan_wol_connection_monitor(); + + if (fw.wol.lost_null >= 5) + fw.wol.wake_up |= true; + } +#endif /* CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS */ + + if (fw.wol.wake_up) { + fw.suspend_mode = CARL9170_AWAKE_HOST; + set(AR9170_USB_REG_WAKE_UP, AR9170_USB_WAKE_UP_WAKE); + } + } +} +#else + +#endif /* CONFIG_CARL9170FW_WOL */ diff --git a/carl9170fw/carlfw/usb/Kconfig b/carl9170fw/carlfw/usb/Kconfig new file mode 100644 index 0000000..dc70776 --- /dev/null +++ b/carl9170fw/carlfw/usb/Kconfig @@ -0,0 +1,37 @@ +menu "USB Firmware Configuration Settings" + +config CARL9170FW_USB_STANDARD_CMDS + def_bool y + prompt "Basic USB Interface" + ---help--- + Allows the device to be queried about Standard USB 2.0 Device + Description Descriptors. + + Say Y, unless you don't care if lsusb -v fails. + +config CARL9170FW_USB_UP_STREAM + def_bool y + prompt "USB Upload Stream" + ---help--- + This features allows the USB silicon to combine small, single + frames into bigger transfers. This can help to reduce + some per-transfer overhead in the application. + + Say Y, unless you have experienced strange rx corruptions. + +config CARL9170FW_USB_DN_STREAM + def_bool n + prompt "USB Download Stream" + +config CARL9170FW_DEBUG_USB + def_bool y + prompt "Pass debug messages through USB transport" + ---help--- + Report all firmware messages through the USB transport. + But there is a catch: In case of a BUG, the USB transport + needs to be functional, otherwise the application won't + receive anything. + + Say Y. + +endmenu diff --git a/carl9170fw/carlfw/usb/fifo.c b/carl9170fw/carlfw/usb/fifo.c new file mode 100644 index 0000000..d5c2e12 --- /dev/null +++ b/carl9170fw/carlfw/usb/fifo.c @@ -0,0 +1,206 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "printf.h" +#include "rom.h" +#include "usb_fifo.h" + +/* TODO / TOTEST */ +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static inline void usb_ep_map(const uint8_t ep, const uint8_t map) +{ + setb(AR9170_USB_REG_EP_MAP + (ep - 1), map); +} + +static inline void usb_fifo_map(const uint8_t fifo, const uint8_t map) +{ + setb(AR9170_USB_REG_FIFO_MAP + (fifo - 1), map); +} + +static inline void usb_fifo_config(const uint8_t fifo, const uint8_t cfg) +{ + setb(AR9170_USB_REG_FIFO_CONFIG + (fifo - 1), cfg); +} + +static inline void usb_ep_packet_size_hi(const uint8_t ep, const uint8_t dir, + const uint16_t size) +{ + setb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (((dir * 0x20) + ep) << 1), + (size >> 8) & 0xf); +} + +static inline void usb_ep_packet_size_lo(const uint8_t ep, const uint8_t dir, + const uint16_t size) +{ + setb(AR9170_USB_REG_EP_IN_MAX_SIZE_LOW + (((dir * 0x20) + ep) << 1), + size & 0xff); +} + +static void usb_ep_in_highbandset(const uint8_t ep, const uint8_t dir, + const uint16_t size) +{ + andb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), ~(BIT(6) | BIT(5))); + + switch (dir) { + case DIRECTION_IN: + setb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), + ((size >> 11) + 1) << 5); + break; + case DIRECTION_OUT: + default: + break; + } +} + +/* + * vUsbFIFO_EPxCfg_HS(void) + * Description: + * 1. Configure the FIFO and EPx map + * input: none + * output: none + */ + +void usb_init_highspeed_fifo_cfg(void) +{ + int i; + + /* EP 1 */ + usb_ep_map(1, HS_C1_I0_A0_EP1_MAP); + usb_fifo_map(HS_C1_I0_A0_EP1_FIFO_START, HS_C1_I0_A0_EP1_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP1_FIFO_START, HS_C1_I0_A0_EP1_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP1_FIFO_START + 1; + i < HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP1_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(1, HS_C1_I0_A0_EP1_DIRECTION, (HS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(1, HS_C1_I0_A0_EP1_DIRECTION, (HS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(1, HS_C1_I0_A0_EP1_DIRECTION, HS_C1_I0_A0_EP1_MAX_PACKET); + + /* EP 2 */ + usb_ep_map(2, HS_C1_I0_A0_EP2_MAP); + usb_fifo_map(HS_C1_I0_A0_EP2_FIFO_START, HS_C1_I0_A0_EP2_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP2_FIFO_START, HS_C1_I0_A0_EP2_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP2_FIFO_START + 1; + i < HS_C1_I0_A0_EP2_FIFO_START + HS_C1_I0_A0_EP2_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP2_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(2, HS_C1_I0_A0_EP2_DIRECTION, (HS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(2, HS_C1_I0_A0_EP2_DIRECTION, (HS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(2, HS_C1_I0_A0_EP2_DIRECTION, HS_C1_I0_A0_EP2_MAX_PACKET); + + /* EP 3 */ + usb_ep_map(3, HS_C1_I0_A0_EP3_MAP); + usb_fifo_map(HS_C1_I0_A0_EP3_FIFO_START, HS_C1_I0_A0_EP3_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP3_FIFO_START, HS_C1_I0_A0_EP3_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP3_FIFO_START + 1; + i < HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP3_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(3, HS_C1_I0_A0_EP3_DIRECTION, (HS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(3, HS_C1_I0_A0_EP3_DIRECTION, (HS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(3, HS_C1_I0_A0_EP3_DIRECTION, HS_C1_I0_A0_EP3_MAX_PACKET); + + /* EP 4 */ + usb_ep_map(4, HS_C1_I0_A0_EP4_MAP); + usb_fifo_map(HS_C1_I0_A0_EP4_FIFO_START, HS_C1_I0_A0_EP4_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP4_FIFO_START, HS_C1_I0_A0_EP4_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP4_FIFO_START + 1; + i < HS_C1_I0_A0_EP4_FIFO_START + HS_C1_I0_A0_EP4_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP4_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(4, HS_C1_I0_A0_EP4_DIRECTION, (HS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(4, HS_C1_I0_A0_EP4_DIRECTION, (HS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(4, HS_C1_I0_A0_EP4_DIRECTION, HS_C1_I0_A0_EP4_MAX_PACKET); +} + +void usb_init_fullspeed_fifo_cfg(void) +{ + int i; + + /* EP 1 */ + usb_ep_map(1, FS_C1_I0_A0_EP1_MAP); + usb_fifo_map(FS_C1_I0_A0_EP1_FIFO_START, FS_C1_I0_A0_EP1_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP1_FIFO_START, FS_C1_I0_A0_EP1_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP1_FIFO_START + 1; + i < FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP1_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(1, FS_C1_I0_A0_EP1_DIRECTION, (FS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(1, FS_C1_I0_A0_EP1_DIRECTION, (FS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + /* ``.JWEI 2003/04/29 */ + usb_ep_in_highbandset(1, FS_C1_I0_A0_EP1_DIRECTION, FS_C1_I0_A0_EP1_MAX_PACKET); + + /* EP 2 */ + usb_ep_map(2, FS_C1_I0_A0_EP2_MAP); + usb_fifo_map(FS_C1_I0_A0_EP2_FIFO_START, FS_C1_I0_A0_EP2_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP2_FIFO_START, FS_C1_I0_A0_EP2_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP2_FIFO_START + 1; + i < FS_C1_I0_A0_EP2_FIFO_START + FS_C1_I0_A0_EP2_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP2_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(2, FS_C1_I0_A0_EP2_DIRECTION, (FS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(2, FS_C1_I0_A0_EP2_DIRECTION, (FS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(2, FS_C1_I0_A0_EP2_DIRECTION, FS_C1_I0_A0_EP2_MAX_PACKET); + + /* EP 3 */ + usb_ep_map(3, FS_C1_I0_A0_EP3_MAP); + usb_fifo_map(FS_C1_I0_A0_EP3_FIFO_START, FS_C1_I0_A0_EP3_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP3_FIFO_START, FS_C1_I0_A0_EP3_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP3_FIFO_START + 1; + i < FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP3_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(3, FS_C1_I0_A0_EP3_DIRECTION, (FS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(3, FS_C1_I0_A0_EP3_DIRECTION, (FS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(3, FS_C1_I0_A0_EP3_DIRECTION, FS_C1_I0_A0_EP3_MAX_PACKET); + + /* EP 4 */ + usb_ep_map(4, FS_C1_I0_A0_EP4_MAP); + usb_fifo_map(FS_C1_I0_A0_EP4_FIFO_START, FS_C1_I0_A0_EP4_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP4_FIFO_START, FS_C1_I0_A0_EP4_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP4_FIFO_START + 1; + i < FS_C1_I0_A0_EP4_FIFO_START + FS_C1_I0_A0_EP4_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP4_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(4, FS_C1_I0_A0_EP4_DIRECTION, (FS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(4, FS_C1_I0_A0_EP4_DIRECTION, (FS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(4, FS_C1_I0_A0_EP4_DIRECTION, FS_C1_I0_A0_EP4_MAX_PACKET); +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ diff --git a/carl9170fw/carlfw/usb/main.c b/carl9170fw/carlfw/usb/main.c new file mode 100644 index 0000000..890970c --- /dev/null +++ b/carl9170fw/carlfw/usb/main.c @@ -0,0 +1,432 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" + +#include "shared/phy.h" +#include "hostif.h" +#include "printf.h" +#include "timer.h" +#include "rom.h" +#include "wl.h" +#include "wol.h" + +#ifdef CONFIG_CARL9170FW_DEBUG_USB +void usb_putc(const char c) +{ + fw.usb.put_buffer[fw.usb.put_index++] = (uint8_t) c; + + if (fw.usb.put_index == CARL9170_MAX_CMD_PAYLOAD_LEN || c == '\0') { + fw.usb.put_buffer[fw.usb.put_index] = 0; + + send_cmd_to_host(__roundup(fw.usb.put_index, 4), + CARL9170_RSP_TEXT, fw.usb.put_index, + fw.usb.put_buffer); + fw.usb.put_index = 0; + } +} + +void usb_print_hex_dump(const void *buf, int len) +{ + unsigned int offset = 0, block = 0; + while (len > 0) { + block = min(__roundup(len, 4), CARL9170_MAX_CMD_PAYLOAD_LEN); + + send_cmd_to_host(block, CARL9170_RSP_HEXDUMP, len, + (const uint8_t *) buf + offset); + + offset += block; + len -= block; + } +} +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + +/* grab a buffer from the interrupt in queue ring-buffer */ +static struct carl9170_rsp *get_int_buf(void) +{ + struct carl9170_rsp *tmp; + + /* fetch the _oldest_ buffer from the ring */ + tmp = &fw.usb.int_buf[fw.usb.int_tail_index]; + + /* assign a unique sequence for every response/trap */ + tmp->hdr.seq = fw.usb.int_tail_index; + + fw.usb.int_tail_index++; + + fw.usb.int_tail_index %= CARL9170_INT_RQ_CACHES; + if (fw.usb.int_pending != CARL9170_INT_RQ_CACHES) + fw.usb.int_pending++; + + return tmp; +} + +/* Pop up data from Interrupt IN Queue to USB Response buffer */ +static struct carl9170_rsp *dequeue_int_buf(unsigned int space) +{ + struct carl9170_rsp *tmp = NULL; + + if (fw.usb.int_pending > 0) { + tmp = &fw.usb.int_buf[fw.usb.int_head_index]; + + if ((unsigned int)(tmp->hdr.len + 8) > space) + return NULL; + + fw.usb.int_head_index++; + fw.usb.int_head_index %= CARL9170_INT_RQ_CACHES; + fw.usb.int_pending--; + } + + return tmp; +} + +static void usb_data_in(void) +{ +} + +static void usb_reg_out(void) +{ + uint32_t *regaddr = (uint32_t *) &dma_mem.reserved.cmd; + uint16_t usbfifolen, i; + + usb_reset_out(); + + usbfifolen = getb(AR9170_USB_REG_EP4_BYTE_COUNT_LOW) | + getb(AR9170_USB_REG_EP4_BYTE_COUNT_HIGH) << 8; + + if (usbfifolen & 0x3) + usbfifolen = (usbfifolen >> 2) + 1; + else + usbfifolen = usbfifolen >> 2; + + for (i = 0; i < usbfifolen; i++) + *regaddr++ = get(AR9170_USB_REG_EP4_DATA); + + handle_cmd(get_int_buf()); + + usb_trigger_in(); +} + +static void usb_status_in(void) +{ + struct carl9170_rsp *rsp; + unsigned int rem, tlen, elen; + + if (!fw.usb.int_desc_available) + return ; + + fw.usb.int_desc_available = 0; + + rem = AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE; + tlen = AR9170_INT_MAGIC_HEADER_SIZE; + + usb_reset_in(); + + while (fw.usb.int_pending) { + rsp = dequeue_int_buf(rem); + if (!rsp) + break; + + elen = rsp->hdr.len + 4; + + memcpy(DESC_PAYLOAD_OFF(fw.usb.int_desc, tlen), rsp, elen); + + rem -= elen; + tlen += elen; + } + + if (tlen == AR9170_INT_MAGIC_HEADER_SIZE) { + DBG("attempted to send an empty int response!\n"); + goto reclaim; + } + + fw.usb.int_desc->ctrl = AR9170_CTRL_FS_BIT | AR9170_CTRL_LS_BIT; + fw.usb.int_desc->totalLen = tlen; + fw.usb.int_desc->dataSize = tlen; + + /* Put to UpQ */ + dma_put(&fw.pta.up_queue, fw.usb.int_desc); + + /* Trigger PTA UP DMA */ + set(AR9170_PTA_REG_UP_DMA_TRIGGER, 1); + usb_trigger_out(); + + return ; + +reclaim: + /* TODO: not sure what to do here */ + fw.usb.int_desc_available = 1; +} + +void send_cmd_to_host(const uint8_t len, const uint8_t type, + const uint8_t ext, const uint8_t *body) +{ + struct carl9170_cmd *resp; + +#ifdef CONFIG_CARL9170FW_DEBUG + if (unlikely(len > sizeof(resp->data))) { + DBG("CMD too long:%x %d\n", type, len); + return ; + } + + /* Element length must be a multiple of 4. */ + if (unlikely(len & 0x3)) { + DBG("CMD length not mult. of 4:%x %d\n", type, len); + return ; + } +#endif /* CONFIG_CARL9170FW_DEBUG */ + + resp = (struct carl9170_cmd *) get_int_buf(); + if (unlikely(resp == NULL)) { + /* not very helpful for NON UART users */ + DBG("out of msg buffers\n"); + return ; + } + + resp->hdr.len = len; + resp->hdr.cmd = type; + resp->hdr.ext = ext; + + memcpy(resp->data, body, len); + usb_trigger_in(); +} + +/* Turn off ADDA/RF power, PLL */ +static void turn_power_off(void) +{ + set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_DIS); + set(AR9170_PHY_REG_ADC_CTL, 0xa0000000 | + AR9170_PHY_ADC_CTL_OFF_PWDADC | AR9170_PHY_ADC_CTL_OFF_PWDDAC); + + /* This will also turn-off the LEDs */ + set(AR9170_GPIO_REG_PORT_DATA, 0); + set(AR9170_GPIO_REG_PORT_TYPE, 0xf); + + set(AR9170_PWR_REG_BASE, 0x40021); + + set(AR9170_MAC_REG_DMA_TRIGGER, 0); + + andl(AR9170_USB_REG_DMA_CTL, ~(AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE | + AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE | + AR9170_USB_DMA_CTL_UP_PACKET_MODE | + AR9170_USB_DMA_CTL_DOWN_STREAM)); + + /* Do a software reset to PTA component */ + orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_RESET); + andl(AR9170_PTA_REG_DMA_MODE_CTRL, ~AR9170_PTA_DMA_MODE_CTRL_RESET); + + orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB); + + set(AR9170_MAC_REG_POWER_STATE_CTRL, + AR9170_MAC_POWER_STATE_CTRL_RESET); + + /* Reset USB FIFO */ + set(AR9170_PWR_REG_RESET, AR9170_PWR_RESET_COMMIT_RESET_MASK | + AR9170_PWR_RESET_DMA_MASK | + AR9170_PWR_RESET_WLAN_MASK); + set(AR9170_PWR_REG_RESET, 0x0); + + clock_set(AHB_20_22MHZ, false); + + set(AR9170_PWR_REG_PLL_ADDAC, 0x5163); /* 0x502b; */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO); + set(0x1c589c, 0); /* 7-0 */ + set(0x1c589c, 0); /* 15-8 */ + set(0x1c589c, 0); /* 23-16 */ + set(0x1c589c, 0); /* 31- */ + set(0x1c589c, 0); /* 39- */ + set(0x1c589c, 0); /* 47- */ + set(0x1c589c, 0); /* 55- */ + set(0x1c589c, 0xf8); /* 63- */ + set(0x1c589c, 0x27); /* 0x24; 71- modified */ + set(0x1c589c, 0xf9); /* 79- */ + set(0x1c589c, 0x90); /* 87- */ + set(0x1c589c, 0x04); /* 95- */ + set(0x1c589c, 0x48); /* 103- */ + set(0x1c589c, 0x19); /* 0; 111- modified */ + set(0x1c589c, 0); /* 119- */ + set(0x1c589c, 0); /* 127- */ + set(0x1c589c, 0); /* 135- */ + set(0x1c589c, 0); /* 143- */ + set(0x1c589c, 0); /* 151- */ + set(0x1c589c, 0x70); /* 159- */ + set(0x1c589c, 0x0c); /* 167- */ + set(0x1c589c, 0); /* 175- */ + set(0x1c589c, 0); /* 183-176 */ + set(0x1c589c, 0); /* 191-184 */ + set(0x1c589c, 0); /* 199- */ + set(0x1c589c, 0); /* 207- */ + set(0x1c589c, 0); /* 215- */ + set(0x1c589c, 0); /* 223- */ + set(0x1c589c, 0); /* 231- */ + set(0x1c58c4, 0); /* 233- 232 */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC); +} + +static void disable_watchdog(void) +{ + if (!fw.watchdog_enable) + return; + + /* write watchdog magic pattern for suspend */ + andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xffff); + orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x98760000); + + /* Disable watchdog */ + set(AR9170_TIMER_REG_WATCH_DOG, 0xffff); +} + +void __noreturn reboot(void) +{ + disable_watchdog(); + + /* Turn off power */ + turn_power_off(); + + /* clean bootloader workspace */ + memset(&dma_mem, 0, sizeof(dma_mem)); + + /* add by ygwei for work around USB PHY chirp sequence problem */ + set(0x10f100, 0x12345678); + + /* Jump to boot code */ + jump_to_bootcode(); +} + +/* service USB events and re-enable USB interrupt */ +static void usb_handler(uint8_t usb_interrupt_level1) +{ + uint8_t usb_interrupt_level2; + + if (usb_interrupt_level1 & BIT(5)) + usb_data_in(); + + if (usb_interrupt_level1 & BIT(4)) + usb_reg_out(); + + if (usb_interrupt_level1 & BIT(6)) + usb_status_in(); + + if (usb_interrupt_level1 & BIT(0)) { + usb_interrupt_level2 = getb(AR9170_USB_REG_INTR_SOURCE_0); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_SETUP) + usb_ep0setup(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_IN) + usb_ep0tx(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_OUT) + usb_ep0rx(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_ABORT) { + /* Clear the command abort interrupt */ + andb(AR9170_USB_REG_INTR_SOURCE_0, (uint8_t) + ~AR9170_USB_INTR_SRC0_ABORT); + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_FAIL || + fw.usb.ep0_action & CARL9170_EP0_STALL) { + /* + * transmission failure. + * stall ep 0 + */ + setb(AR9170_USB_REG_CX_CONFIG_STATUS, BIT(2)); + fw.usb.ep0_action &= ~CARL9170_EP0_STALL; + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_END || + fw.usb.ep0_action & CARL9170_EP0_TRIGGER) { + /* + * transmission done. + * set DONE bit. + */ + setb(AR9170_USB_REG_CX_CONFIG_STATUS, BIT(0)); + fw.usb.ep0_action &= ~CARL9170_EP0_TRIGGER; + } + } + + if (usb_interrupt_level1 & BIT(7)) { + usb_interrupt_level2 = getb(AR9170_USB_REG_INTR_SOURCE_7); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_RX0BYTE) + usb_data_out0Byte(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_TX0BYTE) + usb_data_in0Byte(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESET) { + usb_reset_ack(); + reboot(); + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_SUSPEND) { + usb_suspend_ack(); + + fw.suspend_mode = CARL9170_HOST_SUSPENDED; + +#ifdef CONFIG_CARL9170FW_WOL + if (!(fw.usb.device_feature & USB_DEVICE_REMOTE_WAKEUP) || + !fw.wol.cmd.flags) { + disable_watchdog(); + + /* GO_TO_SUSPEND stops the CPU clock too. */ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_GO_TO_SUSPEND); + } else { + wol_prepare(); + } +#else /* CONFIG_CARL9170FW_WOL */ + disable_watchdog(); + + /* GO_TO_SUSPEND stops the CPU clock too. */ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_GO_TO_SUSPEND); +#endif /* CONFIG_CARL9170FW_WOL */ + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESUME) { + usb_resume_ack(); + + fw.suspend_mode = CARL9170_HOST_AWAKE; + set(AR9170_USB_REG_WAKE_UP, 0); + + reboot(); + } + } +} + +void handle_usb(void) +{ + uint8_t usb_interrupt_level1; + + usb_interrupt_level1 = getb(AR9170_USB_REG_INTR_GROUP); + + if (usb_interrupt_level1) + usb_handler(usb_interrupt_level1); + + if (fw.usb.int_pending > 0) + usb_trigger_in(); +} + +void usb_timer(void) +{ +} diff --git a/carl9170fw/carlfw/usb/usb.c b/carl9170fw/carlfw/usb/usb.c new file mode 100644 index 0000000..74b8f8c --- /dev/null +++ b/carl9170fw/carlfw/usb/usb.c @@ -0,0 +1,758 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * USB Controller + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "carl9170.h" +#include "usb.h" +#include "printf.h" +#include "rom.h" + +/* + * NB: The firmware has to write into these structures + * so don't try to make them "const". + */ + +static struct ar9170_usb_config usb_config_highspeed = { + .cfg = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(sizeof(usb_config_highspeed)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_ONE | +#ifdef CONFIG_CARL9170FW_WOL + USB_CONFIG_ATT_WAKEUP | +#endif /* CONFIG_CARL9170FW_WOL */ + 0, + .bMaxPower = 0xfa, /* 500 mA */ + }, + + .intf = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = AR9170_USB_NUM_EXTRA_EP, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + + .ep = { + { /* EP 1 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_TX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 0, + }, + + { /* EP 2 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_RX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 0, + }, + + { /* EP 3 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_IRQ, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + + { /* EP 4 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + }, +}; + +static struct ar9170_usb_config usb_config_fullspeed = { + .cfg = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(sizeof(usb_config_fullspeed)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_ONE | +#ifdef CONFIG_CARL9170FW_WOL + USB_CONFIG_ATT_WAKEUP | +#endif /* CONFIG_CARL9170FW_WOL */ + 0, + .bMaxPower = 0xfa, /* 500 mA */ + }, + + .intf = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = AR9170_USB_NUM_EXTRA_EP, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + + .ep = { + { /* EP 1 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_TX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0, + }, + + { /* EP 2 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_RX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0, + }, + + { /* EP 3 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_IRQ, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + + { /* EP 4 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + }, +}; + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static void usb_reset_eps(void) +{ + unsigned int i; + + /* clear all EPs' toggle bit */ + for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) { + usb_set_input_ep_toggle(i); + usb_clear_input_ep_toggle(i); + } + + /* + * NB: I've no idea why this cannot be integrated into the + * previous loop? + */ + for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) { + usb_set_output_ep_toggle(i); + usb_clear_output_ep_toggle(i); + } +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + + +static void usb_pta_init(void) +{ + unsigned int usb_dma_ctrl = 0; + /* Set PTA mode to USB */ + andl(AR9170_PTA_REG_DMA_MODE_CTRL, + ~AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB); + + /* Do a software reset to PTA component */ + orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_RESET); + andl(AR9170_PTA_REG_DMA_MODE_CTRL, ~AR9170_PTA_DMA_MODE_CTRL_RESET); + + if (usb_detect_highspeed()) { + fw.usb.os_cfg_desc = &usb_config_fullspeed; + fw.usb.cfg_desc = &usb_config_highspeed; + + /* 512 Byte DMA transfers */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_HIGH_SPEED; + } else { + fw.usb.cfg_desc = &usb_config_fullspeed; + fw.usb.os_cfg_desc = &usb_config_highspeed; + } + +#ifdef CONFIG_CARL9170FW_USB_UP_STREAM +# if (CONFIG_CARL9170FW_RX_FRAME_LEN == 4096) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_4K; +# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 8192) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_8K; +# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 16384) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_16K; +# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 32768) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_32K; +# else +# error "Invalid AR9170_RX_FRAME_LEN setting" +# endif + +#else /* CONFIG_CARL9170FW_USB_UP_STREAM */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_PACKET_MODE; +#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ + +#ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM + /* Enable down stream mode */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_DOWN_STREAM; +#endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */ + +#ifdef CONFIG_CARL9170FW_USB_UP_STREAM + /* Set the up stream mode maximum aggregate number */ + set(AR9170_USB_REG_MAX_AGG_UPLOAD, 4); + + /* + * Set the up stream mode timeout value. + * NB: The vendor driver (otus) set 0x80? + */ + set(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80); +#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ + + /* Enable up stream and down stream */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE | + AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE; + + set(AR9170_USB_REG_DMA_CTL, usb_dma_ctrl); +} + +void usb_init(void) +{ + usb_pta_init(); + + fw.usb.config = 1; + /* + * The fw structure is always initialized with "0" + * during boot(); No need to waste precious bytes here. + * + * fw.usb.interface_setting = 0; + * fw.usb.alternate_interface_setting = 0; + * fw.usb.device_feature = 0; + */ + +#ifdef CONFIG_CARL9170FW_WOL + fw.usb.device_feature |= USB_DEVICE_REMOTE_WAKEUP; + usb_enable_remote_wakeup(); +#endif /* CONFIG_CARL9170FW_WOL */ +} + +#define GET_ARRAY(a, o) ((uint32_t *) (((unsigned long) data) + offset)) + +static void usb_ep0rx_data(const void *data, const unsigned int len) +{ + unsigned int offset; + uint32_t value; + + BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + BUILD_BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + + for (offset = 0; offset < ((len + 3) & ~3); offset += 4) { + value = get(AR9170_USB_REG_EP0_DATA); + memcpy(GET_ARRAY(data, offset), &value, + min(len - offset, (unsigned int)4)); + } +} + +static int usb_ep0tx_data(const void *data, const unsigned int len) +{ + unsigned int offset = 0, block, last_block = 0; + uint32_t value; + + BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + BUILD_BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + + block = min(len, (unsigned int) 4); + offset = 0; + while (offset < len) { + + if (last_block != block || block < 4) + setb(AR9170_USB_REG_FIFO_SIZE, (1 << block) - 1); + + memcpy(&value, GET_ARRAY(data, offset), block); + + set(AR9170_USB_REG_EP0_DATA, value); + + offset += block; + last_block = block = min(len - offset, (unsigned int) 4); + } + + setb(AR9170_USB_REG_FIFO_SIZE, 0xf); + + /* this will push the data to the host */ + return 1; +} +#undef GET_ARRAY + +#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS +static int usb_get_status(const struct usb_ctrlrequest *ctrl) +{ + __le16 status = cpu_to_le16(fw.usb.device_feature); + + if ((ctrl->bRequestType & USB_DIR_MASK) != USB_DIR_IN) + return -1; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status &= cpu_to_le16(~USB_DEVICE_SELF_POWERED); + status &= cpu_to_le16(~USB_DEVICE_REMOTE_WAKEUP); + break; + + case USB_RECIP_INTERFACE: + /* USB spec: This is reserved for future use. */ + status = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + case USB_RECIP_OTHER: + default: + break; + } + + return usb_ep0tx_data((const void *) &status, sizeof(status)); +} + +static int usb_get_string_desc(const struct usb_ctrlrequest *ctrl) +{ + const struct usb_string_descriptor *string_desc = NULL; + + switch (le16_to_cpu(ctrl->wValue) & 0xff) { + case 0x00: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string0_desc; + break; + + case 0x10: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string1_desc; + break; + + case 0x20: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string2_desc; + break; + + case 0x30: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string3_desc; + break; + + default: + break; + } + + if (string_desc) + return usb_ep0tx_data(string_desc, string_desc->bLength); + + return -1; +} + +static int usb_get_device_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + return usb_ep0tx_data(&rom.hw.usb.device_desc, + rom.hw.usb.device_desc.bLength); +} + +static int usb_get_config_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + fw.usb.cfg_desc->cfg.bDescriptorType = USB_DT_CONFIG; + + return usb_ep0tx_data(fw.usb.cfg_desc, + le16_to_cpu(fw.usb.cfg_desc->cfg.wTotalLength)); +} + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static int usb_get_otherspeed_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + + fw.usb.os_cfg_desc->cfg.bDescriptorType = USB_DT_OTHER_SPEED_CONFIG; + + return usb_ep0tx_data(fw.usb.os_cfg_desc, + le16_to_cpu(fw.usb.os_cfg_desc->cfg.wTotalLength)); +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + +static int usb_get_qualifier_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + struct usb_qualifier_descriptor qual; + + /* + * The qualifier descriptor shares some structural details + * with the main device descriptor. + */ + + memcpy(&qual, &rom.hw.usb.device_desc, sizeof(qual)); + + /* (Re)-Initialize fields */ + qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER; + qual.bLength = sizeof(qual); + qual.bNumConfigurations = rom.hw.usb.device_desc.bNumConfigurations; + qual.bRESERVED = 0; + + return usb_ep0tx_data(&qual, qual.bLength); +} + +#define USB_CHECK_REQTYPE(ctrl, recip, dir) \ + (((ctrl->bRequestType & USB_RECIP_MASK) != recip) || \ + ((ctrl->bRequestType & USB_DIR_MASK) != dir)) + +static int usb_get_descriptor(const struct usb_ctrlrequest *ctrl) +{ + int status = -1; + + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN)) + return status; + + switch (le16_to_cpu(ctrl->wValue) >> 8) { + case USB_DT_DEVICE: + status = usb_get_device_desc(ctrl); + break; + + case USB_DT_CONFIG: + status = usb_get_config_desc(ctrl); + break; + + case USB_DT_STRING: + status = usb_get_string_desc(ctrl); + break; + + case USB_DT_INTERFACE: + break; + + case USB_DT_ENDPOINT: + break; + + case USB_DT_DEVICE_QUALIFIER: + status = usb_get_qualifier_desc(ctrl); + break; + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH + case USB_DT_OTHER_SPEED_CONFIG: + status = usb_get_otherspeed_desc(ctrl); + break; +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + default: + break; + + } + + return status; +} + +static int usb_get_configuration(const struct usb_ctrlrequest *ctrl) +{ + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN)) + return -1; + + return usb_ep0tx_data(&fw.usb.config, 1); +} + +static int usb_set_configuration(const struct usb_ctrlrequest *ctrl) +{ + unsigned int config; + + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT)) + return -1; + + config = le16_to_cpu(ctrl->wValue); + switch (config) { + case 0: + /* Disable Device */ + andb(AR9170_USB_REG_DEVICE_ADDRESS, + (uint8_t) ~(AR9170_USB_DEVICE_ADDRESS_CONFIGURE)); +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH + case 1: + fw.usb.config = config; + + if (usb_detect_highspeed()) { + /* High Speed Configuration */ + usb_init_highspeed_fifo_cfg(); + } else { + /* Full Speed Configuration */ + usb_init_fullspeed_fifo_cfg(); + } + break; + + default: + return -1; + } + /* usb_pta_init() ? */ + + usb_reset_eps(); + orb(AR9170_USB_REG_DEVICE_ADDRESS, + (AR9170_USB_DEVICE_ADDRESS_CONFIGURE)); + + usb_enable_global_int(); + usb_trigger_out(); + return 1; +#else + default: + return -1; + } +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ +} + +static int usb_set_address(const struct usb_ctrlrequest *ctrl) +{ + unsigned int address; + + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT)) + return -1; + + address = le16_to_cpu(ctrl->wValue); + + /* + * The original firmware used 0x100 (which is, of course, + * too big to fit into uint8_t). + * However based on the available information (hw.h), BIT(7) + * is used as some sort of flag and should not be + * part of the device address. + */ + if (address >= BIT(7)) + return -1; + + setb(AR9170_USB_REG_DEVICE_ADDRESS, (uint8_t) address); + return 1; +} + +static int usb_get_interface(const struct usb_ctrlrequest *ctrl) +{ + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_INTERFACE, USB_DIR_IN)) + return -1; + + if (usb_configured() == false) + return -1; + + switch (fw.usb.config) { + case 1: + break; + + default: + return -1; + } + + return usb_ep0tx_data(&fw.usb.alternate_interface_setting, 1); +} + +static int usb_manipulate_feature(const struct usb_ctrlrequest *ctrl, bool __unused clear) +{ + unsigned int feature; + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT)) + return -1; + + if (usb_configured() == false) + return -1; + + feature = le16_to_cpu(ctrl->wValue); + +#ifdef CONFIG_CARL9170FW_WOL + if (feature & USB_DEVICE_REMOTE_WAKEUP) { + if (clear) + usb_disable_remote_wakeup(); + else + usb_enable_remote_wakeup(); + } +#endif /* CONFIG_CARL9170FW_WOL */ + + if (clear) + fw.usb.device_feature &= ~feature; + else + fw.usb.device_feature |= feature; + + return 1; +} + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static int usb_set_interface(const struct usb_ctrlrequest *ctrl) +{ + unsigned int intf, alt_intf; + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_INTERFACE, USB_DIR_OUT)) + return -1; + + if (usb_configured() == false) + return -1; + + intf = le16_to_cpu(ctrl->wIndex); + alt_intf = le16_to_cpu(ctrl->wValue); + + switch (intf) { + case 0: + if (alt_intf != fw.usb.cfg_desc->intf.bAlternateSetting) + return -1; + + fw.usb.interface_setting = (uint8_t) intf; + fw.usb.alternate_interface_setting = (uint8_t) alt_intf; + if (usb_detect_highspeed()) + usb_init_highspeed_fifo_cfg(); + else + usb_init_fullspeed_fifo_cfg(); + + usb_reset_eps(); + usb_enable_global_int(); + usb_trigger_out(); + return 1; + + default: + return -1; + } +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ +#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */ + +static int usb_standard_command(const struct usb_ctrlrequest *ctrl __unused) +{ + int status = -1; + +#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + status = usb_get_status(ctrl); + break; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + usb_manipulate_feature(ctrl, ctrl->bRequest == USB_REQ_CLEAR_FEATURE); + break; + + case USB_REQ_SET_ADDRESS: + status = usb_set_address(ctrl); + break; + + case USB_REQ_GET_DESCRIPTOR: + status = usb_get_descriptor(ctrl); + break; + + case USB_REQ_SET_DESCRIPTOR: + break; + + case USB_REQ_GET_CONFIGURATION: + status = usb_get_configuration(ctrl); + break; + + case USB_REQ_SET_CONFIGURATION: + status = usb_set_configuration(ctrl); + break; + + case USB_REQ_GET_INTERFACE: + status = usb_get_interface(ctrl); + break; + + case USB_REQ_SET_INTERFACE: +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH + status = usb_set_interface(ctrl); +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + break; + + case USB_REQ_SYNCH_FRAME: + break; + + default: + break; + + } +#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */ + + return status; +} + +static int usb_class_command(const struct usb_ctrlrequest *ctrl __unused) +{ + return -1; +} + +static int usb_vendor_command(const struct usb_ctrlrequest *ctrl __unused) +{ + /* + * Note: Firmware upload/boot is not implemented. + * It's impossible to replace the current image + * in place. + */ + + return -1; +} + +#undef USB_CHECK_TYPE + +void usb_ep0setup(void) +{ + struct usb_ctrlrequest ctrl; + int status = -1; + usb_ep0rx_data(&ctrl, sizeof(ctrl)); + + switch (ctrl.bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + status = usb_standard_command(&ctrl); + break; + + case USB_TYPE_CLASS: + status = usb_class_command(&ctrl); + break; + + case USB_TYPE_VENDOR: + status = usb_vendor_command(&ctrl); + break; + + default: + break; + + } + + if (status < 0) + fw.usb.ep0_action |= CARL9170_EP0_STALL; +#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS + if (status > 0) + fw.usb.ep0_action |= CARL9170_EP0_TRIGGER; +#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */ +} + +void usb_ep0rx(void) +{ + if (BUG_ON(!fw.usb.ep0_txrx_buffer || !fw.usb.ep0_txrx_len)) + return ; + + usb_ep0rx_data(fw.usb.ep0_txrx_buffer, fw.usb.ep0_txrx_len); + fw.usb.ep0_txrx_pos = fw.usb.ep0_txrx_len; +} + +void usb_ep0tx(void) +{ + if (BUG_ON(!fw.usb.ep0_txrx_buffer || !fw.usb.ep0_txrx_len)) + return ; + + usb_ep0tx_data(fw.usb.ep0_txrx_buffer, fw.usb.ep0_txrx_len); + fw.usb.ep0_txrx_pos = fw.usb.ep0_txrx_len; +} diff --git a/carl9170fw/config/CMakeLists.txt b/carl9170fw/config/CMakeLists.txt new file mode 100644 index 0000000..9e2ddeb --- /dev/null +++ b/carl9170fw/config/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8) + +project(config) + +#set(CMAKE_VERBOSE_MAKEFILE ON) + +find_package(BISON REQUIRED) +find_package(FLEX REQUIRED) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../include/generated") + +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../extra") +FIND_PACKAGE(GPERF REQUIRED) + +BISON_TARGET(zconf zconf.y zconf.tab.c COMPILE_FLAGS "-l -b zconf -p zconf -t") +FLEX_TARGET(zconfscan zconf.l zconf.lex.c COMPILE_FLAGS "-Pzconf -L") +GPERF_TARGET(zconfhash zconf.gperf zconf.hash.c) + +SET(zconf_deps ${FLEX_zconfscan_OUTPUTS} ${GPERF_zconfhash_OUTPUTS}) +SET_SOURCE_FILES_PROPERTIES(${BISON_zconf_OUTPUTS} + PROPERTIES OBJECT_DEPENDS "${zconf_deps}") + +set(conf_src conf.c ${BISON_zconf_OUTPUTS}) + +add_executable(conf ${conf_src}) diff --git a/carl9170fw/config/conf.c b/carl9170fw/config/conf.c new file mode 100644 index 0000000..6fdacb3 --- /dev/null +++ b/carl9170fw/config/conf.c @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <locale.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include "lkc.h" + +static void conf(struct menu *menu); +static void check_conf(struct menu *menu); +static void xfgets(char *str, int size, FILE *in); + +enum input_mode { + oldaskconfig, + oldconfig, + allnoconfig, + allyesconfig, + allmodconfig, + alldefconfig, + randconfig, + defconfig, + savedefconfig, + listnewconfig, + oldnoconfig, +} input_mode = oldaskconfig; + +static int indent = 1; +static int valid_stdin = 1; +static int conf_cnt; +static char line[128]; +static struct menu *rootEntry; + +static void print_help(struct menu *menu) +{ + struct gstr help = str_new(); + + menu_get_ext_help(menu, &help); + + printf("\n%s\n", str_get(&help)); + str_free(&help); +} + +static void strip(char *str) +{ + char *p = str; + int l; + + while ((isspace(*p))) + p++; + l = strlen(p); + if (p != str) + memmove(str, p, l + 1); + if (!l) + return; + p = str + l - 1; + while ((isspace(*p))) + *p-- = 0; +} + +static void check_stdin(void) +{ + if (!valid_stdin) { + printf(_("aborted!\n\n")); + printf(_("Console input/output is redirected. ")); + printf(_("Run 'make config' to update configuration.\n\n")); + exit(1); + } +} + +static int conf_askvalue(struct symbol *sym, const char *def) +{ + enum symbol_type type = sym_get_type(sym); + + if (!sym_has_value(sym)) + printf(_("(NEW) ")); + + line[0] = '\n'; + line[1] = 0; + + if (!sym_is_changable(sym)) { + printf("%s\n", def); + line[0] = '\n'; + line[1] = 0; + return 0; + } + + switch (input_mode) { + case oldconfig: + if (sym_has_value(sym)) { + printf("%s\n", def); + return 0; + } + check_stdin(); + /* fall through */ + case oldaskconfig: + fflush(stdout); + xfgets(line, 128, stdin); + return 1; + default: + break; + } + + switch (type) { + case S_INT: + case S_HEX: + case S_STRING: + printf("%s\n", def); + return 1; + default: + ; + } + printf("%s", line); + return 1; +} + +static int conf_string(struct menu *menu) +{ + struct symbol *sym = menu->sym; + const char *def; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + printf("(%s) ", sym->name); + def = sym_get_string_value(sym); + if (sym_get_string_value(sym)) + printf("[%s] ", def); + if (!conf_askvalue(sym, def)) + return 0; + switch (line[0]) { + case '\n': + break; + case '?': + /* print help */ + if (line[1] == '\n') { + print_help(menu); + def = NULL; + break; + } + /* fall through */ + default: + line[strlen(line)-1] = 0; + def = line; + } + if (def && sym_set_string_value(sym, def)) + return 0; + } +} + +static int conf_sym(struct menu *menu) +{ + struct symbol *sym = menu->sym; + tristate oldval, newval; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + if (sym->name) + printf("(%s) ", sym->name); + putchar('['); + oldval = sym_get_tristate_value(sym); + switch (oldval) { + case no: + putchar('N'); + break; + case mod: + putchar('M'); + break; + case yes: + putchar('Y'); + break; + } + if (oldval != no && sym_tristate_within_range(sym, no)) + printf("/n"); + if (oldval != mod && sym_tristate_within_range(sym, mod)) + printf("/m"); + if (oldval != yes && sym_tristate_within_range(sym, yes)) + printf("/y"); + if (menu_has_help(menu)) + printf("/?"); + printf("] "); + if (!conf_askvalue(sym, sym_get_string_value(sym))) + return 0; + strip(line); + + switch (line[0]) { + case 'n': + case 'N': + newval = no; + if (!line[1] || !strcmp(&line[1], "o")) + break; + continue; + case 'm': + case 'M': + newval = mod; + if (!line[1]) + break; + continue; + case 'y': + case 'Y': + newval = yes; + if (!line[1] || !strcmp(&line[1], "es")) + break; + continue; + case 0: + newval = oldval; + break; + case '?': + goto help; + default: + continue; + } + if (sym_set_tristate_value(sym, newval)) + return 0; +help: + print_help(menu); + } +} + +static int conf_choice(struct menu *menu) +{ + struct symbol *sym, *def_sym; + struct menu *child; + bool is_new; + + sym = menu->sym; + is_new = !sym_has_value(sym); + if (sym_is_changable(sym)) { + conf_sym(menu); + sym_calc_value(sym); + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + return 0; + case yes: + break; + } + } else { + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + return 0; + case yes: + break; + } + } + + while (1) { + int cnt, def; + + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + def_sym = sym_get_choice_value(sym); + cnt = def = 0; + line[0] = 0; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (!child->sym) { + printf("%*c %s\n", indent, '*', _(menu_get_prompt(child))); + continue; + } + cnt++; + if (child->sym == def_sym) { + def = cnt; + printf("%*c", indent, '>'); + } else + printf("%*c", indent, ' '); + printf(" %d. %s", cnt, _(menu_get_prompt(child))); + if (child->sym->name) + printf(" (%s)", child->sym->name); + if (!sym_has_value(child->sym)) + printf(_(" (NEW)")); + printf("\n"); + } + printf(_("%*schoice"), indent - 1, ""); + if (cnt == 1) { + printf("[1]: 1\n"); + goto conf_childs; + } + printf("[1-%d", cnt); + if (menu_has_help(menu)) + printf("?"); + printf("]: "); + switch (input_mode) { + case oldconfig: + if (!is_new) { + cnt = def; + printf("%d\n", cnt); + break; + } + check_stdin(); + /* fall through */ + case oldaskconfig: + fflush(stdout); + xfgets(line, 128, stdin); + strip(line); + if (line[0] == '?') { + print_help(menu); + continue; + } + if (!line[0]) + cnt = def; + else if (isdigit(line[0])) + cnt = atoi(line); + else + continue; + break; + default: + break; + } + + conf_childs: + for (child = menu->list; child; child = child->next) { + if (!child->sym || !menu_is_visible(child)) + continue; + if (!--cnt) + break; + } + if (!child) + continue; + if (line[0] && line[strlen(line) - 1] == '?') { + print_help(child); + continue; + } + sym_set_choice_value(sym, child->sym); + for (child = child->list; child; child = child->next) { + indent += 2; + conf(child); + indent -= 2; + } + return 1; + } +} + +static void conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (prop) { + const char *prompt; + + switch (prop->type) { + case P_MENU: + if ((input_mode == listnewconfig || + input_mode == oldnoconfig) && + rootEntry != menu) { + check_conf(menu); + return; + } + /* fall through */ + case P_COMMENT: + prompt = menu_get_prompt(menu); + if (prompt) + printf("%*c\n%*c %s\n%*c\n", + indent, '*', + indent, '*', _(prompt), + indent, '*'); + default: + ; + } + } + + if (!sym) + goto conf_childs; + + if (sym_is_choice(sym)) { + conf_choice(menu); + if (sym->curr.tri != mod) + return; + goto conf_childs; + } + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + conf_string(menu); + break; + default: + conf_sym(menu); + break; + } + +conf_childs: + if (sym) + indent += 2; + for (child = menu->list; child; child = child->next) + conf(child); + if (sym) + indent -= 2; +} + +static void check_conf(struct menu *menu) +{ + struct symbol *sym; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + if (sym && !sym_has_value(sym)) { + if (sym_is_changable(sym) || + (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) { + if (input_mode == listnewconfig) { + if (sym->name && !sym_is_choice_value(sym)) { + printf("%s%s\n", CONFIG_, sym->name); + } + } else if (input_mode != oldnoconfig) { + if (!conf_cnt++) + printf(_("*\n* Restart config...\n*\n")); + rootEntry = menu_get_parent_menu(menu); + conf(rootEntry); + } + } + } + + for (child = menu->list; child; child = child->next) + check_conf(child); +} + +static struct option long_opts[] = { + {"askconfig", no_argument, NULL, oldaskconfig}, + {"config", no_argument, NULL, oldconfig}, + {"defconfig", optional_argument, NULL, defconfig}, + {"savedefconfig", required_argument, NULL, savedefconfig}, + {"allnoconfig", no_argument, NULL, allnoconfig}, + {"allyesconfig", no_argument, NULL, allyesconfig}, + {"allmodconfig", no_argument, NULL, allmodconfig}, + {"alldefconfig", no_argument, NULL, alldefconfig}, + {"randconfig", no_argument, NULL, randconfig}, + {"listnewconfig", no_argument, NULL, listnewconfig}, + {"noconfig", no_argument, NULL, oldnoconfig}, + {NULL, 0, NULL, 0} +}; + +static void conf_usage(const char *progname) +{ + + printf("Usage: %s [option] <kconfig-file>\n", progname); + printf("[option] is _one_ of the following:\n"); + printf(" --listnewconfig List new options\n"); + printf(" --askconfig Start a new configuration using a line-oriented program\n"); + printf(" --config Update a configuration using a provided .config as base\n"); + printf(" --silentconfig Same as config, but quietly, additionally update deps\n"); + printf(" --noconfig Same as silentconfig but set new symbols to no\n"); + printf(" --defconfig <file> New config with default defined in <file>\n"); + printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n"); + printf(" --allnoconfig New config where all options are answered with no\n"); + printf(" --allyesconfig New config where all options are answered with yes\n"); + printf(" --allmodconfig New config where all options are answered with mod\n"); + printf(" --alldefconfig New config with all symbols set to default\n"); + printf(" --randconfig New config with random answer to all options\n"); +} + +int main(int ac, char **av) +{ + const char *progname = av[0]; + int opt; + const char *name, *defconfig_file = NULL /* gcc uninit */; + struct stat tmpstat; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + while ((opt = getopt_long(ac, av, "", long_opts, NULL)) != -1) { + input_mode = (enum input_mode)opt; + switch (opt) { + case defconfig: + case savedefconfig: + defconfig_file = optarg; + break; + case randconfig: + { + struct timeval now; + unsigned int seed; + + /* + * Use microseconds derived seed, + * compensate for systems where it may be zero + */ + gettimeofday(&now, NULL); + + seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1)); + srand(seed); + break; + } + case oldaskconfig: + case oldconfig: + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case listnewconfig: + case oldnoconfig: + break; + case '?': + conf_usage(progname); + exit(1); + break; + } + } + if (ac == optind) { + printf(_("%s: Kconfig file missing\n"), av[0]); + conf_usage(progname); + exit(1); + } + name = av[optind]; + conf_parse(name); + //zconfdump(stdout); + + switch (input_mode) { + case defconfig: + if (!defconfig_file) + defconfig_file = conf_get_default_confname(); + if (conf_read(defconfig_file)) { + printf(_("***\n" + "*** Can't find default configuration \"%s\"!\n" + "***\n"), defconfig_file); + exit(1); + } + break; + case savedefconfig: + case oldaskconfig: + case oldconfig: + case listnewconfig: + case oldnoconfig: + conf_read(NULL); + break; + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case randconfig: + name = getenv("KCONFIG_ALLCONFIG"); + if (name && !stat(name, &tmpstat)) { + conf_read_simple(name, S_DEF_USER); + break; + } + switch (input_mode) { + case allnoconfig: name = "allno.config"; break; + case allyesconfig: name = "allyes.config"; break; + case allmodconfig: name = "allmod.config"; break; + case alldefconfig: name = "alldef.config"; break; + case randconfig: name = "allrandom.config"; break; + default: break; + } + if (!stat(name, &tmpstat)) + conf_read_simple(name, S_DEF_USER); + else if (!stat("all.config", &tmpstat)) + conf_read_simple("all.config", S_DEF_USER); + break; + default: + break; + } + + valid_stdin = isatty(0) && isatty(1) && isatty(2); + + switch (input_mode) { + case allnoconfig: + conf_set_all_new_symbols(def_no); + break; + case allyesconfig: + conf_set_all_new_symbols(def_yes); + break; + case allmodconfig: + conf_set_all_new_symbols(def_mod); + break; + case alldefconfig: + conf_set_all_new_symbols(def_default); + break; + case randconfig: + conf_set_all_new_symbols(def_random); + break; + case defconfig: + conf_set_all_new_symbols(def_default); + break; + case savedefconfig: + break; + case oldaskconfig: + rootEntry = &rootmenu; + conf(&rootmenu); + input_mode = oldconfig; + /* fall through */ + case oldconfig: + case listnewconfig: + case oldnoconfig: + /* Update until a loop caused no more changes */ + do { + conf_cnt = 0; + check_conf(&rootmenu); + } while (conf_cnt && + (input_mode != listnewconfig && + input_mode != oldnoconfig)); + break; + } + + if (input_mode == savedefconfig) { + if (conf_write_defconfig(defconfig_file)) { + fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"), + defconfig_file); + return 1; + } + } else if (input_mode != listnewconfig) { + /* + * build so we shall update autoconf. + */ + if (conf_write(NULL)) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + exit(1); + } + if (conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during update of the configuration.\n\n")); + return 1; + } + } + return 0; +} + +/* + * Helper function to facilitate fgets() by Jean Sacren. + */ +void xfgets(char *str, int size, FILE *in) +{ + if (fgets(str, size, in) == NULL) + fprintf(stderr, "\nError in reading or end of file.\n"); +} diff --git a/carl9170fw/config/confdata.c b/carl9170fw/config/confdata.c new file mode 100644 index 0000000..d45c37e --- /dev/null +++ b/carl9170fw/config/confdata.c @@ -0,0 +1,1170 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <sys/stat.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "lkc.h" + +static void conf_warning(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static void conf_message(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static const char *conf_filename; +static int conf_lineno, conf_warnings, conf_unsaved; + +const char conf_defname[] = "include/generated/defconfig"; + +static void conf_warning(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + conf_warnings++; +} + +static void conf_default_message_callback(const char *fmt, va_list ap) +{ + printf("#\n# "); + vprintf(fmt, ap); + printf("\n#\n"); +} + +static void (*conf_message_callback) (const char *fmt, va_list ap) = + conf_default_message_callback; +void conf_set_message_callback(void (*fn) (const char *fmt, va_list ap)) +{ + conf_message_callback = fn; +} + +static void conf_message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (conf_message_callback) + conf_message_callback(fmt, ap); +} + +const char *conf_get_configname(void) +{ + char *name = getenv("KCONFIG_CONFIG"); + + return name ? name : ".config"; +} + +const char *conf_get_autoconfig_name(void) +{ + char *name = getenv("KCONFIG_AUTOCONFIG"); + + return name ? name : "include/generated/auto.conf"; +} + +static char *conf_expand_value(const char *in) +{ + struct symbol *sym; + const char *src; + static char res_value[SYMBOL_MAXLENGTH]; + char *dst, name[SYMBOL_MAXLENGTH]; + + res_value[0] = 0; + dst = name; + while ((src = strchr(in, '$'))) { + strncat(res_value, in, src - in); + src++; + dst = name; + while (isalnum(*src) || *src == '_') + *dst++ = *src++; + *dst = 0; + sym = sym_lookup(name, 0); + sym_calc_value(sym); + strcat(res_value, sym_get_string_value(sym)); + in = src; + } + strcat(res_value, in); + + return res_value; +} + +char *conf_get_default_confname(void) +{ + struct stat buf; + static char fullname[PATH_MAX+1]; + char *env, *name; + + name = conf_expand_value(conf_defname); + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + if (!stat(fullname, &buf)) + return fullname; + } + return name; +} + +static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) +{ + char *p2; + + switch (sym->type) { + case S_TRISTATE: + if (p[0] == 'm') { + sym->def[def].tri = mod; + sym->flags |= def_flags; + break; + } + /* fall through */ + case S_BOOLEAN: + if (p[0] == 'y') { + sym->def[def].tri = yes; + sym->flags |= def_flags; + break; + } + if (p[0] == 'n') { + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + } + conf_warning("symbol value '%s' invalid for %s", p, sym->name); + return 1; + case S_OTHER: + if (*p != '"') { + for (p2 = p; *p2 && !isspace(*p2); p2++) + ; + sym->type = S_STRING; + goto done; + } + /* fall through */ + case S_STRING: + if (*p++ != '"') + break; + for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); + } + if (!p2) { + conf_warning("invalid string found"); + return 1; + } + /* fall through */ + case S_INT: + case S_HEX: + done: + if (sym_string_valid(sym, p)) { + sym->def[def].val = strdup(p); + sym->flags |= def_flags; + } else { + conf_warning("symbol value '%s' invalid for %s", p, sym->name); + return 1; + } + break; + default: + ; + } + return 0; +} + +int conf_read_simple(const char *name, int def) +{ + FILE *in = NULL; + char line[1024]; + char *p, *p2; + struct symbol *sym; + int i, def_flags; + + if (name) { + in = zconf_fopen(name); + } else { + struct property *prop; + + name = conf_get_configname(); + in = zconf_fopen(name); + if (in) + goto load; + sym_add_change_count(1); + if (!sym_defconfig_list) { + if (modules_sym) + sym_calc_value(modules_sym); + return 1; + } + + for_all_defaults(sym_defconfig_list, prop) { + if (expr_calc_value(prop->visible.expr) == no || + prop->expr->type != E_SYMBOL) + continue; + name = conf_expand_value(prop->expr->left.sym->name); + in = zconf_fopen(name); + if (in) { + conf_message(_("using defaults found in %s"), + name); + goto load; + } + } + } + if (!in) + return 1; + +load: + conf_filename = name; + conf_lineno = 0; + conf_warnings = 0; + conf_unsaved = 0; + + def_flags = SYMBOL_DEF << def; + for_all_symbols(i, sym) { + sym->flags |= SYMBOL_CHANGED; + sym->flags &= ~(def_flags|SYMBOL_VALID); + if (sym_is_choice(sym)) + sym->flags |= def_flags; + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + if (sym->def[def].val) + free(sym->def[def].val); + /* fall through */ + default: + sym->def[def].val = NULL; + sym->def[def].tri = no; + } + } + + while (fgets(line, sizeof(line), in)) { + conf_lineno++; + sym = NULL; + if (line[0] == '#') { + if (memcmp(line + 2, CONFIG_, strlen(CONFIG_))) + continue; + p = strchr(line + 2 + strlen(CONFIG_), ' '); + if (!p) + continue; + *p++ = 0; + if (strncmp(p, "is not set", 10)) + continue; + if (def == S_DEF_USER) { + sym = sym_find(line + 2 + strlen(CONFIG_)); + if (!sym) { + sym_add_change_count(1); + goto setsym; + } + } else { + sym = sym_lookup(line + 2 + strlen(CONFIG_), 0); + if (sym->type == S_UNKNOWN) + sym->type = S_BOOLEAN; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + default: + ; + } + } else if (memcmp(line, CONFIG_, strlen(CONFIG_)) == 0) { + p = strchr(line + strlen(CONFIG_), '='); + if (!p) + continue; + *p++ = 0; + p2 = strchr(p, '\n'); + if (p2) { + *p2-- = 0; + if (*p2 == '\r') + *p2 = 0; + } + if (def == S_DEF_USER) { + sym = sym_find(line + strlen(CONFIG_)); + if (!sym) { + sym_add_change_count(1); + goto setsym; + } + } else { + sym = sym_lookup(line + strlen(CONFIG_), 0); + if (sym->type == S_UNKNOWN) + sym->type = S_OTHER; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + if (conf_set_sym_val(sym, def, def_flags, p)) + continue; + } else { + if (line[0] != '\r' && line[0] != '\n') + conf_warning("unexpected data"); + continue; + } +setsym: + if (sym && sym_is_choice_value(sym)) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + switch (sym->def[def].tri) { + case no: + break; + case mod: + if (cs->def[def].tri == yes) { + conf_warning("%s creates inconsistent choice state", sym->name); + cs->flags &= ~def_flags; + } + break; + case yes: + if (cs->def[def].tri != no) + conf_warning("override: %s changes choice state", sym->name); + cs->def[def].val = sym; + break; + } + cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); + } + } + fclose(in); + + if (modules_sym) + sym_calc_value(modules_sym); + return 0; +} + +int conf_read(const char *name) +{ + struct symbol *sym; + int i; + + sym_set_change_count(0); + + if (conf_read_simple(name, S_DEF_USER)) + return 1; + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO)) + continue; + if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { + /* check that calculated value agrees with saved value */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym)) + break; + if (!sym_is_choice(sym)) + continue; + /* fall through */ + default: + if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) + continue; + break; + } + } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) + /* no previous value and not saved */ + continue; + conf_unsaved++; + /* maybe print value in verbose mode... */ + } + + for_all_symbols(i, sym) { + if (sym_has_value(sym) && !sym_is_choice_value(sym)) { + /* Reset values of generates values, so they'll appear + * as new, if they should become visible, but that + * doesn't quite work if the Kconfig and the saved + * configuration disagree. + */ + if (sym->visible == no && !conf_unsaved) + sym->flags &= ~SYMBOL_DEF_USER; + switch (sym->type) { + case S_STRING: + case S_INT: + case S_HEX: + /* Reset a string value if it's out of range */ + if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) + break; + sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER); + conf_unsaved++; + break; + default: + break; + } + } + } + + sym_add_change_count(conf_warnings || conf_unsaved); + + return 0; +} + +/* + * Kconfig configuration printer + * + * This printer is used when generating the resulting configuration after + * kconfig invocation and `defconfig' files. Unset symbol might be omitted by + * passing a non-NULL argument to the printer. + * + */ +static void +kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (*value == 'n') { + bool skip_unset = (arg != NULL); + + if (!skip_unset) + fprintf(fp, "# %s%s is not set\n", + CONFIG_, sym->name); + return; + } + break; + default: + break; + } + + fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value); +} + +static void +kconfig_print_cmake_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (*value == 'n') { + bool skip_unset = (arg != NULL); + + if (!skip_unset) + fprintf(fp, "set(%s%s false)\n", + CONFIG_, sym->name, value); + return; + } else if (*value == 'm') { + abort(); + } else { + fprintf(fp, "set(%s%s true)\n", CONFIG_, sym->name, value); + } + break; + case S_HEX: { + const char *prefix = ""; + + if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) + prefix = "0x"; + fprintf(fp, "set(%s%s %s%s)\n", + CONFIG_, sym->name, prefix, value); + break; + } + case S_STRING: + case S_INT: + fprintf(fp, "set(%s%s %s)\n", + CONFIG_, sym->name, value); + break; + default: + break; + } + +} + +static void +kconfig_print_comment(FILE *fp, const char *value, void *arg) +{ + const char *p = value; + size_t l; + + for (;;) { + l = strcspn(p, "\n"); + fprintf(fp, "#"); + if (l) { + fprintf(fp, " "); + xfwrite(p, l, 1, fp); + p += l; + } + fprintf(fp, "\n"); + if (*p++ == '\0') + break; + } +} + +static struct conf_printer kconfig_printer_cb = +{ + .print_symbol = kconfig_print_symbol, + .print_comment = kconfig_print_comment, +}; + +static struct conf_printer kconfig_printer_cmake_cb = +{ + .print_symbol = kconfig_print_cmake_symbol, + .print_comment = kconfig_print_comment, +}; + +/* + * Header printer + * + * This printer is used when generating the `include/generated/autoconf.h' file. + */ +static void +header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: { + const char *suffix = ""; + + switch (*value) { + case 'n': + break; + case 'm': + suffix = "_MODULE"; + /* fall through */ + default: + fprintf(fp, "#define %s%s%s 1\n", + CONFIG_, sym->name, suffix); + } + break; + } + case S_HEX: { + const char *prefix = ""; + + if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) + prefix = "0x"; + fprintf(fp, "#define %s%s %s%s\n", + CONFIG_, sym->name, prefix, value); + break; + } + case S_STRING: + case S_INT: + fprintf(fp, "#define %s%s %s\n", + CONFIG_, sym->name, value); + break; + default: + break; + } + +} + +static void +header_print_comment(FILE *fp, const char *value, void *arg) +{ + const char *p = value; + size_t l; + + fprintf(fp, "/*\n"); + for (;;) { + l = strcspn(p, "\n"); + fprintf(fp, " *"); + if (l) { + fprintf(fp, " "); + xfwrite(p, l, 1, fp); + p += l; + } + fprintf(fp, "\n"); + if (*p++ == '\0') + break; + } + fprintf(fp, " */\n"); +} + +static struct conf_printer header_printer_cb = +{ + .print_symbol = header_print_symbol, + .print_comment = header_print_comment, +}; + +/* + * Tristate printer + * + * This printer is used when generating the `include/generated/tristate.conf' file. + */ +static void +tristate_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + if (sym->type == S_TRISTATE && *value != 'n') + fprintf(fp, "%s%s=%c\n", CONFIG_, sym->name, (char)toupper(*value)); +} + +static struct conf_printer tristate_printer_cb = +{ + .print_symbol = tristate_print_symbol, + .print_comment = kconfig_print_comment, +}; + +static void conf_write_symbol(FILE *fp, struct symbol *sym, + struct conf_printer *printer, void *printer_arg) +{ + const char *str; + + switch (sym->type) { + case S_OTHER: + case S_UNKNOWN: + break; + case S_STRING: + str = sym_get_string_value(sym); + str = sym_escape_string_value(str); + printer->print_symbol(fp, sym, str, printer_arg); + free((void *)str); + break; + default: + str = sym_get_string_value(sym); + printer->print_symbol(fp, sym, str, printer_arg); + } +} + +static void +conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), + "\n" + "Automatically generated file; DO NOT EDIT.\n" + "%s\n", + rootmenu.prompt->text); + + printer->print_comment(fp, buf, printer_arg); +} + +/* + * Write out a minimal config. + * All values that has default values are skipped as this is redundant. + */ +int conf_write_defconfig(const char *filename) +{ + struct symbol *sym; + struct menu *menu; + FILE *out; + + out = fopen(filename, "w"); + if (!out) + return 1; + + sym_clear_all_valid(); + + /* Traverse all menus to find all relevant symbols */ + menu = rootmenu.list; + + while (menu != NULL) + { + sym = menu->sym; + if (sym == NULL) { + if (!menu_is_visible(menu)) + goto next_menu; + } else if (!sym_is_choice(sym)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next_menu; + sym->flags &= ~SYMBOL_WRITE; + /* If we cannot change the symbol - skip */ + if (!sym_is_changable(sym)) + goto next_menu; + /* If symbol equals to default value - skip */ + if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) + goto next_menu; + + /* + * If symbol is a choice value and equals to the + * default for a choice - skip. + * But only if value is bool and equal to "y" and + * choice is not "optional". + * (If choice is "optional" then all values can be "n") + */ + if (sym_is_choice_value(sym)) { + struct symbol *cs; + struct symbol *ds; + + cs = prop_get_symbol(sym_get_choice_prop(sym)); + ds = sym_choice_default(cs); + if (!sym_is_optional(cs) && sym == ds) { + if ((sym->type == S_BOOLEAN) && + sym_get_tristate_value(sym) == yes) + goto next_menu; + } + } + conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + } +next_menu: + if (menu->list != NULL) { + menu = menu->list; + } + else if (menu->next != NULL) { + menu = menu->next; + } else { + while ((menu = menu->parent)) { + if (menu->next != NULL) { + menu = menu->next; + break; + } + } + } + } + fclose(out); + return 0; +} + +int conf_write(const char *name) +{ + FILE *out; + struct symbol *sym; + struct menu *menu; + const char *basename; + const char *str; + char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1]; + char *env; + + dirname[0] = 0; + if (name && name[0]) { + struct stat st; + char *slash; + + if (!stat(name, &st) && S_ISDIR(st.st_mode)) { + strcpy(dirname, name); + strcat(dirname, "/"); + basename = conf_get_configname(); + } else if ((slash = strrchr(name, '/'))) { + int size = slash - name + 1; + memcpy(dirname, name, size); + dirname[size] = 0; + if (slash[1]) + basename = slash + 1; + else + basename = conf_get_configname(); + } else + basename = name; + } else + basename = conf_get_configname(); + + sprintf(newname, "%s%s", dirname, basename); + env = getenv("KCONFIG_OVERWRITECONFIG"); + if (!env || !*env) { + sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid()); + out = fopen(tmpname, "w"); + } else { + *tmpname = 0; + out = fopen(newname, "w"); + } + if (!out) + return 1; + + conf_write_heading(out, &kconfig_printer_cb, NULL); + + if (!conf_get_changed()) + sym_clear_all_valid(); + + menu = rootmenu.list; + while (menu) { + sym = menu->sym; + if (!sym) { + if (!menu_is_visible(menu)) + goto next; + str = menu_get_prompt(menu); + fprintf(out, "\n" + "#\n" + "# %s\n" + "#\n", str); + } else if (!(sym->flags & SYMBOL_CHOICE)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next; + sym->flags &= ~SYMBOL_WRITE; + + conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + } + +next: + if (menu->list) { + menu = menu->list; + continue; + } + if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->next) { + menu = menu->next; + break; + } + } + } + fclose(out); + + if (*tmpname) { + strcat(dirname, basename); + strcat(dirname, ".old"); + rename(newname, dirname); + if (rename(tmpname, newname)) + return 1; + } + + conf_message(_("configuration written to %s"), newname); + + sym_set_change_count(0); + + return 0; +} + +static int conf_split_config(void) +{ + const char *name; + char path[PATH_MAX+1]; + char *s, *d, c; + struct symbol *sym; + struct stat sb; + int res, i, fd; + + name = conf_get_autoconfig_name(); + conf_read_simple(name, S_DEF_AUTO); + + if (chdir("include/generated")) + return 1; + + res = 0; + for_all_symbols(i, sym) { + sym_calc_value(sym); + if ((sym->flags & SYMBOL_AUTO) || !sym->name) + continue; + if (sym->flags & SYMBOL_WRITE) { + if (sym->flags & SYMBOL_DEF_AUTO) { + /* + * symbol has old and new value, + * so compare them... + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == + sym->def[S_DEF_AUTO].tri) + continue; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (!strcmp(sym_get_string_value(sym), + sym->def[S_DEF_AUTO].val)) + continue; + break; + default: + break; + } + } else { + /* + * If there is no old value, only 'no' (unset) + * is allowed as new value. + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == no) + continue; + break; + default: + break; + } + } + } else if (!(sym->flags & SYMBOL_DEF_AUTO)) + /* There is neither an old nor a new value. */ + continue; + /* else + * There is an old value, but no new value ('no' (unset) + * isn't saved in auto.conf, so the old value is always + * different from 'no'). + */ + + /* Replace all '_' and append ".h" */ + s = sym->name; + d = path; + while ((c = *s++)) { + c = tolower(c); + *d++ = (c == '_') ? '/' : c; + } + strcpy(d, ".h"); + + /* Assume directory path already exists. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + if (errno != ENOENT) { + res = 1; + break; + } + /* + * Create directory components, + * unless they exist already. + */ + d = path; + while ((d = strchr(d, '/'))) { + *d = 0; + if (stat(path, &sb) && mkdir(path, 0755)) { + res = 1; + goto out; + } + *d++ = '/'; + } + /* Try it again. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + res = 1; + break; + } + } + close(fd); + } +out: + if (chdir("../..")) + return 1; + + return res; +} + +int conf_write_autoconf(void) +{ + struct symbol *sym; + const char *name; + FILE *out, *tristate, *out_h, *out_c; + int i; + + sym_clear_all_valid(); + + file_write_dep("include/generated/auto.conf.cmd"); + + if (conf_split_config()) + return 1; + + out = fopen(".tmpconfig", "w"); + if (!out) + return 1; + + tristate = fopen(".tmpconfig_tristate", "w"); + if (!tristate) { + fclose(out); + return 1; + } + + out_h = fopen(".tmpconfig.h", "w"); + if (!out_h) { + fclose(out); + fclose(tristate); + return 1; + } + + out_c = fopen(".tmpconfig.cmake", "w"); + if (!out_c) { + fclose(out); + fclose(tristate); + fclose(out_h); + } + + conf_write_heading(out, &kconfig_printer_cb, NULL); + + conf_write_heading(tristate, &tristate_printer_cb, NULL); + + conf_write_heading(out_h, &header_printer_cb, NULL); + + conf_write_heading(out_c, &kconfig_printer_cmake_cb, NULL); + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE) || !sym->name) + continue; + + /* write symbol to auto.conf, tristate and header files */ + conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); + + conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); + + conf_write_symbol(out_h, sym, &header_printer_cb, NULL); + + conf_write_symbol(out_c, sym, &kconfig_printer_cmake_cb, NULL); + } + fclose(out); + fclose(tristate); + fclose(out_h); + fclose(out_c); + + name = getenv("KCONFIG_AUTOHEADER"); + if (!name) + name = "include/generated/autoconf.h"; + if (rename(".tmpconfig.h", name)) + return 1; + name = getenv("KCONFIG_TRISTATE"); + if (!name) + name = "include/generated/tristate.conf"; + if (rename(".tmpconfig_tristate", name)) + return 1; + name = getenv("KCONFIG_CMAKE"); + if (!name) + name = "config.cmake"; + if (rename(".tmpconfig.cmake", name)) + return 1; + name = conf_get_autoconfig_name(); + /* + * This must be the last step, kbuild has a dependency on auto.conf + * and this marks the successful completion of the previous steps. + */ + if (rename(".tmpconfig", name)) + return 1; + + return 0; +} + +static int sym_change_count; +static void (*conf_changed_callback)(void); + +void sym_set_change_count(int count) +{ + int _sym_change_count = sym_change_count; + sym_change_count = count; + if (conf_changed_callback && + (bool)_sym_change_count != (bool)count) + conf_changed_callback(); +} + +void sym_add_change_count(int count) +{ + sym_set_change_count(count + sym_change_count); +} + +bool conf_get_changed(void) +{ + return sym_change_count; +} + +void conf_set_changed_callback(void (*fn)(void)) +{ + conf_changed_callback = fn; +} + +static void randomize_choice_values(struct symbol *csym) +{ + struct property *prop; + struct symbol *sym; + struct expr *e; + int cnt, def; + + /* + * If choice is mod then we may have more items selected + * and if no then no-one. + * In both cases stop. + */ + if (csym->curr.tri != yes) + return; + + prop = sym_get_choice_prop(csym); + + /* count entries in choice block */ + cnt = 0; + expr_list_for_each_sym(prop->expr, e, sym) + cnt++; + + /* + * find a random value and set it to yes, + * set the rest to no so we have only one set + */ + def = (rand() % cnt); + + cnt = 0; + expr_list_for_each_sym(prop->expr, e, sym) { + if (def == cnt++) { + sym->def[S_DEF_USER].tri = yes; + csym->def[S_DEF_USER].val = sym; + } + else { + sym->def[S_DEF_USER].tri = no; + } + } + csym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + csym->flags &= ~(SYMBOL_VALID); +} + +static void set_all_choice_values(struct symbol *csym) +{ + struct property *prop; + struct symbol *sym; + struct expr *e; + + prop = sym_get_choice_prop(csym); + + /* + * Set all non-assinged choice values to no + */ + expr_list_for_each_sym(prop->expr, e, sym) { + if (!sym_has_value(sym)) + sym->def[S_DEF_USER].tri = no; + } + csym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + csym->flags &= ~(SYMBOL_VALID); +} + +void conf_set_all_new_symbols(enum conf_def_mode mode) +{ + struct symbol *sym, *csym; + int i, cnt; + + for_all_symbols(i, sym) { + if (sym_has_value(sym)) + continue; + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + switch (mode) { + case def_yes: + sym->def[S_DEF_USER].tri = yes; + break; + case def_mod: + sym->def[S_DEF_USER].tri = mod; + break; + case def_no: + sym->def[S_DEF_USER].tri = no; + break; + case def_random: + cnt = sym_get_type(sym) == S_TRISTATE ? 3 : 2; + sym->def[S_DEF_USER].tri = (tristate)(rand() % cnt); + break; + default: + continue; + } + if (!(sym_is_choice(sym) && mode == def_random)) + sym->flags |= SYMBOL_DEF_USER; + break; + default: + break; + } + + } + + sym_clear_all_valid(); + + /* + * We have different type of choice blocks. + * If curr.tri equals to mod then we can select several + * choice symbols in one block. + * In this case we do nothing. + * If curr.tri equals yes then only one symbol can be + * selected in a choice block and we set it to yes, + * and the rest to no. + */ + for_all_symbols(i, csym) { + if (sym_has_value(csym) || !sym_is_choice(csym)) + continue; + + sym_calc_value(csym); + if (mode == def_random) + randomize_choice_values(csym); + else + set_all_choice_values(csym); + } +} diff --git a/carl9170fw/config/expr.c b/carl9170fw/config/expr.c new file mode 100644 index 0000000..290ce41 --- /dev/null +++ b/carl9170fw/config/expr.c @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lkc.h" + +#define DEBUG_EXPR 0 + +struct expr *expr_alloc_symbol(struct symbol *sym) +{ + struct expr *e = calloc(1, sizeof(*e)); + e->type = E_SYMBOL; + e->left.sym = sym; + return e; +} + +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) +{ + struct expr *e = calloc(1, sizeof(*e)); + e->type = type; + e->left.expr = ce; + return e; +} + +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) +{ + struct expr *e = calloc(1, sizeof(*e)); + e->type = type; + e->left.expr = e1; + e->right.expr = e2; + return e; +} + +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) +{ + struct expr *e = calloc(1, sizeof(*e)); + e->type = type; + e->left.sym = s1; + e->right.sym = s2; + return e; +} + +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; +} + +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; +} + +struct expr *expr_copy(const struct expr *org) +{ + struct expr *e; + + if (!org) + return NULL; + + e = malloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + switch (org->type) { + case E_SYMBOL: + e->left = org->left; + break; + case E_NOT: + e->left.expr = expr_copy(org->left.expr); + break; + case E_EQUAL: + case E_UNEQUAL: + e->left.sym = org->left.sym; + e->right.sym = org->right.sym; + break; + case E_AND: + case E_OR: + case E_LIST: + e->left.expr = expr_copy(org->left.expr); + e->right.expr = expr_copy(org->right.expr); + break; + default: + printf("can't copy type %d\n", e->type); + free(e); + e = NULL; + break; + } + + return e; +} + +void expr_free(struct expr *e) +{ + if (!e) + return; + + switch (e->type) { + case E_SYMBOL: + break; + case E_NOT: + expr_free(e->left.expr); + return; + case E_EQUAL: + case E_UNEQUAL: + break; + case E_OR: + case E_AND: + expr_free(e->left.expr); + expr_free(e->right.expr); + break; + default: + printf("how to free type %d?\n", e->type); + break; + } + free(e); +} + +static int trans_count; + +#define e1 (*ep1) +#define e2 (*ep2) + +static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ + if (e1->type == type) { + __expr_eliminate_eq(type, &e1->left.expr, &e2); + __expr_eliminate_eq(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + __expr_eliminate_eq(type, &e1, &e2->left.expr); + __expr_eliminate_eq(type, &e1, &e2->right.expr); + return; + } + if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym && + (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) + return; + if (!expr_eq(e1, e2)) + return; + trans_count++; + expr_free(e1); expr_free(e2); + switch (type) { + case E_OR: + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + break; + case E_AND: + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + break; + default: + ; + } +} + +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) +{ + if (!e1 || !e2) + return; + switch (e1->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e1->type, ep1, ep2); + default: + ; + } + if (e1->type != e2->type) switch (e2->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e2->type, ep1, ep2); + default: + ; + } + e1 = expr_eliminate_yn(e1); + e2 = expr_eliminate_yn(e2); +} + +#undef e1 +#undef e2 + +int expr_eq(struct expr *e1, struct expr *e2) +{ + int res, old_count; + + if (e1->type != e2->type) + return 0; + switch (e1->type) { + case E_EQUAL: + case E_UNEQUAL: + return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; + case E_SYMBOL: + return e1->left.sym == e2->left.sym; + case E_NOT: + return expr_eq(e1->left.expr, e2->left.expr); + case E_AND: + case E_OR: + e1 = expr_copy(e1); + e2 = expr_copy(e2); + old_count = trans_count; + expr_eliminate_eq(&e1, &e2); + res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym); + expr_free(e1); + expr_free(e2); + trans_count = old_count; + return res; + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + + if (DEBUG_EXPR) { + expr_fprint(e1, stdout); + printf(" = "); + expr_fprint(e2, stdout); + printf(" ?\n"); + } + + return 0; +} + +struct expr *expr_eliminate_yn(struct expr *e) +{ + struct expr *tmp; + + if (e) switch (e->type) { + case E_AND: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } + } + break; + case E_OR: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + break; + default: + ; + } + return e; +} + +/* + * bool FOO!=n => FOO + */ +struct expr *expr_trans_bool(struct expr *e) +{ + if (!e) + return NULL; + switch (e->type) { + case E_AND: + case E_OR: + case E_NOT: + e->left.expr = expr_trans_bool(e->left.expr); + e->right.expr = expr_trans_bool(e->right.expr); + break; + case E_UNEQUAL: + // FOO!=n -> FOO + if (e->left.sym->type == S_TRISTATE) { + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + } + } + break; + default: + ; + } + return e; +} + +/* + * e1 || e2 -> ? + */ +static struct expr *expr_join_or(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='m') -> (a!='n') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='n') -> (a!='m') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { + // (a='m') || (a='n') -> (a!='y') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); + } + } + if (sym1->type == S_BOOLEAN && sym1 == sym2) { + if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || + (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) + return expr_alloc_symbol(&symbol_yes); + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") || ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static struct expr *expr_join_and(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) + // (a) && (a='y') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) + // (a) && (a!='n') -> (a) + return expr_alloc_symbol(sym1); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) + // (a) && (a!='m') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e1->right.sym; + if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e2->right.sym; + if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='m') -> (a='n') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) + // (a!='m') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || + (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) + return NULL; + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") && ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp; + + if (e1->type == type) { + expr_eliminate_dups1(type, &e1->left.expr, &e2); + expr_eliminate_dups1(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups1(type, &e1, &e2->left.expr); + expr_eliminate_dups1(type, &e1, &e2->right.expr); + return; + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e1->type, &e1, &e1); + default: + ; + } + + switch (type) { + case E_OR: + tmp = expr_join_or(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_no); + e2 = tmp; + trans_count++; + } + break; + case E_AND: + tmp = expr_join_and(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_yes); + e2 = tmp; + trans_count++; + } + break; + default: + ; + } +#undef e1 +#undef e2 +} + +static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp, *tmp1, *tmp2; + + if (e1->type == type) { + expr_eliminate_dups2(type, &e1->left.expr, &e2); + expr_eliminate_dups2(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups2(type, &e1, &e2->left.expr); + expr_eliminate_dups2(type, &e1, &e2->right.expr); + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO || BAR) && (!FOO && !BAR) -> n + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_and(&tmp1, &tmp2); + if (expr_is_yes(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_no); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + case E_AND: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO && BAR) || (!FOO || !BAR) -> y + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_or(&tmp1, &tmp2); + if (expr_is_no(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_yes); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + default: + ; + } +#undef e1 +#undef e2 +} + +struct expr *expr_eliminate_dups(struct expr *e) +{ + int oldcount; + if (!e) + return e; + + oldcount = trans_count; + while (1) { + trans_count = 0; + switch (e->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e->type, &e, &e); + expr_eliminate_dups2(e->type, &e, &e); + default: + ; + } + if (!trans_count) + break; + e = expr_eliminate_yn(e); + } + trans_count = oldcount; + return e; +} + +struct expr *expr_transform(struct expr *e) +{ + struct expr *tmp; + + if (!e) + return NULL; + switch (e->type) { + case E_EQUAL: + case E_UNEQUAL: + case E_SYMBOL: + case E_LIST: + break; + default: + e->left.expr = expr_transform(e->left.expr); + e->right.expr = expr_transform(e->right.expr); + } + + switch (e->type) { + case E_EQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + break; + case E_UNEQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + break; + case E_NOT: + switch (e->left.expr->type) { + case E_NOT: + // !!a -> a + tmp = e->left.expr->left.expr; + free(e->left.expr); + free(e); + e = tmp; + e = expr_transform(e); + break; + case E_EQUAL: + case E_UNEQUAL: + // !a='x' -> a!='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; + break; + case E_OR: + // !(a || b) -> !a && !b + tmp = e->left.expr; + e->type = E_AND; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_AND: + // !(a && b) -> !a || !b + tmp = e->left.expr; + e->type = E_OR; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_SYMBOL: + if (e->left.expr->left.sym == &symbol_yes) { + // !'y' -> 'n' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + break; + } + if (e->left.expr->left.sym == &symbol_mod) { + // !'m' -> 'm' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_mod; + break; + } + if (e->left.expr->left.sym == &symbol_no) { + // !'n' -> 'y' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + break; + } + break; + default: + ; + } + break; + default: + ; + } + return e; +} + +int expr_contains_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return 0; + + switch (dep->type) { + case E_AND: + case E_OR: + return expr_contains_symbol(dep->left.expr, sym) || + expr_contains_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + case E_UNEQUAL: + return dep->left.sym == sym || + dep->right.sym == sym; + case E_NOT: + return expr_contains_symbol(dep->left.expr, sym); + default: + ; + } + return 0; +} + +bool expr_depends_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return false; + + switch (dep->type) { + case E_AND: + return expr_depends_symbol(dep->left.expr, sym) || + expr_depends_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) + return true; + } + break; + case E_UNEQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_no) + return true; + } + break; + default: + ; + } + return false; +} + +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_AND, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_OR, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + if (e1->type == type) { + expr_extract_eq(type, ep, &e1->left.expr, &e2); + expr_extract_eq(type, ep, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_extract_eq(type, ep, ep1, &e2->left.expr); + expr_extract_eq(type, ep, ep1, &e2->right.expr); + return; + } + if (expr_eq(e1, e2)) { + *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1; + expr_free(e2); + if (type == E_AND) { + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + } else if (type == E_OR) { + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + } + } +#undef e1 +#undef e2 +} + +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) +{ + struct expr *e1, *e2; + + if (!e) { + e = expr_alloc_symbol(sym); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + } + switch (e->type) { + case E_AND: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_AND, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_OR, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_OR: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_OR, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_AND, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_NOT: + return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); + case E_UNEQUAL: + case E_EQUAL: + if (type == E_EQUAL) { + if (sym == &symbol_yes) + return expr_copy(e); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_no); + if (sym == &symbol_no) + return expr_alloc_one(E_NOT, expr_copy(e)); + } else { + if (sym == &symbol_yes) + return expr_alloc_one(E_NOT, expr_copy(e)); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_yes); + if (sym == &symbol_no) + return expr_copy(e); + } + break; + case E_SYMBOL: + return expr_alloc_comp(type, e->left.sym, sym); + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + return NULL; +} + +tristate expr_calc_value(struct expr *e) +{ + tristate val1, val2; + const char *str1, *str2; + + if (!e) + return yes; + + switch (e->type) { + case E_SYMBOL: + sym_calc_value(e->left.sym); + return e->left.sym->curr.tri; + case E_AND: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_AND(val1, val2); + case E_OR: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_OR(val1, val2); + case E_NOT: + val1 = expr_calc_value(e->left.expr); + return EXPR_NOT(val1); + case E_EQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? yes : no; + case E_UNEQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? no : yes; + default: + printf("expr_calc_value: %d?\n", e->type); + return no; + } +} + +int expr_compare_type(enum expr_type t1, enum expr_type t2) +{ +#if 0 + return 1; +#else + if (t1 == t2) + return 0; + switch (t1) { + case E_EQUAL: + case E_UNEQUAL: + if (t2 == E_NOT) + return 1; + case E_NOT: + if (t2 == E_AND) + return 1; + case E_AND: + if (t2 == E_OR) + return 1; + case E_OR: + if (t2 == E_LIST) + return 1; + case E_LIST: + if (t2 == 0) + return 1; + default: + return -1; + } + printf("[%dgt%d?]", t1, t2); + return 0; +#endif +} + +static inline struct expr * +expr_get_leftmost_symbol(const struct expr *e) +{ + + if (e == NULL) + return NULL; + + while (e->type != E_SYMBOL) + e = e->left.expr; + + return expr_copy(e); +} + +/* + * Given expression `e1' and `e2', returns the leaf of the longest + * sub-expression of `e1' not containing 'e2. + */ +struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2) +{ + struct expr *ret; + + switch (e1->type) { + case E_OR: + return expr_alloc_and( + expr_simplify_unmet_dep(e1->left.expr, e2), + expr_simplify_unmet_dep(e1->right.expr, e2)); + case E_AND: { + struct expr *e; + e = expr_alloc_and(expr_copy(e1), expr_copy(e2)); + e = expr_eliminate_dups(e); + ret = (!expr_eq(e, e1)) ? e1 : NULL; + expr_free(e); + break; + } + default: + ret = e1; + break; + } + + return expr_get_leftmost_symbol(ret); +} + +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) +{ + if (!e) { + fn(data, NULL, "y"); + return; + } + + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, "("); + switch (e->type) { + case E_SYMBOL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + break; + case E_NOT: + fn(data, NULL, "!"); + expr_print(e->left.expr, fn, data, E_NOT); + break; + case E_EQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + fn(data, NULL, "="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_UNEQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + fn(data, NULL, "!="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_OR: + expr_print(e->left.expr, fn, data, E_OR); + fn(data, NULL, " || "); + expr_print(e->right.expr, fn, data, E_OR); + break; + case E_AND: + expr_print(e->left.expr, fn, data, E_AND); + fn(data, NULL, " && "); + expr_print(e->right.expr, fn, data, E_AND); + break; + case E_LIST: + fn(data, e->right.sym, e->right.sym->name); + if (e->left.expr) { + fn(data, NULL, " ^ "); + expr_print(e->left.expr, fn, data, E_LIST); + } + break; + case E_RANGE: + fn(data, NULL, "["); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, " "); + fn(data, e->right.sym, e->right.sym->name); + fn(data, NULL, "]"); + break; + default: + { + char buf[32]; + sprintf(buf, "<unknown type %d>", e->type); + fn(data, NULL, buf); + break; + } + } + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, ")"); +} + +static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) +{ + xfwrite(str, strlen(str), 1, data); +} + +void expr_fprint(struct expr *e, FILE *out) +{ + expr_print(e, expr_print_file_helper, out, E_NONE); +} + +static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str) +{ + struct gstr *gs = (struct gstr*)data; + const char *sym_str = NULL; + + if (sym) + sym_str = sym_get_string_value(sym); + + if (gs->max_width) { + unsigned extra_length = strlen(str); + const char *last_cr = strrchr(gs->s, '\n'); + unsigned last_line_length; + + if (sym_str) + extra_length += 4 + strlen(sym_str); + + if (!last_cr) + last_cr = gs->s; + + last_line_length = strlen(gs->s) - (last_cr - gs->s); + + if ((last_line_length + extra_length) > gs->max_width) + str_append(gs, "\\\n"); + } + + str_append(gs, str); + if (sym && sym->type != S_UNKNOWN) + str_printf(gs, " [=%s]", sym_str); +} + +void expr_gstr_print(struct expr *e, struct gstr *gs) +{ + expr_print(e, expr_print_gstr_helper, gs, E_NONE); +} diff --git a/carl9170fw/config/expr.h b/carl9170fw/config/expr.h new file mode 100644 index 0000000..d4ecce8 --- /dev/null +++ b/carl9170fw/config/expr.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef EXPR_H +#define EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <assert.h> +#include <stdio.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +struct file { + struct file *next; + struct file *parent; + const char *name; + int lineno; +}; + +typedef enum tristate { + no, mod, yes +} tristate; + +enum expr_type { + E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LIST, E_SYMBOL, E_RANGE +}; + +union expr_data { + struct expr *expr; + struct symbol *sym; +}; + +struct expr { + enum expr_type type; + union expr_data left, right; +}; + +#define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) +#define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) +#define EXPR_NOT(dep) (2-(dep)) + +#define expr_list_for_each_sym(l, e, s) \ + for (e = (l); e && (s = e->right.sym); e = e->left.expr) + +struct expr_value { + struct expr *expr; + tristate tri; +}; + +struct symbol_value { + void *val; + tristate tri; +}; + +enum symbol_type { + S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER +}; + +/* enum values are used as index to symbol.def[] */ +enum { + S_DEF_USER, /* main user value */ + S_DEF_AUTO, /* values read from auto.conf */ + S_DEF_DEF3, /* Reserved for UI usage */ + S_DEF_DEF4, /* Reserved for UI usage */ + S_DEF_COUNT +}; + +struct symbol { + struct symbol *next; + char *name; + enum symbol_type type; + struct symbol_value curr; + struct symbol_value def[S_DEF_COUNT]; + tristate visible; + int flags; + struct property *prop; + struct expr_value dir_dep; + struct expr_value rev_dep; +}; + +#define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) + +#define SYMBOL_CONST 0x0001 /* symbol is const */ +#define SYMBOL_CHECK 0x0008 /* used during dependency checking */ +#define SYMBOL_CHOICE 0x0010 /* start of a choice block (null name) */ +#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ +#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ +#define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */ +#define SYMBOL_WRITE 0x0200 /* ? */ +#define SYMBOL_CHANGED 0x0400 /* ? */ +#define SYMBOL_AUTO 0x1000 /* value from environment variable */ +#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ +#define SYMBOL_WARNED 0x8000 /* warning has been issued */ + +/* Set when symbol.def[] is used */ +#define SYMBOL_DEF 0x10000 /* First bit of SYMBOL_DEF */ +#define SYMBOL_DEF_USER 0x10000 /* symbol.def[S_DEF_USER] is valid */ +#define SYMBOL_DEF_AUTO 0x20000 /* symbol.def[S_DEF_AUTO] is valid */ +#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ +#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ + +#define SYMBOL_MAXLENGTH 256 +#define SYMBOL_HASHSIZE 9973 + +/* A property represent the config options that can be associated + * with a config "symbol". + * Sample: + * config FOO + * default y + * prompt "foo prompt" + * select BAR + * config BAZ + * int "BAZ Value" + * range 1..255 + */ +enum prop_type { + P_UNKNOWN, + P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */ + P_COMMENT, /* text associated with a comment */ + P_MENU, /* prompt associated with a menuconfig option */ + P_DEFAULT, /* default y */ + P_CHOICE, /* choice value */ + P_SELECT, /* select BAR */ + P_RANGE, /* range 7..100 (for a symbol) */ + P_ENV, /* value from environment variable */ + P_SYMBOL, /* where a symbol is defined */ +}; + +struct property { + struct property *next; /* next property - null if last */ + struct symbol *sym; /* the symbol for which the property is associated */ + enum prop_type type; /* type of property */ + const char *text; /* the prompt value - P_PROMPT, P_MENU, P_COMMENT */ + struct expr_value visible; + struct expr *expr; /* the optional conditional part of the property */ + struct menu *menu; /* the menu the property are associated with + * valid for: P_SELECT, P_RANGE, P_CHOICE, + * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */ + struct file *file; /* what file was this property defined */ + int lineno; /* what lineno was this property defined */ +}; + +#define for_all_properties(sym, st, tok) \ + for (st = sym->prop; st; st = st->next) \ + if (st->type == (tok)) +#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) +#define for_all_prompts(sym, st) \ + for (st = sym->prop; st; st = st->next) \ + if (st->text) + +struct menu { + struct menu *next; + struct menu *parent; + struct menu *list; + struct symbol *sym; + struct property *prompt; + struct expr *visibility; + struct expr *dep; + unsigned int flags; + char *help; + struct file *file; + int lineno; + void *data; +}; + +#define MENU_CHANGED 0x0001 +#define MENU_ROOT 0x0002 + +extern struct file *file_list; +extern struct file *current_file; +struct file *lookup_file(const char *name); + +extern struct symbol symbol_yes, symbol_no, symbol_mod; +extern struct symbol *modules_sym; +extern struct symbol *sym_defconfig_list; +extern int cdebug; +struct expr *expr_alloc_symbol(struct symbol *sym); +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); +struct expr *expr_copy(const struct expr *org); +void expr_free(struct expr *e); +int expr_eq(struct expr *e1, struct expr *e2); +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); +tristate expr_calc_value(struct expr *e); +struct expr *expr_eliminate_yn(struct expr *e); +struct expr *expr_trans_bool(struct expr *e); +struct expr *expr_eliminate_dups(struct expr *e); +struct expr *expr_transform(struct expr *e); +int expr_contains_symbol(struct expr *dep, struct symbol *sym); +bool expr_depends_symbol(struct expr *dep, struct symbol *sym); +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); +struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2); + +void expr_fprint(struct expr *e, FILE *out); +struct gstr; /* forward */ +void expr_gstr_print(struct expr *e, struct gstr *gs); + +static inline int expr_is_yes(struct expr *e) +{ + return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); +} + +static inline int expr_is_no(struct expr *e) +{ + return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); +} + +#ifdef __cplusplus +} +#endif + +#endif /* EXPR_H */ diff --git a/carl9170fw/config/lkc.h b/carl9170fw/config/lkc.h new file mode 100644 index 0000000..c18f2bd --- /dev/null +++ b/carl9170fw/config/lkc.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef LKC_H +#define LKC_H + +#include "expr.h" + +#ifndef KBUILD_NO_NLS +# include <libintl.h> +#else +static inline const char *gettext(const char *txt) { return txt; } +static inline void textdomain(const char *domainname) {} +static inline void bindtextdomain(const char *name, const char *dir) {} +static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; } +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define P(name,type,arg) extern type name arg +#include "lkc_proto.h" +#undef P + +#define SRCTREE "srctree" + +#ifndef PACKAGE +#define PACKAGE "linux" +#endif + +#define LOCALEDIR "/usr/share/locale" + +#define _(text) gettext(text) +#define N_(text) (text) + +#ifndef CONFIG_ +#define CONFIG_ "CONFIG_" +#endif + +#define TF_COMMAND 0x0001 +#define TF_PARAM 0x0002 +#define TF_OPTION 0x0004 + +enum conf_def_mode { + def_default, + def_yes, + def_mod, + def_no, + def_random +}; + +#define T_OPT_MODULES 1 +#define T_OPT_DEFCONFIG_LIST 2 +#define T_OPT_ENV 3 + +struct kconf_id { + int name; + int token; + unsigned int flags; + enum symbol_type stype; +}; + +extern int zconfdebug; + +int zconfparse(void); +void zconfdump(FILE *out); +void zconf_starthelp(void); +FILE *zconf_fopen(const char *name); +void zconf_initscan(const char *name); +void zconf_nextfile(const char *name); +int zconf_lineno(void); +const char *zconf_curname(void); + +/* confdata.c */ +const char *conf_get_configname(void); +const char *conf_get_autoconfig_name(void); +char *conf_get_default_confname(void); +void sym_set_change_count(int count); +void sym_add_change_count(int count); +void conf_set_all_new_symbols(enum conf_def_mode mode); + +struct conf_printer { + void (*print_symbol)(FILE *, struct symbol *, const char *, void *); + void (*print_comment)(FILE *, const char *, void *); +}; + +/* confdata.c and expr.c */ +static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) +{ + assert(len != 0); + + if (fwrite(str, len, count, out) != count) + fprintf(stderr, "Error in writing or end of file.\n"); +} + +/* menu.c */ +void _menu_init(void); +void menu_warn(struct menu *menu, const char *fmt, ...); +struct menu *menu_add_menu(void); +void menu_end_menu(void); +void menu_add_entry(struct symbol *sym); +void menu_end_entry(void); +void menu_add_dep(struct expr *dep); +void menu_add_visibility(struct expr *dep); +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_add_option(int token, char *arg); +void menu_finalize(struct menu *parent); +void menu_set_type(int type); + +/* util.c */ +struct file *file_lookup(const char *name); +int file_write_dep(const char *name); + +struct gstr { + size_t len; + char *s; + /* + * when max_width is not zero long lines in string s (if any) get + * wrapped not to exceed the max_width value + */ + int max_width; +}; +struct gstr str_new(void); +struct gstr str_assign(const char *s); +void str_free(struct gstr *gs); +void str_append(struct gstr *gs, const char *s); +void str_printf(struct gstr *gs, const char *fmt, ...); +const char *str_get(struct gstr *gs); + +/* symbol.c */ +extern struct expr *sym_env_list; + +void sym_init(void); +void sym_clear_all_valid(void); +void sym_set_all_changed(void); +void sym_set_changed(struct symbol *sym); +struct symbol *sym_choice_default(struct symbol *sym); +const char *sym_get_string_default(struct symbol *sym); +struct symbol *sym_check_deps(struct symbol *sym); +struct property *prop_alloc(enum prop_type type, struct symbol *sym); +struct symbol *prop_get_symbol(struct property *prop); +struct property *sym_get_env_prop(struct symbol *sym); + +static inline tristate sym_get_tristate_value(struct symbol *sym) +{ + return sym->curr.tri; +} + + +static inline struct symbol *sym_get_choice_value(struct symbol *sym) +{ + return (struct symbol *)sym->curr.val; +} + +static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) +{ + return sym_set_tristate_value(chval, yes); +} + +static inline bool sym_is_choice(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICE ? true : false; +} + +static inline bool sym_is_choice_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICEVAL ? true : false; +} + +static inline bool sym_is_optional(struct symbol *sym) +{ + return sym->flags & SYMBOL_OPTIONAL ? true : false; +} + +static inline bool sym_has_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_DEF_USER ? true : false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LKC_H */ diff --git a/carl9170fw/config/lkc_proto.h b/carl9170fw/config/lkc_proto.h new file mode 100644 index 0000000..47fe9c3 --- /dev/null +++ b/carl9170fw/config/lkc_proto.h @@ -0,0 +1,54 @@ +#include <stdarg.h> + +/* confdata.c */ +P(conf_parse,void,(const char *name)); +P(conf_read,int,(const char *name)); +P(conf_read_simple,int,(const char *name, int)); +P(conf_write_defconfig,int,(const char *name)); +P(conf_write,int,(const char *name)); +P(conf_write_autoconf,int,(void)); +P(conf_get_changed,bool,(void)); +P(conf_set_changed_callback, void,(void (*fn)(void))); +P(conf_set_message_callback, void,(void (*fn)(const char *fmt, va_list ap))); + +/* menu.c */ +P(rootmenu,struct menu,); + +P(menu_is_visible, bool, (struct menu *menu)); +P(menu_has_prompt, bool, (struct menu *menu)); +P(menu_get_prompt,const char *,(struct menu *menu)); +P(menu_get_root_menu,struct menu *,(struct menu *menu)); +P(menu_get_parent_menu,struct menu *,(struct menu *menu)); +P(menu_has_help,bool,(struct menu *menu)); +P(menu_get_help,const char *,(struct menu *menu)); +P(get_symbol_str, void, (struct gstr *r, struct symbol *sym)); +P(get_relations_str, struct gstr, (struct symbol **sym_arr)); +P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help)); + +/* symbol.c */ +P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); + +P(sym_lookup,struct symbol *,(const char *name, int flags)); +P(sym_find,struct symbol *,(const char *name)); +P(sym_expand_string_value,const char *,(const char *in)); +P(sym_escape_string_value, const char *,(const char *in)); +P(sym_re_search,struct symbol **,(const char *pattern)); +P(sym_type_name,const char *,(enum symbol_type type)); +P(sym_calc_value,void,(struct symbol *sym)); +P(sym_get_type,enum symbol_type,(struct symbol *sym)); +P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); +P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); +P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); +P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); +P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); +P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); +P(sym_is_changable,bool,(struct symbol *sym)); +P(sym_get_choice_prop,struct property *,(struct symbol *sym)); +P(sym_get_default_prop,struct property *,(struct symbol *sym)); +P(sym_get_string_value,const char *,(struct symbol *sym)); + +P(prop_get_type_name,const char *,(enum prop_type type)); + +/* expr.c */ +P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); +P(expr_print,void,(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)); diff --git a/carl9170fw/config/menu.c b/carl9170fw/config/menu.c new file mode 100644 index 0000000..8c2a97e --- /dev/null +++ b/carl9170fw/config/menu.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "lkc.h" + +static const char nohelp_text[] = "There is no help available for this option."; + +struct menu rootmenu; +static struct menu **last_entry_ptr; + +struct file *file_list; +struct file *current_file; + +void menu_warn(struct menu *menu, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +static void prop_warn(struct property *prop, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void _menu_init(void) +{ + current_entry = current_menu = &rootmenu; + last_entry_ptr = &rootmenu.list; +} + +void menu_add_entry(struct symbol *sym) +{ + struct menu *menu; + + menu = malloc(sizeof(*menu)); + memset(menu, 0, sizeof(*menu)); + menu->sym = sym; + menu->parent = current_menu; + menu->file = current_file; + menu->lineno = zconf_lineno(); + + *last_entry_ptr = menu; + last_entry_ptr = &menu->next; + current_entry = menu; + if (sym) + menu_add_symbol(P_SYMBOL, sym, NULL); +} + +void menu_end_entry(void) +{ +} + +struct menu *menu_add_menu(void) +{ + menu_end_entry(); + last_entry_ptr = ¤t_entry->list; + return current_menu = current_entry; +} + +void menu_end_menu(void) +{ + last_entry_ptr = ¤t_menu->next; + current_menu = current_menu->parent; +} + +static struct expr *menu_check_dep(struct expr *e) +{ + if (!e) + return e; + + switch (e->type) { + case E_NOT: + e->left.expr = menu_check_dep(e->left.expr); + break; + case E_OR: + case E_AND: + e->left.expr = menu_check_dep(e->left.expr); + e->right.expr = menu_check_dep(e->right.expr); + break; + case E_SYMBOL: + /* change 'm' into 'm' && MODULES */ + if (e->left.sym == &symbol_mod) + return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); + break; + default: + break; + } + return e; +} + +void menu_add_dep(struct expr *dep) +{ + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +} + +void menu_set_type(int type) +{ + struct symbol *sym = current_entry->sym; + + if (sym->type == type) + return; + if (sym->type == S_UNKNOWN) { + sym->type = type; + return; + } + menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'", + sym->name ? sym->name : "<choice>", + sym_type_name(sym->type), sym_type_name(type)); +} + +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +{ + struct property *prop = prop_alloc(type, current_entry->sym); + + prop->menu = current_entry; + prop->expr = expr; + prop->visible.expr = menu_check_dep(dep); + + if (prompt) { + if (isspace(*prompt)) { + prop_warn(prop, "leading whitespace ignored"); + while (isspace(*prompt)) + prompt++; + } + if (current_entry->prompt && current_entry != &rootmenu) + prop_warn(prop, "prompt redefined"); + + /* Apply all upper menus' visibilities to actual prompts. */ + if(type == P_PROMPT) { + struct menu *menu = current_entry; + + while ((menu = menu->parent) != NULL) { + if (!menu->visibility) + continue; + prop->visible.expr + = expr_alloc_and(prop->visible.expr, + menu->visibility); + } + } + + current_entry->prompt = prop; + } + prop->text = prompt; + + return prop; +} + +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) +{ + return menu_add_prop(type, prompt, NULL, dep); +} + +void menu_add_visibility(struct expr *expr) +{ + current_entry->visibility = expr_alloc_and(current_entry->visibility, + expr); +} + +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) +{ + menu_add_prop(type, NULL, expr, dep); +} + +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) +{ + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); +} + +void menu_add_option(int token, char *arg) +{ + struct property *prop; + + switch (token) { + case T_OPT_MODULES: + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(current_entry->sym); + break; + case T_OPT_DEFCONFIG_LIST: + if (!sym_defconfig_list) + sym_defconfig_list = current_entry->sym; + else if (sym_defconfig_list != current_entry->sym) + zconf_error("trying to redefine defconfig symbol"); + break; + case T_OPT_ENV: + prop_add_env(arg); + break; + } +} + +static int menu_validate_number(struct symbol *sym, struct symbol *sym2) +{ + return sym2->type == S_INT || sym2->type == S_HEX || + (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); +} + +static void sym_check_prop(struct symbol *sym) +{ + struct property *prop; + struct symbol *sym2; + for (prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_DEFAULT: + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && + prop->expr->type != E_SYMBOL) + prop_warn(prop, + "default for config symbol '%s'" + " must be a single symbol", sym->name); + if (prop->expr->type != E_SYMBOL) + break; + sym2 = prop_get_symbol(prop); + if (sym->type == S_HEX || sym->type == S_INT) { + if (!menu_validate_number(sym, sym2)) + prop_warn(prop, + "'%s': number is invalid", + sym->name); + } + break; + case P_SELECT: + sym2 = prop_get_symbol(prop); + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) + prop_warn(prop, + "config symbol '%s' uses select, but is " + "not boolean or tristate", sym->name); + else if (sym2->type != S_UNKNOWN && + sym2->type != S_BOOLEAN && + sym2->type != S_TRISTATE) + prop_warn(prop, + "'%s' has wrong type. 'select' only " + "accept arguments of boolean and " + "tristate type", sym2->name); + break; + case P_RANGE: + if (sym->type != S_INT && sym->type != S_HEX) + prop_warn(prop, "range is only allowed " + "for int or hex symbols"); + if (!menu_validate_number(sym, prop->expr->left.sym) || + !menu_validate_number(sym, prop->expr->right.sym)) + prop_warn(prop, "range is invalid"); + break; + default: + ; + } + } +} + +void menu_finalize(struct menu *parent) +{ + struct menu *menu, *last_menu; + struct symbol *sym; + struct property *prop; + struct expr *parentdep, *basedep, *dep, *dep2, **ep; + + sym = parent->sym; + if (parent->list) { + if (sym && sym_is_choice(sym)) { + if (sym->type == S_UNKNOWN) { + /* find the first choice value to find out choice type */ + current_entry = parent; + for (menu = parent->list; menu; menu = menu->next) { + if (menu->sym && menu->sym->type != S_UNKNOWN) { + menu_set_type(menu->sym->type); + break; + } + } + } + /* set the type of the remaining choice values */ + for (menu = parent->list; menu; menu = menu->next) { + current_entry = menu; + if (menu->sym && menu->sym->type == S_UNKNOWN) + menu_set_type(sym->type); + } + parentdep = expr_alloc_symbol(sym); + } else if (parent->prompt) + parentdep = parent->prompt->visible.expr; + else + parentdep = parent->dep; + + for (menu = parent->list; menu; menu = menu->next) { + basedep = expr_transform(menu->dep); + basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + if (menu->sym) + prop = menu->sym->prop; + else + prop = menu->prompt; + for (; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_transform(prop->visible.expr); + dep = expr_alloc_and(expr_copy(basedep), dep); + dep = expr_eliminate_dups(dep); + if (menu->sym && menu->sym->type != S_TRISTATE) + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } + } + } + for (menu = parent->list; menu; menu = menu->next) + menu_finalize(menu); + } else if (sym) { + basedep = parent->prompt ? parent->prompt->visible.expr : NULL; + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); + basedep = expr_eliminate_dups(expr_transform(basedep)); + last_menu = NULL; + for (menu = parent->next; menu; menu = menu->next) { + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; + if (!expr_contains_symbol(dep, sym)) + break; + if (expr_depends_symbol(dep, sym)) + goto next; + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); + dep = expr_eliminate_dups(expr_transform(dep)); + dep2 = expr_copy(basedep); + expr_eliminate_eq(&dep, &dep2); + expr_free(dep); + if (!expr_is_yes(dep2)) { + expr_free(dep2); + break; + } + expr_free(dep2); + next: + menu_finalize(menu); + menu->parent = parent; + last_menu = menu; + } + if (last_menu) { + parent->list = parent->next; + parent->next = last_menu->next; + last_menu->next = NULL; + } + + sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); + } + for (menu = parent->list; menu; menu = menu->next) { + if (sym && sym_is_choice(sym) && + menu->sym && !sym_is_choice_value(menu->sym)) { + current_entry = menu; + menu->sym->flags |= SYMBOL_CHOICEVAL; + if (!menu->prompt) + menu_warn(menu, "choice value must have a prompt"); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_DEFAULT) + prop_warn(prop, "defaults for choice " + "values not supported"); + if (prop->menu == menu) + continue; + if (prop->type == P_PROMPT && + prop->menu->parent->sym != sym) + prop_warn(prop, "choice value used outside its choice group"); + } + /* Non-tristate choice values of tristate choices must + * depend on the choice being set to Y. The choice + * values' dependencies were propagated to their + * properties above, so the change here must be re- + * propagated. + */ + if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { + basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); + menu->dep = expr_alloc_and(basedep, menu->dep); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + prop->visible.expr = expr_alloc_and(expr_copy(basedep), + prop->visible.expr); + } + } + menu_add_symbol(P_CHOICE, sym, NULL); + prop = sym_get_choice_prop(sym); + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) + ; + *ep = expr_alloc_one(E_LIST, NULL); + (*ep)->right.sym = menu->sym; + } + if (menu->list && (!menu->prompt || !menu->prompt->text)) { + for (last_menu = menu->list; ; last_menu = last_menu->next) { + last_menu->parent = parent; + if (!last_menu->next) + break; + } + last_menu->next = menu->next; + menu->next = menu->list; + menu->list = NULL; + } + } + + if (sym && !(sym->flags & SYMBOL_WARNED)) { + if (sym->type == S_UNKNOWN) + menu_warn(parent, "config symbol defined without type"); + + if (sym_is_choice(sym) && !parent->prompt) + menu_warn(parent, "choice must have a prompt"); + + /* Check properties connected to this symbol */ + sym_check_prop(sym); + sym->flags |= SYMBOL_WARNED; + } + + if (sym && !sym_is_optional(sym) && parent->prompt) { + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, + expr_alloc_and(parent->prompt->visible.expr, + expr_alloc_symbol(&symbol_mod))); + } +} + +bool menu_has_prompt(struct menu *menu) +{ + if (!menu->prompt) + return false; + return true; +} + +bool menu_is_visible(struct menu *menu) +{ + struct menu *child; + struct symbol *sym; + tristate visible; + + if (!menu->prompt) + return false; + + if (menu->visibility) { + if (expr_calc_value(menu->visibility) == no) + return no; + } + + sym = menu->sym; + if (sym) { + sym_calc_value(sym); + visible = menu->prompt->visible.tri; + } else + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); + + if (visible != no) + return true; + + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child)) { + if (sym) + sym->flags |= SYMBOL_DEF_USER; + return true; + } + } + + return false; +} + +const char *menu_get_prompt(struct menu *menu) +{ + if (menu->prompt) + return menu->prompt->text; + else if (menu->sym) + return menu->sym->name; + return NULL; +} + +struct menu *menu_get_root_menu(struct menu *menu) +{ + return &rootmenu; +} + +struct menu *menu_get_parent_menu(struct menu *menu) +{ + enum prop_type type; + + for (; menu != &rootmenu; menu = menu->parent) { + type = menu->prompt ? menu->prompt->type : 0; + if (type == P_MENU) + break; + } + return menu; +} + +bool menu_has_help(struct menu *menu) +{ + return menu->help != NULL; +} + +const char *menu_get_help(struct menu *menu) +{ + if (menu->help) + return menu->help; + else + return ""; +} + +static void get_prompt_str(struct gstr *r, struct property *prop) +{ + int i, j; + struct menu *submenu[8], *menu; + + str_printf(r, _("Prompt: %s\n"), _(prop->text)); + str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name, + prop->menu->lineno); + if (!expr_is_yes(prop->visible.expr)) { + str_append(r, _(" Depends on: ")); + expr_gstr_print(prop->visible.expr, r); + str_append(r, "\n"); + } + menu = prop->menu->parent; + for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) + submenu[i++] = menu; + if (i > 0) { + str_printf(r, _(" Location:\n")); + for (j = 4; --i >= 0; j += 2) { + menu = submenu[i]; + str_printf(r, "%*c-> %s", j, ' ', _(menu_get_prompt(menu))); + if (menu->sym) { + str_printf(r, " (%s [=%s])", menu->sym->name ? + menu->sym->name : _("<choice>"), + sym_get_string_value(menu->sym)); + } + str_append(r, "\n"); + } + } +} + +void get_symbol_str(struct gstr *r, struct symbol *sym) +{ + bool hit; + struct property *prop; + + if (sym && sym->name) { + str_printf(r, "Symbol: %s [=%s]\n", sym->name, + sym_get_string_value(sym)); + str_printf(r, "Type : %s\n", sym_type_name(sym->type)); + if (sym->type == S_INT || sym->type == S_HEX) { + prop = sym_get_range_prop(sym); + if (prop) { + str_printf(r, "Range : "); + expr_gstr_print(prop->expr, r); + str_append(r, "\n"); + } + } + } + for_all_prompts(sym, prop) + get_prompt_str(r, prop); + hit = false; + for_all_properties(sym, prop, P_SELECT) { + if (!hit) { + str_append(r, " Selects: "); + hit = true; + } else + str_printf(r, " && "); + expr_gstr_print(prop->expr, r); + } + if (hit) + str_append(r, "\n"); + if (sym->rev_dep.expr) { + str_append(r, _(" Selected by: ")); + expr_gstr_print(sym->rev_dep.expr, r); + str_append(r, "\n"); + } + str_append(r, "\n\n"); +} + +struct gstr get_relations_str(struct symbol **sym_arr) +{ + struct symbol *sym; + struct gstr res = str_new(); + int i; + + for (i = 0; sym_arr && (sym = sym_arr[i]); i++) + get_symbol_str(&res, sym); + if (!i) + str_append(&res, _("No matches found.\n")); + return res; +} + + +void menu_get_ext_help(struct menu *menu, struct gstr *help) +{ + struct symbol *sym = menu->sym; + const char *help_text = nohelp_text; + + if (menu_has_help(menu)) { + if (sym->name) + str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); + help_text = menu_get_help(menu); + } + str_printf(help, "%s\n", _(help_text)); + if (sym) + get_symbol_str(help, sym); +} diff --git a/carl9170fw/config/symbol.c b/carl9170fw/config/symbol.c new file mode 100644 index 0000000..22a3c40 --- /dev/null +++ b/carl9170fw/config/symbol.c @@ -0,0 +1,1310 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <sys/utsname.h> + +#include "lkc.h" + +struct symbol symbol_yes = { + .name = "y", + .curr = { "y", yes }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_mod = { + .name = "m", + .curr = { "m", mod }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_no = { + .name = "n", + .curr = { "n", no }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_empty = { + .name = "", + .curr = { "", no }, + .flags = SYMBOL_VALID, +}; + +struct symbol *sym_defconfig_list; +struct symbol *modules_sym; +tristate modules_val; + +struct expr *sym_env_list; + +static void sym_add_default(struct symbol *sym, const char *def) +{ + struct property *prop = prop_alloc(P_DEFAULT, sym); + + prop->expr = expr_alloc_symbol(sym_lookup(def, SYMBOL_CONST)); +} + +void sym_init(void) +{ + struct symbol *sym; + struct utsname uts; + static bool inited = false; + + if (inited) + return; + inited = true; + + uname(&uts); + + sym = sym_lookup("UNAME_RELEASE", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + sym_add_default(sym, uts.release); +} + +enum symbol_type sym_get_type(struct symbol *sym) +{ + enum symbol_type type = sym->type; + + if (type == S_TRISTATE) { + if (sym_is_choice_value(sym) && sym->visible == yes) + type = S_BOOLEAN; + else if (modules_val == no) + type = S_BOOLEAN; + } + return type; +} + +const char *sym_type_name(enum symbol_type type) +{ + switch (type) { + case S_BOOLEAN: + return "boolean"; + case S_TRISTATE: + return "tristate"; + case S_INT: + return "integer"; + case S_HEX: + return "hex"; + case S_STRING: + return "string"; + case S_UNKNOWN: + return "unknown"; + case S_OTHER: + break; + } + return "???"; +} + +struct property *sym_get_choice_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_choices(sym, prop) + return prop; + return NULL; +} + +struct property *sym_get_env_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_ENV) + return prop; + return NULL; +} + +struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static struct property *sym_get_range_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_RANGE) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static int sym_get_range_val(struct symbol *sym, int base) +{ + sym_calc_value(sym); + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + return strtol(sym->curr.val, NULL, base); +} + +static void sym_validate_range(struct symbol *sym) +{ + struct property *prop; + int base, val, val2; + char str[64]; + + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + return; + } + prop = sym_get_range_prop(sym); + if (!prop) + return; + val = strtol(sym->curr.val, NULL, base); + val2 = sym_get_range_val(prop->expr->left.sym, base); + if (val >= val2) { + val2 = sym_get_range_val(prop->expr->right.sym, base); + if (val <= val2) + return; + } + if (sym->type == S_INT) + sprintf(str, "%d", val2); + else + sprintf(str, "0x%x", val2); + sym->curr.val = strdup(str); +} + +static void sym_calc_visibility(struct symbol *sym) +{ + struct property *prop; + tristate tri; + + /* any prompt visible? */ + tri = no; + for_all_prompts(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + tri = EXPR_OR(tri, prop->visible.tri); + } + if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) + tri = yes; + if (sym->visible != tri) { + sym->visible = tri; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym)) + return; + /* defaulting to "yes" if no explicit "depends on" are given */ + tri = yes; + if (sym->dir_dep.expr) + tri = expr_calc_value(sym->dir_dep.expr); + if (tri == mod) + tri = yes; + if (sym->dir_dep.tri != tri) { + sym->dir_dep.tri = tri; + sym_set_changed(sym); + } + tri = no; + if (sym->rev_dep.expr) + tri = expr_calc_value(sym->rev_dep.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->rev_dep.tri != tri) { + sym->rev_dep.tri = tri; + sym_set_changed(sym); + } +} + +/* + * Find the default symbol for a choice. + * First try the default values for the choice symbol + * Next locate the first visible choice value + * Return NULL if none was found + */ +struct symbol *sym_choice_default(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + + /* any of the defaults visible? */ + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri == no) + continue; + def_sym = prop_get_symbol(prop); + if (def_sym->visible != no) + return def_sym; + } + + /* just get the first visible value */ + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) + if (def_sym->visible != no) + return def_sym; + + /* failed to locate any defaults */ + return NULL; +} + +static struct symbol *sym_calc_choice(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + int flags; + + /* first calculate all choice values' visibilities */ + flags = sym->flags; + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + flags &= def_sym->flags; + } + + sym->flags &= flags | ~SYMBOL_DEF_USER; + + /* is the user choice visible? */ + def_sym = sym->def[S_DEF_USER].val; + if (def_sym && def_sym->visible != no) + return def_sym; + + def_sym = sym_choice_default(sym); + + if (def_sym == NULL) + /* no choice? reset tristate value */ + sym->curr.tri = no; + + return def_sym; +} + +void sym_calc_value(struct symbol *sym) +{ + struct symbol_value newval, oldval; + struct property *prop; + struct expr *e; + + if (!sym) + return; + + if (sym->flags & SYMBOL_VALID) + return; + sym->flags |= SYMBOL_VALID; + + oldval = sym->curr; + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + newval = symbol_empty.curr; + break; + case S_BOOLEAN: + case S_TRISTATE: + newval = symbol_no.curr; + break; + default: + sym->curr.val = sym->name; + sym->curr.tri = no; + return; + } + if (!sym_is_choice_value(sym)) + sym->flags &= ~SYMBOL_WRITE; + + sym_calc_visibility(sym); + + /* set default if recursively called */ + sym->curr = newval; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_is_choice_value(sym) && sym->visible == yes) { + prop = sym_get_choice_prop(sym); + newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + } else { + if (sym->visible != no) { + /* if the symbol is visible use the user value + * if available, otherwise try the default value + */ + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri, + sym->visible); + goto calc_newval; + } + } + if (sym->rev_dep.tri != no) + sym->flags |= SYMBOL_WRITE; + if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) { + sym->flags |= SYMBOL_WRITE; + newval.tri = EXPR_AND(expr_calc_value(prop->expr), + prop->visible.tri); + } + } + calc_newval: + if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) { + struct expr *e; + e = expr_simplify_unmet_dep(sym->rev_dep.expr, + sym->dir_dep.expr); + fprintf(stderr, "warning: ("); + expr_fprint(e, stderr); + fprintf(stderr, ") selects %s which has unmet direct dependencies (", + sym->name); + expr_fprint(sym->dir_dep.expr, stderr); + fprintf(stderr, ")\n"); + expr_free(e); + } + newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); + } + if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) + newval.tri = yes; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (sym->visible != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.val = sym->def[S_DEF_USER].val; + break; + } + } + prop = sym_get_default_prop(sym); + if (prop) { + struct symbol *ds = prop_get_symbol(prop); + if (ds) { + sym->flags |= SYMBOL_WRITE; + sym_calc_value(ds); + newval.val = ds->curr.val; + } + } + break; + default: + ; + } + + sym->curr = newval; + if (sym_is_choice(sym) && newval.tri == yes) + sym->curr.val = sym_calc_choice(sym); + sym_validate_range(sym); + + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { + sym_set_changed(sym); + if (modules_sym == sym) { + sym_set_all_changed(); + modules_val = modules_sym->curr.tri; + } + } + + if (sym_is_choice(sym)) { + struct symbol *choice_sym; + + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, choice_sym) { + if ((sym->flags & SYMBOL_WRITE) && + choice_sym->visible != no) + choice_sym->flags |= SYMBOL_WRITE; + if (sym->flags & SYMBOL_CHANGED) + sym_set_changed(choice_sym); + } + } + + if (sym->flags & SYMBOL_AUTO) + sym->flags &= ~SYMBOL_WRITE; +} + +void sym_clear_all_valid(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym->flags &= ~SYMBOL_VALID; + sym_add_change_count(1); + if (modules_sym) + sym_calc_value(modules_sym); +} + +void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + +bool sym_tristate_within_range(struct symbol *sym, tristate val) +{ + int type = sym_get_type(sym); + + if (sym->visible == no) + return false; + + if (type != S_BOOLEAN && type != S_TRISTATE) + return false; + + if (type == S_BOOLEAN && val == mod) + return false; + if (sym->visible <= sym->rev_dep.tri) + return false; + if (sym_is_choice_value(sym) && sym->visible == yes) + return val == yes; + return val >= sym->rev_dep.tri && val <= sym->visible; +} + +bool sym_set_tristate_value(struct symbol *sym, tristate val) +{ + tristate oldval = sym_get_tristate_value(sym); + + if (oldval != val && !sym_tristate_within_range(sym, val)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + /* + * setting a choice value also resets the new flag of the choice + * symbol and all other choice values. + */ + if (sym_is_choice_value(sym) && val == yes) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + struct property *prop; + struct expr *e; + + cs->def[S_DEF_USER].val = sym; + cs->flags |= SYMBOL_DEF_USER; + prop = sym_get_choice_prop(cs); + for (e = prop->expr; e; e = e->left.expr) { + if (e->right.sym->visible != no) + e->right.sym->flags |= SYMBOL_DEF_USER; + } + } + + sym->def[S_DEF_USER].tri = val; + if (oldval != val) + sym_clear_all_valid(); + + return true; +} + +tristate sym_toggle_tristate_value(struct symbol *sym) +{ + tristate oldval, newval; + + oldval = newval = sym_get_tristate_value(sym); + do { + switch (newval) { + case no: + newval = mod; + break; + case mod: + newval = yes; + break; + case yes: + newval = no; + break; + } + if (sym_set_tristate_value(sym, newval)) + break; + } while (oldval != newval); + return newval; +} + +bool sym_string_valid(struct symbol *sym, const char *str) +{ + signed char ch; + + switch (sym->type) { + case S_STRING: + return true; + case S_INT: + ch = *str++; + if (ch == '-') + ch = *str++; + if (!isdigit(ch)) + return false; + if (ch == '0' && *str != 0) + return false; + while ((ch = *str++)) { + if (!isdigit(ch)) + return false; + } + return true; + case S_HEX: + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + ch = *str++; + do { + if (!isxdigit(ch)) + return false; + } while ((ch = *str++)); + return true; + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + case 'm': case 'M': + case 'n': case 'N': + return true; + } + return false; + default: + return false; + } +} + +bool sym_string_within_range(struct symbol *sym, const char *str) +{ + struct property *prop; + int val; + + switch (sym->type) { + case S_STRING: + return sym_string_valid(sym, str); + case S_INT: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 10); + return val >= sym_get_range_val(prop->expr->left.sym, 10) && + val <= sym_get_range_val(prop->expr->right.sym, 10); + case S_HEX: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 16); + return val >= sym_get_range_val(prop->expr->left.sym, 16) && + val <= sym_get_range_val(prop->expr->right.sym, 16); + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + return sym_tristate_within_range(sym, yes); + case 'm': case 'M': + return sym_tristate_within_range(sym, mod); + case 'n': case 'N': + return sym_tristate_within_range(sym, no); + } + return false; + default: + return false; + } +} + +bool sym_set_string_value(struct symbol *sym, const char *newval) +{ + const char *oldval; + char *val; + int size; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (newval[0]) { + case 'y': case 'Y': + return sym_set_tristate_value(sym, yes); + case 'm': case 'M': + return sym_set_tristate_value(sym, mod); + case 'n': case 'N': + return sym_set_tristate_value(sym, no); + } + return false; + default: + ; + } + + if (!sym_string_within_range(sym, newval)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + + oldval = sym->def[S_DEF_USER].val; + size = strlen(newval) + 1; + if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { + size += 2; + sym->def[S_DEF_USER].val = val = malloc(size); + *val++ = '0'; + *val++ = 'x'; + } else if (!oldval || strcmp(oldval, newval)) + sym->def[S_DEF_USER].val = val = malloc(size); + else + return true; + + strcpy(val, newval); + free((void *)oldval); + sym_clear_all_valid(); + + return true; +} + +/* + * Find the default value associated to a symbol. + * For tristate symbol handle the modules=n case + * in which case "m" becomes "y". + * If the symbol does not have any default then fallback + * to the fixed default values. + */ +const char *sym_get_string_default(struct symbol *sym) +{ + struct property *prop; + struct symbol *ds; + const char *str; + tristate val; + + sym_calc_visibility(sym); + sym_calc_value(modules_sym); + val = symbol_no.curr.tri; + str = symbol_empty.curr.val; + + /* If symbol has a default value look it up */ + prop = sym_get_default_prop(sym); + if (prop != NULL) { + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + /* The visibility may limit the value from yes => mod */ + val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); + break; + default: + /* + * The following fails to handle the situation + * where a default value is further limited by + * the valid range. + */ + ds = prop_get_symbol(prop); + if (ds != NULL) { + sym_calc_value(ds); + str = (const char *)ds->curr.val; + } + } + } + + /* Handle select statements */ + val = EXPR_OR(val, sym->rev_dep.tri); + + /* transpose mod to yes if modules are not enabled */ + if (val == mod) + if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no) + val = yes; + + /* transpose mod to yes if type is bool */ + if (sym->type == S_BOOLEAN && val == mod) + val = yes; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (val) { + case no: return "n"; + case mod: return "m"; + case yes: return "y"; + } + case S_INT: + case S_HEX: + return str; + case S_STRING: + return str; + case S_OTHER: + case S_UNKNOWN: + break; + } + return ""; +} + +const char *sym_get_string_value(struct symbol *sym) +{ + tristate val; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + return "n"; + case mod: + sym_calc_value(modules_sym); + return (modules_sym->curr.tri == no) ? "n" : "m"; + case yes: + return "y"; + } + break; + default: + ; + } + return (const char *)sym->curr.val; +} + +bool sym_is_changable(struct symbol *sym) +{ + return sym->visible > sym->rev_dep.tri; +} + +static unsigned strhash(const char *s) +{ + /* fnv32 hash */ + unsigned hash = 2166136261U; + for (; *s; s++) + hash = (hash ^ *s) * 0x01000193; + return hash; +} + +struct symbol *sym_lookup(const char *name, int flags) +{ + struct symbol *symbol; + char *new_name; + int hash; + + if (name) { + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + hash = strhash(name) % SYMBOL_HASHSIZE; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (symbol->name && + !strcmp(symbol->name, name) && + (flags ? symbol->flags & flags + : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE)))) + return symbol; + } + new_name = strdup(name); + } else { + new_name = NULL; + hash = 0; + } + + symbol = malloc(sizeof(*symbol)); + memset(symbol, 0, sizeof(*symbol)); + symbol->name = new_name; + symbol->type = S_UNKNOWN; + symbol->flags |= flags; + + symbol->next = symbol_hash[hash]; + symbol_hash[hash] = symbol; + + return symbol; +} + +struct symbol *sym_find(const char *name) +{ + struct symbol *symbol = NULL; + int hash = 0; + + if (!name) + return NULL; + + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + hash = strhash(name) % SYMBOL_HASHSIZE; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (symbol->name && + !strcmp(symbol->name, name) && + !(symbol->flags & SYMBOL_CONST)) + break; + } + + return symbol; +} + +/* + * Expand symbol's names embedded in the string given in argument. Symbols' + * name to be expanded shall be prefixed by a '$'. Unknown symbol expands to + * the empty string. + */ +const char *sym_expand_string_value(const char *in) +{ + const char *src; + char *res; + size_t reslen; + + reslen = strlen(in) + 1; + res = malloc(reslen); + res[0] = '\0'; + + while ((src = strchr(in, '$'))) { + char *p, name[SYMBOL_MAXLENGTH]; + const char *symval = ""; + struct symbol *sym; + size_t newlen; + + strncat(res, in, src - in); + src++; + + p = name; + while (isalnum(*src) || *src == '_') + *p++ = *src++; + *p = '\0'; + + sym = sym_find(name); + if (sym != NULL) { + sym_calc_value(sym); + symval = sym_get_string_value(sym); + } + + newlen = strlen(res) + strlen(symval) + strlen(src) + 1; + if (newlen > reslen) { + reslen = newlen; + res = realloc(res, reslen); + } + + strcat(res, symval); + in = src; + } + strcat(res, in); + + return res; +} + +const char *sym_escape_string_value(const char *in) +{ + const char *p; + size_t reslen; + char *res; + size_t l; + + reslen = strlen(in) + strlen("\"\"") + 1; + + p = in; + for (;;) { + l = strcspn(p, "\"\\"); + p += l; + + if (p[0] == '\0') + break; + + reslen++; + p++; + } + + res = malloc(reslen); + res[0] = '\0'; + + strcat(res, "\""); + + p = in; + for (;;) { + l = strcspn(p, "\"\\"); + strncat(res, p, l); + p += l; + + if (p[0] == '\0') + break; + + strcat(res, "\\"); + strncat(res, p++, 1); + } + + strcat(res, "\""); + return res; +} + +struct symbol **sym_re_search(const char *pattern) +{ + struct symbol *sym, **sym_arr = NULL; + int i, cnt, size; + regex_t re; + + cnt = size = 0; + /* Skip if empty */ + if (strlen(pattern) == 0) + return NULL; + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE)) + return NULL; + + for_all_symbols(i, sym) { + if (sym->flags & SYMBOL_CONST || !sym->name) + continue; + if (regexec(&re, sym->name, 0, NULL, 0)) + continue; + if (cnt + 1 >= size) { + void *tmp = sym_arr; + size += 16; + sym_arr = realloc(sym_arr, size * sizeof(struct symbol *)); + if (!sym_arr) { + free(tmp); + return NULL; + } + } + sym_calc_value(sym); + sym_arr[cnt++] = sym; + } + if (sym_arr) + sym_arr[cnt] = NULL; + regfree(&re); + + return sym_arr; +} + +/* + * When we check for recursive dependencies we use a stack to save + * current state so we can print out relevant info to user. + * The entries are located on the call stack so no need to free memory. + * Note inser() remove() must always match to properly clear the stack. + */ +static struct dep_stack { + struct dep_stack *prev, *next; + struct symbol *sym; + struct property *prop; + struct expr *expr; +} *check_top; + +static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym) +{ + memset(stack, 0, sizeof(*stack)); + if (check_top) + check_top->next = stack; + stack->prev = check_top; + stack->sym = sym; + check_top = stack; +} + +static void dep_stack_remove(void) +{ + check_top = check_top->prev; + if (check_top) + check_top->next = NULL; +} + +/* + * Called when we have detected a recursive dependency. + * check_top point to the top of the stact so we use + * the ->prev pointer to locate the bottom of the stack. + */ +static void sym_check_print_recursive(struct symbol *last_sym) +{ + struct dep_stack *stack; + struct symbol *sym, *next_sym; + struct menu *menu = NULL; + struct property *prop; + struct dep_stack cv_stack; + + if (sym_is_choice_value(last_sym)) { + dep_stack_insert(&cv_stack, last_sym); + last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); + } + + for (stack = check_top; stack != NULL; stack = stack->prev) + if (stack->sym == last_sym) + break; + if (!stack) { + fprintf(stderr, "unexpected recursive dependency error\n"); + return; + } + + for (; stack; stack = stack->next) { + sym = stack->sym; + next_sym = stack->next ? stack->next->sym : last_sym; + prop = stack->prop; + if (prop == NULL) + prop = stack->sym->prop; + + /* for choice values find the menu entry (used below) */ + if (sym_is_choice(sym) || sym_is_choice_value(sym)) { + for (prop = sym->prop; prop; prop = prop->next) { + menu = prop->menu; + if (prop->menu) + break; + } + } + if (stack->sym == last_sym) + fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", + prop->file->name, prop->lineno); + if (stack->expr) { + fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "<choice>", + prop_get_type_name(prop->type), + next_sym->name ? next_sym->name : "<choice>"); + } else if (stack->prop) { + fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } else if (sym_is_choice(sym)) { + fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", + menu->file->name, menu->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } else if (sym_is_choice_value(sym)) { + fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", + menu->file->name, menu->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } else { + fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } + } + + if (check_top == &cv_stack) + dep_stack_remove(); +} + +static struct symbol *sym_check_expr_deps(struct expr *e) +{ + struct symbol *sym; + + if (!e) + return NULL; + switch (e->type) { + case E_OR: + case E_AND: + sym = sym_check_expr_deps(e->left.expr); + if (sym) + return sym; + return sym_check_expr_deps(e->right.expr); + case E_NOT: + return sym_check_expr_deps(e->left.expr); + case E_EQUAL: + case E_UNEQUAL: + sym = sym_check_deps(e->left.sym); + if (sym) + return sym; + return sym_check_deps(e->right.sym); + case E_SYMBOL: + return sym_check_deps(e->left.sym); + default: + break; + } + printf("Oops! How to check %d?\n", e->type); + return NULL; +} + +/* return NULL when dependencies are OK */ +static struct symbol *sym_check_sym_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + struct dep_stack stack; + + dep_stack_insert(&stack, sym); + + sym2 = sym_check_expr_deps(sym->rev_dep.expr); + if (sym2) + goto out; + + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->type == P_CHOICE || prop->type == P_SELECT) + continue; + stack.prop = prop; + sym2 = sym_check_expr_deps(prop->visible.expr); + if (sym2) + break; + if (prop->type != P_DEFAULT || sym_is_choice(sym)) + continue; + stack.expr = prop->expr; + sym2 = sym_check_expr_deps(prop->expr); + if (sym2) + break; + stack.expr = NULL; + } + +out: + dep_stack_remove(); + + return sym2; +} + +static struct symbol *sym_check_choice_deps(struct symbol *choice) +{ + struct symbol *sym, *sym2; + struct property *prop; + struct expr *e; + struct dep_stack stack; + + dep_stack_insert(&stack, choice); + + prop = sym_get_choice_prop(choice); + expr_list_for_each_sym(prop->expr, e, sym) + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + + choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_sym_deps(choice); + choice->flags &= ~SYMBOL_CHECK; + if (sym2) + goto out; + + expr_list_for_each_sym(prop->expr, e, sym) { + sym2 = sym_check_sym_deps(sym); + if (sym2) + break; + } +out: + expr_list_for_each_sym(prop->expr, e, sym) + sym->flags &= ~SYMBOL_CHECK; + + if (sym2 && sym_is_choice_value(sym2) && + prop_get_symbol(sym_get_choice_prop(sym2)) == choice) + sym2 = choice; + + dep_stack_remove(); + + return sym2; +} + +struct symbol *sym_check_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + + if (sym->flags & SYMBOL_CHECK) { + sym_check_print_recursive(sym); + return sym; + } + if (sym->flags & SYMBOL_CHECKED) + return NULL; + + if (sym_is_choice_value(sym)) { + struct dep_stack stack; + + /* for choice groups start the check with main choice symbol */ + dep_stack_insert(&stack, sym); + prop = sym_get_choice_prop(sym); + sym2 = sym_check_deps(prop_get_symbol(prop)); + dep_stack_remove(); + } else if (sym_is_choice(sym)) { + sym2 = sym_check_choice_deps(sym); + } else { + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_sym_deps(sym); + sym->flags &= ~SYMBOL_CHECK; + } + + if (sym2 && sym2 == sym) + sym2 = NULL; + + return sym2; +} + +struct property *prop_alloc(enum prop_type type, struct symbol *sym) +{ + struct property *prop; + struct property **propp; + + prop = malloc(sizeof(*prop)); + memset(prop, 0, sizeof(*prop)); + prop->type = type; + prop->sym = sym; + prop->file = current_file; + prop->lineno = zconf_lineno(); + + /* append property to the prop list of symbol */ + if (sym) { + for (propp = &sym->prop; *propp; propp = &(*propp)->next) + ; + *propp = prop; + } + + return prop; +} + +struct symbol *prop_get_symbol(struct property *prop) +{ + if (prop->expr && (prop->expr->type == E_SYMBOL || + prop->expr->type == E_LIST)) + return prop->expr->left.sym; + return NULL; +} + +const char *prop_get_type_name(enum prop_type type) +{ + switch (type) { + case P_PROMPT: + return "prompt"; + case P_ENV: + return "env"; + case P_COMMENT: + return "comment"; + case P_MENU: + return "menu"; + case P_DEFAULT: + return "default"; + case P_CHOICE: + return "choice"; + case P_SELECT: + return "select"; + case P_RANGE: + return "range"; + case P_SYMBOL: + return "symbol"; + case P_UNKNOWN: + break; + } + return "unknown"; +} + +static void prop_add_env(const char *env) +{ + struct symbol *sym, *sym2; + struct property *prop; + char *p; + + sym = current_entry->sym; + sym->flags |= SYMBOL_AUTO; + for_all_properties(sym, prop, P_ENV) { + sym2 = prop_get_symbol(prop); + if (strcmp(sym2->name, env)) + menu_warn(current_entry, "redefining environment symbol from %s", + sym2->name); + return; + } + + prop = prop_alloc(P_ENV, sym); + prop->expr = expr_alloc_symbol(sym_lookup(env, SYMBOL_CONST)); + + sym_env_list = expr_alloc_one(E_LIST, sym_env_list); + sym_env_list->right.sym = sym; + + p = getenv(env); + if (p) + sym_add_default(sym, p); + else + menu_warn(current_entry, "environment variable %s undefined", env); +} diff --git a/carl9170fw/config/util.c b/carl9170fw/config/util.c new file mode 100644 index 0000000..d0b8b23 --- /dev/null +++ b/carl9170fw/config/util.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org> + * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org> + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include "lkc.h" + +/* file already present in list? If not add it */ +struct file *file_lookup(const char *name) +{ + struct file *file; + const char *file_name = sym_expand_string_value(name); + + for (file = file_list; file; file = file->next) { + if (!strcmp(name, file->name)) { + free((void *)file_name); + return file; + } + } + + file = malloc(sizeof(*file)); + memset(file, 0, sizeof(*file)); + file->name = file_name; + file->next = file_list; + file_list = file; + return file; +} + +/* write a dependency file as used by kbuild to track dependencies */ +int file_write_dep(const char *name) +{ + struct symbol *sym, *env_sym; + struct expr *e; + struct file *file; + FILE *out; + + if (!name) + name = ".kconfig.d"; + out = fopen("..config.tmp", "w"); + if (!out) + return 1; + fprintf(out, "deps_config := \\\n"); + for (file = file_list; file; file = file->next) { + if (file->next) + fprintf(out, "\t%s \\\n", file->name); + else + fprintf(out, "\t%s\n", file->name); + } + fprintf(out, "\n%s: \\\n" + "\t$(deps_config)\n\n", conf_get_autoconfig_name()); + + expr_list_for_each_sym(sym_env_list, e, sym) { + struct property *prop; + const char *value; + + prop = sym_get_env_prop(sym); + env_sym = prop_get_symbol(prop); + if (!env_sym) + continue; + value = getenv(env_sym->name); + if (!value) + value = ""; + fprintf(out, "ifneq \"$(%s)\" \"%s\"\n", env_sym->name, value); + fprintf(out, "%s: FORCE\n", conf_get_autoconfig_name()); + fprintf(out, "endif\n"); + } + + fprintf(out, "\n$(deps_config): ;\n"); + fclose(out); + rename("..config.tmp", name); + return 0; +} + + +/* Allocate initial growable string */ +struct gstr str_new(void) +{ + struct gstr gs; + gs.s = malloc(sizeof(char) * 64); + gs.len = 64; + gs.max_width = 0; + strcpy(gs.s, "\0"); + return gs; +} + +/* Allocate and assign growable string */ +struct gstr str_assign(const char *s) +{ + struct gstr gs; + gs.s = strdup(s); + gs.len = strlen(s) + 1; + gs.max_width = 0; + return gs; +} + +/* Free storage for growable string */ +void str_free(struct gstr *gs) +{ + if (gs->s) + free(gs->s); + gs->s = NULL; + gs->len = 0; +} + +/* Append to growable string */ +void str_append(struct gstr *gs, const char *s) +{ + size_t l; + if (s) { + l = strlen(gs->s) + strlen(s) + 1; + if (l > gs->len) { + gs->s = realloc(gs->s, l); + gs->len = l; + } + strcat(gs->s, s); + } +} + +/* Append printf formatted string to growable string */ +void str_printf(struct gstr *gs, const char *fmt, ...) +{ + va_list ap; + char s[10000]; /* big enough... */ + va_start(ap, fmt); + vsnprintf(s, sizeof(s), fmt, ap); + str_append(gs, s); + va_end(ap); +} + +/* Retrieve value of growable string */ +const char *str_get(struct gstr *gs) +{ + return gs->s; +} + diff --git a/carl9170fw/config/zconf.gperf b/carl9170fw/config/zconf.gperf new file mode 100644 index 0000000..dd8ec26 --- /dev/null +++ b/carl9170fw/config/zconf.gperf @@ -0,0 +1,47 @@ +%language=ANSI-C +%define hash-function-name kconf_id_hash +%define lookup-function-name kconf_id_lookup +%define string-pool-name kconf_id_strings +%compare-strncmp +%enum +%pic +%struct-type + +struct kconf_id; + +struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len); + +%% +mainmenu, T_MAINMENU, TF_COMMAND +menu, T_MENU, TF_COMMAND +endmenu, T_ENDMENU, TF_COMMAND +source, T_SOURCE, TF_COMMAND +choice, T_CHOICE, TF_COMMAND +endchoice, T_ENDCHOICE, TF_COMMAND +comment, T_COMMENT, TF_COMMAND +config, T_CONFIG, TF_COMMAND +menuconfig, T_MENUCONFIG, TF_COMMAND +help, T_HELP, TF_COMMAND +if, T_IF, TF_COMMAND|TF_PARAM +endif, T_ENDIF, TF_COMMAND +depends, T_DEPENDS, TF_COMMAND +optional, T_OPTIONAL, TF_COMMAND +default, T_DEFAULT, TF_COMMAND, S_UNKNOWN +prompt, T_PROMPT, TF_COMMAND +tristate, T_TYPE, TF_COMMAND, S_TRISTATE +def_tristate, T_DEFAULT, TF_COMMAND, S_TRISTATE +bool, T_TYPE, TF_COMMAND, S_BOOLEAN +boolean, T_TYPE, TF_COMMAND, S_BOOLEAN +def_bool, T_DEFAULT, TF_COMMAND, S_BOOLEAN +int, T_TYPE, TF_COMMAND, S_INT +hex, T_TYPE, TF_COMMAND, S_HEX +string, T_TYPE, TF_COMMAND, S_STRING +select, T_SELECT, TF_COMMAND +range, T_RANGE, TF_COMMAND +visible, T_VISIBLE, TF_COMMAND +option, T_OPTION, TF_COMMAND +on, T_ON, TF_PARAM +modules, T_OPT_MODULES, TF_OPTION +defconfig_list, T_OPT_DEFCONFIG_LIST,TF_OPTION +env, T_OPT_ENV, TF_OPTION +%% diff --git a/carl9170fw/config/zconf.l b/carl9170fw/config/zconf.l new file mode 100644 index 0000000..a2b2895 --- /dev/null +++ b/carl9170fw/config/zconf.l @@ -0,0 +1,364 @@ +%option nostdinit noyywrap never-interactive full ecs +%option 8bit nodefault perf-report perf-report +%option noinput +%x COMMAND HELP STRING PARAM +%{ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +static void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +static void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +static void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} +%} + +ws [ \n\t] +n [A-Za-z0-9_] + +%% + int str = 0; + int ts, i; + +[ \t]*#.*\n | +[ \t]*\n { + current_file->lineno++; + return T_EOL; +} +[ \t]*#.* + + +[ \t]+ { + BEGIN(COMMAND); +} + +. { + unput(yytext[0]); + BEGIN(COMMAND); +} + + +<COMMAND>{ + {n}+ { + const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + . + \n { + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } +} + +<PARAM>{ + "&&" return T_AND; + "||" return T_OR; + "(" return T_OPEN_PAREN; + ")" return T_CLOSE_PAREN; + "!" return T_NOT; + "=" return T_EQUAL; + "!=" return T_UNEQUAL; + \"|\' { + str = yytext[0]; + new_string(); + BEGIN(STRING); + } + \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + --- /* ignore */ + ({n}|[-/.])+ { + const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + #.* /* comment */ + \\\n current_file->lineno++; + . + <<EOF>> { + BEGIN(INITIAL); + } +} + +<STRING>{ + [^'"\\\n]+/\n { + append_string(yytext, yyleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + [^'"\\\n]+ { + append_string(yytext, yyleng); + } + \\.?/\n { + append_string(yytext + 1, yyleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + \\.? { + append_string(yytext + 1, yyleng - 1); + } + \'|\" { + if (str == yytext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(yytext, 1); + } + \n { + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <<EOF>> { + BEGIN(INITIAL); + } +} + +<HELP>{ + [ \t]+ { + ts = 0; + for (i = 0; i < yyleng; i++) { + if (yytext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + [ \t]*\n/[^ \t\n] { + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + [ \t]*\n { + current_file->lineno++; + append_string("\n", 1); + } + [^ \t\n].* { + while (yyleng) { + if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) + break; + yyleng--; + } + append_string(yytext, yyleng); + if (!first_ts) + first_ts = last_ts; + } + <<EOF>> { + zconf_endhelp(); + return T_HELPTEXT; + } +} + +<<EOF>> { + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(yyin); + yyterminate(); +} + +%% +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the firmware. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + yyin = zconf_fopen(name); + if (!yyin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; +} + +void zconf_nextfile(const char *name) +{ + struct file *iter; + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + yyin = zconf_fopen(file->name); + if (!yyin) { + printf("%s:%d: can't open file \"%s\"\n", + zconf_curname(), zconf_lineno(), file->name); + exit(1); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + for (iter = current_file->parent; iter; iter = iter->parent ) { + if (!strcmp(current_file->name,iter->name) ) { + printf("%s:%d: recursive inclusion detected. " + "Inclusion path:\n current file : '%s'\n", + zconf_curname(), zconf_lineno(), + zconf_curname()); + iter = current_file->parent; + while (iter && \ + strcmp(iter->name,current_file->name)) { + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno-1); + iter = iter->parent; + } + if (iter) + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno+1); + exit(1); + } + } + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +const char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : "<none>"; +} diff --git a/carl9170fw/config/zconf.y b/carl9170fw/config/zconf.y new file mode 100644 index 0000000..b957a7c --- /dev/null +++ b/carl9170fw/config/zconf.y @@ -0,0 +1,740 @@ +%{ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include "lkc.h" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[SYMBOL_HASHSIZE]; + +static struct menu *current_menu, *current_entry; + +%} +%expect 30 + +%union +{ + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + const struct kconf_id *id; +} + +%token <id>T_MAINMENU +%token <id>T_MENU +%token <id>T_ENDMENU +%token <id>T_SOURCE +%token <id>T_CHOICE +%token <id>T_ENDCHOICE +%token <id>T_COMMENT +%token <id>T_CONFIG +%token <id>T_MENUCONFIG +%token <id>T_HELP +%token <string> T_HELPTEXT +%token <id>T_IF +%token <id>T_ENDIF +%token <id>T_DEPENDS +%token <id>T_OPTIONAL +%token <id>T_PROMPT +%token <id>T_TYPE +%token <id>T_DEFAULT +%token <id>T_SELECT +%token <id>T_RANGE +%token <id>T_VISIBLE +%token <id>T_OPTION +%token <id>T_ON +%token <string> T_WORD +%token <string> T_WORD_QUOTE +%token T_UNEQUAL +%token T_CLOSE_PAREN +%token T_OPEN_PAREN +%token T_EOL + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%nonassoc T_NOT + +%type <string> prompt +%type <symbol> symbol +%type <expr> expr +%type <expr> if_expr +%type <id> end +%type <id> option_name +%type <menu> if_entry menu_entry choice_entry +%type <string> symbol_option_arg word_opt + +%destructor { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + $$->file->name, $$->lineno); + if (current_menu == $$) + menu_end_menu(); +} if_entry menu_entry choice_entry + +%{ +/* Include zconf.hash.c here so it can see the token constants. */ +#include "zconf.hash.c" +%} + +%% +input: nl start | start; + +start: mainmenu_stmt stmt_list | stmt_list; + +stmt_list: + /* empty */ + | stmt_list common_stmt + | stmt_list choice_stmt + | stmt_list menu_stmt + | stmt_list end { zconf_error("unexpected end statement"); } + | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } + | stmt_list option_name error T_EOL +{ + zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name); +} + | stmt_list error T_EOL { zconf_error("invalid statement"); } +; + +option_name: + T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT | T_VISIBLE +; + +common_stmt: + T_EOL + | if_stmt + | comment_stmt + | config_stmt + | menuconfig_stmt + | source_stmt +; + +option_error: + T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); } + | error T_EOL { zconf_error("invalid option"); } +; + + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +config_stmt: config_entry_start config_option_list +{ + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list symbol_option + | config_option_list depends + | config_option_list help + | config_option_list option_error + | config_option_list T_EOL +; + +config_option: T_TYPE prompt_stmt_opt T_EOL +{ + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEFAULT expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + if ($1->stype != S_UNKNOWN) + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_SELECT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +}; + +symbol_option: T_OPTION symbol_option_list T_EOL +; + +symbol_option_list: + /* empty */ + | symbol_option_list T_WORD symbol_option_arg +{ + const struct kconf_id *id = kconf_id_lookup($2, strlen($2)); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, $3); + else + zconfprint("warning: ignoring unknown option %s", $2); + free($2); +}; + +symbol_option_arg: + /* empty */ { $$ = NULL; } + | T_EQUAL prompt { $$ = $2; } +; + +/* choice entry */ + +choice: T_CHOICE word_opt T_EOL +{ + struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE); + sym->flags |= SYMBOL_AUTO; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +}; + +choice_entry: choice choice_option_list +{ + $$ = menu_add_menu(); +}; + +choice_end: end +{ + if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +}; + +choice_stmt: choice_entry choice_block choice_end +; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help + | choice_option_list T_EOL + | choice_option_list option_error +; + +choice_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_TYPE prompt_stmt_opt T_EOL +{ + if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) { + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); + } else + YYERROR; +}; + +choice_option: T_OPTIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_DEFAULT T_WORD if_expr T_EOL +{ + if ($1->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +}; + +choice_block: + /* empty */ + | choice_block common_stmt +; + +/* if entry */ + +if_entry: T_IF expr nl +{ + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep($2); + $$ = menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +}; + +if_stmt: if_entry if_block if_end +; + +if_block: + /* empty */ + | if_block common_stmt + | if_block menu_stmt + | if_block choice_stmt +; + +/* mainmenu entry */ + +mainmenu_stmt: T_MAINMENU prompt nl +{ + menu_add_prompt(P_MENU, $2, NULL); +}; + +/* menu entry */ + +menu: T_MENU prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_MENU, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +}; + +menu_entry: menu visibility_list depends_list +{ + $$ = menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +}; + +menu_stmt: menu_entry menu_block menu_end +; + +menu_block: + /* empty */ + | menu_block common_stmt + | menu_block menu_stmt + | menu_block choice_stmt +; + +source_stmt: T_SOURCE prompt T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); + zconf_nextfile($2); +}; + +/* comment entry */ + +comment: T_COMMENT prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +}; + +comment_stmt: comment depends_list +{ + menu_end_entry(); +}; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + current_entry->help = $2; +}; + +/* depends option */ + +depends_list: + /* empty */ + | depends_list depends + | depends_list T_EOL + | depends_list option_error +; + +depends: T_DEPENDS T_ON expr T_EOL +{ + menu_add_dep($3); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +}; + +/* visibility option */ + +visibility_list: + /* empty */ + | visibility_list visible + | visibility_list T_EOL +; + +visible: T_VISIBLE if_expr +{ + menu_add_visibility($2); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | prompt if_expr +{ + menu_add_prompt(P_PROMPT, $1, $2); +}; + +prompt: T_WORD + | T_WORD_QUOTE +; + +end: T_ENDMENU T_EOL { $$ = $1; } + | T_ENDCHOICE T_EOL { $$ = $1; } + | T_ENDIF T_EOL { $$ = $1; } +; + +nl: + T_EOL + | nl T_EOL +; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } + | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } +; + +word_opt: /* empty */ { $$ = NULL; } + | T_WORD + +%% + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + _menu_init(); + modules_sym = sym_lookup(NULL, 0); + modules_sym->type = S_BOOLEAN; + modules_sym->flags |= SYMBOL_AUTO; + rootmenu.prompt = menu_add_prompt(P_MENU, "CARL9170 Firmware Configuration", NULL); + + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym->prop) { + struct property *prop; + + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(sym_lookup("MODULES", 0)); + } + + rootmenu.prompt->text = _(rootmenu.prompt->text); + rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text); + + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +static const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + case T_VISIBLE: return "visible"; + } + return "<token>"; +} + +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +static void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +static void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "\nchoice\n"); + else + fprintf(out, "\nconfig %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + case P_SELECT: + fputs( " select ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_RANGE: + fputs( " range ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_MENU: + fputs( " menu ", out); + print_quoted_string(out, prop->text); + fputc('\n', out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "zconf.lex.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" diff --git a/carl9170fw/extra/FindGPERF.cmake b/carl9170fw/extra/FindGPERF.cmake new file mode 100644 index 0000000..baf8749 --- /dev/null +++ b/carl9170fw/extra/FindGPERF.cmake @@ -0,0 +1,160 @@ +# - Find gperf executable and provides a macro to generate custom build rules +# +# The module defines the following variables: +# GPERF_FOUND - true is gperf executable is found +# GPERF_EXECUTABLE - the path to the gperf executable +# GPERF_VERSION - the version of gperf +# GPERF_LIBRARIES - The gperf libraries +# +# The minimum required version of gperf can be specified using the +# standard syntax, e.g. FIND_PACKAGE(GPERF 2.5.13) +# +# +# If gperf is found on the system, the module provides the macro: +# GPERF_TARGET(Name GperfInput GperfOutput [COMPILE_FLAGS <string>]) +# which creates a custom command to generate the <GperfOutput> file from +# the <GperfInput> file. If COMPILE_FLAGS option is specified, the next +# parameter is added to the gperf command line. Name is an alias used to +# get details of this custom command. Indeed the macro defines the +# following variables: +# GPERF_${Name}_DEFINED - true is the macro ran successfully +# GPERF_${Name}_OUTPUTS - the source file generated by the custom rule, an +# alias for GperfOutput +# GPERF_${Name}_INPUT - the gperf source file, an alias for ${GperfInput} +# +# Gperf scanners oftenly use tokens defined by Bison: the code generated +# by Gperf depends of the header generated by Bison. This module also +# defines a macro: +# ADD_GPERF_BISON_DEPENDENCY(GperfTarget BisonTarget) +# which adds the required dependency between a scanner and a parser +# where <GperfTarget> and <BisonTarget> are the first parameters of +# respectively GPERF_TARGET and BISON_TARGET macros. +# +# ==================================================================== +# Example: +# +# find_package(GPERF) +# +# GPERF_TARGET(MyHash hash.gperf ${CMAKE_CURRENT_BINARY_DIR}/hash.c) +# +# ==================================================================== + +#============================================================================= +# Copyright 2009 Kitware, Inc. +# Copyright 2006 Tristan Carel +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 2.8.4) + +FIND_PROGRAM(GPERF_EXECUTABLE gperf DOC "path to the gperf executable") +MARK_AS_ADVANCED(GPERF_EXECUTABLE) + +FIND_LIBRARY(FL_LIBRARY NAMES fl + DOC "path to the fl library") +MARK_AS_ADVANCED(FL_LIBRARY) +SET(GPERF_LIBRARIES ${FL_LIBRARY}) + +IF(GPERF_EXECUTABLE) + + EXECUTE_PROCESS(COMMAND ${GPERF_EXECUTABLE} --version + OUTPUT_VARIABLE GPERF_version_output + ERROR_VARIABLE GPERF_version_error + RESULT_VARIABLE GPERF_version_result + OUTPUT_STRIP_TRAILING_WHITESPACE) + + SET(ENV{LC_ALL} ${_Bison_SAVED_LC_ALL}) + + IF(NOT ${GPERF_version_result} EQUAL 0) + MESSAGE(SEND_ERROR "Command \"${GPERF_EXECUTABLE} --version\" failed with output:\n${GPERF_version_error}") + ELSE() + STRING(REGEX REPLACE "^GNU gperf ([^\n]+)\n.*" "\\1" + GPERF_VERSION "${GPERF_version_output}") + ENDIF() + + #============================================================ + # GPERF_TARGET (public macro) + #============================================================ + # + MACRO(GPERF_TARGET Name Input Output) + SET(GPERF_TARGET_usage "GPERF_TARGET(<Name> <Input> <Output> [COMPILE_FLAGS <string>]") + IF(${ARGC} GREATER 3) + IF(${ARGC} EQUAL 5) + IF("${ARGV3}" STREQUAL "COMPILE_FLAGS") + SET(GPERF_EXECUTABLE_opts "${ARGV4}") + SEPARATE_ARGUMENTS(GPERF_EXECUTABLE_opts) + ELSE() + MESSAGE(SEND_ERROR ${GPERF_TARGET_usage}) + ENDIF() + ELSE() + MESSAGE(SEND_ERROR ${GPERF_TARGET_usage}) + ENDIF() + ENDIF() + + ADD_CUSTOM_COMMAND(OUTPUT ${Output} + COMMAND ${GPERF_EXECUTABLE} + ARGS ${GPERF_EXECUTABLE_opts} < ${Input} > ${Output} + DEPENDS ${Input} + COMMENT "[GPERF][${Name}] Building hash with gperf ${GPERF_VERSION}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + + SET(GPERF_${Name}_DEFINED TRUE) + SET(GPERF_${Name}_OUTPUTS ${Output}) + SET(GPERF_${Name}_INPUT ${Input}) + SET(GPERF_${Name}_COMPILE_FLAGS ${GPERF_EXECUTABLE_opts}) + ENDMACRO(GPERF_TARGET) + #============================================================ + + + #============================================================ + # ADD_GPERF_BISON_DEPENDENCY (public macro) + #============================================================ + # + MACRO(ADD_GPERF_BISON_DEPENDENCY GperfTarget BisonTarget) + + IF(NOT GPERF_${GperfTarget}_OUTPUTS) + MESSAGE(SEND_ERROR "Gperf target `${GperfTarget}' does not exists.") + ENDIF() + + IF(NOT BISON_${BisonTarget}_OUTPUT_HEADER) + MESSAGE(SEND_ERROR "Bison target `${BisonTarget}' does not exists.") + ENDIF() + + SET_SOURCE_FILES_PROPERTIES(${GPERF_${GperfTarget}_OUTPUTS} + PROPERTIES OBJECT_DEPENDS ${BISON_${BisonTarget}_OUTPUT_HEADER}) + ENDMACRO(ADD_GPERF_BISON_DEPENDENCY) + #============================================================ + +ENDIF(GPERF_EXECUTABLE) + +INCLUDE(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GPERF REQUIRED_VARS GPERF_EXECUTABLE + VERSION_VAR GPERF_VERSION) + +# FindGPERF.cmake ends here diff --git a/carl9170fw/extra/FindPackageHandleStandardArgs.cmake b/carl9170fw/extra/FindPackageHandleStandardArgs.cmake new file mode 100644 index 0000000..ec3440c --- /dev/null +++ b/carl9170fw/extra/FindPackageHandleStandardArgs.cmake @@ -0,0 +1,278 @@ +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> ... ) +# +# This function is intended to be used in FindXXX.cmake modules files. +# It handles the REQUIRED, QUIET and version-related arguments to FIND_PACKAGE(). +# It also sets the <UPPERCASED_NAME>_FOUND variable. +# The package is considered found if all variables <var1>... listed contain +# valid results, e.g. valid filepaths. +# +# There are two modes of this function. The first argument in both modes is +# the name of the Find-module where it is called (in original casing). +# +# The first simple mode looks like this: +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> (DEFAULT_MSG|"Custom failure message") <var1>...<varN> ) +# If the variables <var1> to <varN> are all valid, then <UPPERCASED_NAME>_FOUND +# will be set to TRUE. +# If DEFAULT_MSG is given as second argument, then the function will generate +# itself useful success and error messages. You can also supply a custom error message +# for the failure case. This is not recommended. +# +# The second mode is more powerful and also supports version checking: +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME [REQUIRED_VARS <var1>...<varN>] +# [VERSION_VAR <versionvar> +# [CONFIG_MODE] +# [FAIL_MESSAGE "Custom failure message"] ) +# +# As above, if <var1> through <varN> are all valid, <UPPERCASED_NAME>_FOUND +# will be set to TRUE. +# After REQUIRED_VARS the variables which are required for this package are listed. +# Following VERSION_VAR the name of the variable can be specified which holds +# the version of the package which has been found. If this is done, this version +# will be checked against the (potentially) specified required version used +# in the find_package() call. The EXACT keyword is also handled. The default +# messages include information about the required version and the version +# which has been actually found, both if the version is ok or not. +# Use the option CONFIG_MODE if your FindXXX.cmake module is a wrapper for +# a find_package(... NO_MODULE) call, in this case all the information +# provided by the config-mode of find_package() will be evaluated +# automatically. +# Via FAIL_MESSAGE a custom failure message can be specified, if this is not +# used, the default message will be displayed. +# +# Example for mode 1: +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) +# +# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and +# LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to TRUE. +# If it is not found and REQUIRED was used, it fails with FATAL_ERROR, +# independent whether QUIET was used or not. +# If it is found, success will be reported, including the content of <var1>. +# On repeated Cmake runs, the same message won't be printed again. +# +# Example for mode 2: +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE +# VERSION_VAR BISON_VERSION) +# In this case, BISON is considered to be found if the variable(s) listed +# after REQUIRED_VAR are all valid, i.e. BISON_EXECUTABLE in this case. +# Also the version of BISON will be checked by using the version contained +# in BISON_VERSION. +# Since no FAIL_MESSAGE is given, the default messages will be printed. +# +# Another example for mode 2: +# +# FIND_PACKAGE(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(Automoc4 CONFIG_MODE) +# In this case, FindAutmoc4.cmake wraps a call to FIND_PACKAGE(Automoc4 NO_MODULE) +# and adds an additional search directory for automoc4. +# The following FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper +# success/error message. + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +INCLUDE(FindPackageMessage) +INCLUDE(CMakeParseArguments) + +# internal helper macro +MACRO(_FPHSA_FAILURE_MESSAGE _msg) + IF (${_NAME}_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "${_msg}") + ELSE (${_NAME}_FIND_REQUIRED) + IF (NOT ${_NAME}_FIND_QUIETLY) + MESSAGE(STATUS "${_msg}") + ENDIF (NOT ${_NAME}_FIND_QUIETLY) + ENDIF (${_NAME}_FIND_REQUIRED) +ENDMACRO(_FPHSA_FAILURE_MESSAGE _msg) + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +MACRO(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # <name>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + IF(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + ELSE(${_NAME}_CONFIG) + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + IF(${_NAME}_CONSIDERED_CONFIGS) + SET(configsText "") + LIST(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + MATH(EXPR configsCount "${configsCount} - 1") + FOREACH(currentConfigIndex RANGE ${configsCount}) + LIST(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + LIST(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + SET(configsText "${configsText} ${filename} (version ${version})\n") + ENDFOREACH(currentConfigIndex) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") + + ELSE(${_NAME}_CONSIDERED_CONFIGS) + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + ENDIF(${_NAME}_CONSIDERED_CONFIGS) + ENDIF(${_NAME}_CONFIG) +ENDMACRO(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + + +FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) + +# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in +# new extended or in the "old" mode: + SET(options CONFIG_MODE) + SET(oneValueArgs FAIL_MESSAGE VERSION_VAR) + SET(multiValueArgs REQUIRED_VARS) + SET(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + LIST(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + IF(${INDEX} EQUAL -1) + SET(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + SET(FPHSA_REQUIRED_VARS ${ARGN}) + SET(FPHSA_VERSION_VAR) + ELSE(${INDEX} EQUAL -1) + + CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + IF(FPHSA_UNPARSED_ARGUMENTS) + MESSAGE(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + ENDIF(FPHSA_UNPARSED_ARGUMENTS) + + IF(NOT FPHSA_FAIL_MESSAGE) + SET(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + ENDIF(NOT FPHSA_FAIL_MESSAGE) + ENDIF(${INDEX} EQUAL -1) + +# now that we collected all arguments, process them + + IF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") + SET(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + ENDIF("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") + + # In config-mode, we rely on the variable <package>_CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + IF(FPHSA_CONFIG_MODE) + LIST(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + LIST(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + SET(FPHSA_VERSION_VAR ${_NAME}_VERSION) + ENDIF(FPHSA_CONFIG_MODE) + + IF(NOT FPHSA_REQUIRED_VARS) + MESSAGE(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + ENDIF(NOT FPHSA_REQUIRED_VARS) + + LIST(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + + STRING(TOUPPER ${_NAME} _NAME_UPPER) + STRING(TOLOWER ${_NAME} _NAME_LOWER) + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + SET(MISSING_VARS "") + SET(DETAILS "") + SET(${_NAME_UPPER}_FOUND TRUE) + # check if all passed variables are valid + FOREACH(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + IF(NOT ${_CURRENT_VAR}) + SET(${_NAME_UPPER}_FOUND FALSE) + SET(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}") + ELSE(NOT ${_CURRENT_VAR}) + SET(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]") + ENDIF(NOT ${_CURRENT_VAR}) + ENDFOREACH(_CURRENT_VAR) + + + # version handling: + SET(VERSION_MSG "") + SET(VERSION_OK TRUE) + SET(VERSION ${${FPHSA_VERSION_VAR}} ) + IF (${_NAME}_FIND_VERSION) + + IF(VERSION) + + IF(${_NAME}_FIND_VERSION_EXACT) # exact version required + IF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + SET(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + SET(VERSION_OK FALSE) + ELSE (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + SET(VERSION_MSG "(found suitable exact version \"${VERSION}\")") + ENDIF (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + + ELSE(${_NAME}_FIND_VERSION_EXACT) # minimum version specified: + IF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + SET(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") + SET(VERSION_OK FALSE) + ELSE ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + SET(VERSION_MSG "(found suitable version \"${VERSION}\", required is \"${${_NAME}_FIND_VERSION}\")") + ENDIF ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + ENDIF(${_NAME}_FIND_VERSION_EXACT) + + ELSE(VERSION) + + # if the package was not found, but a version was given, add that to the output: + IF(${_NAME}_FIND_VERSION_EXACT) + SET(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + ELSE(${_NAME}_FIND_VERSION_EXACT) + SET(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + ENDIF(${_NAME}_FIND_VERSION_EXACT) + + ENDIF(VERSION) + ELSE (${_NAME}_FIND_VERSION) + IF(VERSION) + SET(VERSION_MSG "(found version \"${VERSION}\")") + ENDIF(VERSION) + ENDIF (${_NAME}_FIND_VERSION) + + IF(VERSION_OK) + SET(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]") + ELSE(VERSION_OK) + SET(${_NAME_UPPER}_FOUND FALSE) + ENDIF(VERSION_OK) + + + # print the result: + IF (${_NAME_UPPER}_FOUND) + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG}" "${DETAILS}") + ELSE (${_NAME_UPPER}_FOUND) + + IF(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + ELSE(FPHSA_CONFIG_MODE) + IF(NOT VERSION_OK) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") + ELSE(NOT VERSION_OK) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") + ENDIF(NOT VERSION_OK) + ENDIF(FPHSA_CONFIG_MODE) + + ENDIF (${_NAME_UPPER}_FOUND) + + SET(${_NAME_UPPER}_FOUND ${${_NAME_UPPER}_FOUND} PARENT_SCOPE) + +ENDFUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _FIRST_ARG) diff --git a/carl9170fw/extra/FindUSB-1.0.cmake b/carl9170fw/extra/FindUSB-1.0.cmake new file mode 100644 index 0000000..83df063 --- /dev/null +++ b/carl9170fw/extra/FindUSB-1.0.cmake @@ -0,0 +1,112 @@ +# - Try to find USB-1.0 +# Once done this will define +# +# USB-1.0_FOUND - system has USB-1.0 +# USB-1.0_INCLUDE_DIRS - the USB-1.0 include directory +# USB-1.0_LIBRARIES - Link these to use USB-1.0 +# USB-1.0_DEFINITIONS - Compiler switches required for using USB-1.0 +# +# Copyright (c) 2009 Andreas Schneider <mail@cynapses.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +if (USB-1.0_LIBRARIES AND USB-1.0_INCLUDE_DIRS) + # in cache already + set(USB-1.0_FOUND TRUE) +else (USB-1.0_LIBRARIES AND USB-1.0_INCLUDE_DIRS) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(libusb-1.0 _USB-1.0_INCLUDEDIR _USB-1.0_LIBDIR _USB-1.0_LDFLAGS _USB-1.0_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(_USB-1.0 libusb-1.0) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + + find_path(USB-1.0_INCLUDE_DIR + NAMES + libusb.h + PATHS + ${_USB-1.0_INCLUDEDIR} + /usr/include + /usr/local/include + /opt/local/include + /sw/include + PATH_SUFFIXES + libusb-1.0 + ) + mark_as_advanced(USB-1.0_INCLUDE_DIR) + + find_library(USB-1.0_LIBRARY + NAMES + usb-1.0 + PATHS + ${_USB-1.0_LIBDIR} + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + mark_as_advanced(USB-1.0_LIBRARY) + + if (USB-1.0_LIBRARY) + set(USB-1.0_FOUND TRUE) + mark_as_advanced(USB-1.0_FOUND) + endif (USB-1.0_LIBRARY) + + set(USB-1.0_INCLUDE_DIRS + ${USB-1.0_INCLUDE_DIR} + ) + + if (USB-1.0_FOUND) + set(USB-1.0_LIBRARIES + ${USB-1.0_LIBRARIES} + ${USB-1.0_LIBRARY} + ) + endif (USB-1.0_FOUND) + + if (USB-1.0_INCLUDE_DIRS AND USB-1.0_LIBRARIES) + set(USB-1.0_FOUND TRUE) + endif (USB-1.0_INCLUDE_DIRS AND USB-1.0_LIBRARIES) + + if (USB-1.0_FOUND) + if (NOT USB-1.0_FIND_QUIETLY) + message(STATUS "Found USB-1.0: ${USB-1.0_LIBRARIES}") + endif (NOT USB-1.0_FIND_QUIETLY) + else (USB-1.0_FOUND) + if (USB-1.0_FIND_REQUIRED) + message(FATAL_ERROR "Could not find USB-1.0") + endif (USB-1.0_FIND_REQUIRED) + endif (USB-1.0_FOUND) + + # show the USB-1.0_INCLUDE_DIRS and USB-1.0_LIBRARIES variables only in the advanced view + mark_as_advanced(USB-1.0_INCLUDE_DIRS USB-1.0_LIBRARIES) + +endif (USB-1.0_LIBRARIES AND USB-1.0_INCLUDE_DIRS) + diff --git a/carl9170fw/extra/GCCVersion.cmake b/carl9170fw/extra/GCCVersion.cmake new file mode 100644 index 0000000..bb5fef3 --- /dev/null +++ b/carl9170fw/extra/GCCVersion.cmake @@ -0,0 +1,60 @@ +#============================================================================= +# Copyright 2006-2009 Kitware, Inc. +# Copyright 2006-2008 Andreas Schneider <mail@cynapses.org> +# Copyright 2007 Wengo +# Copyright 2007 Mike Jackson +# Copyright 2008 Andreas Pakulat <apaku@gmx.de> +# Copyright 2008-2009 Philip Lowman <philip@yhbt.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#------------------------------------------------------------------------------- + +# +# Runs compiler with "-dumpversion" and parses major/minor +# version with a regex. +# +FUNCTION(_COMPILER_DUMPVERSION _OUTPUT_VERSION) + + EXEC_PROGRAM(${CMAKE_C_COMPILER} + ARGS ${CMAKE_C_COMPILER_ARG1} -dumpversion + OUTPUT_VARIABLE _COMPILER_VERSION + ) + STRING(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" + _COMPILER_VERSION ${_COMPILER_VERSION}) + + SET(${_OUTPUT_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) +ENDFUNCTION() + +# +# End functions/macros +# +#------------------------------------------------------------------------------- + + diff --git a/carl9170fw/extra/libusb-zeropacket.diff b/carl9170fw/extra/libusb-zeropacket.diff new file mode 100644 index 0000000..9aa73de --- /dev/null +++ b/carl9170fw/extra/libusb-zeropacket.diff @@ -0,0 +1,38 @@ +diff -Nurp libusb-1.0-1.0.2/libusb/libusb.h libusb-1.0-1.0.2-orig/libusb/libusb.h +--- libusb-1.0-1.0.2/libusb/libusb.h 2009-06-07 23:18:19.000000000 +0200 ++++ libusb-1.0-1.0.2-orig/libusb/libusb.h 2009-08-10 22:07:41.000000000 +0200 +@@ -673,6 +673,9 @@ enum libusb_transfer_flags { + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, ++ ++ /** Send an extra termination packet, when needed */ ++ LIBUSB_TRANSFER_ZERO_PACKET = 1<<3, + }; + + /** \ingroup asyncio +diff -Nurp libusb-1.0-1.0.2/libusb/os/linux_usbfs.c libusb-1.0-1.0.2-orig/libusb/os/linux_usbfs.c +--- libusb-1.0-1.0.2/libusb/os/linux_usbfs.c 2009-06-10 22:41:26.000000000 +0200 ++++ libusb-1.0-1.0.2-orig/libusb/os/linux_usbfs.c 2009-08-10 22:10:14.000000000 +0200 +@@ -1298,6 +1298,8 @@ static int submit_bulk_transfer(struct u + urb->type = urb_type; + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer + (i * MAX_BULK_BUFFER_LENGTH); ++ if (transfer->flags & LIBUSB_TRANSFER_ZERO_PACKET) ++ urb->flags = USBFS_URB_ZERO_PACKET; + if (i == num_urbs - 1 && last_urb_partial) + urb->buffer_length = transfer->length % MAX_BULK_BUFFER_LENGTH; + else +diff -Nurp libusb-1.0-1.0.2/libusb/os/linux_usbfs.h libusb-1.0-1.0.2-orig/libusb/os/linux_usbfs.h +--- libusb-1.0-1.0.2/libusb/os/linux_usbfs.h 2008-07-16 16:17:57.000000000 +0200 ++++ libusb-1.0-1.0.2-orig/libusb/os/linux_usbfs.h 2009-08-10 22:13:15.000000000 +0200 +@@ -63,6 +63,9 @@ struct usbfs_getdriver { + #define USBFS_URB_DISABLE_SPD 1 + #define USBFS_URB_ISO_ASAP 2 + #define USBFS_URB_QUEUE_BULK 0x10 ++#define USBFS_URB_NO_FSBR 0x20 ++#define USBFS_URB_ZERO_PACKET 0x40 ++#define USBFS_URB_NO_INTERRUPT 0x80 + + enum usbfs_urb_type { + USBFS_URB_TYPE_ISO = 0, diff --git a/carl9170fw/extra/sh-elf-linux.cmake b/carl9170fw/extra/sh-elf-linux.cmake new file mode 100644 index 0000000..8be6d36 --- /dev/null +++ b/carl9170fw/extra/sh-elf-linux.cmake @@ -0,0 +1,21 @@ +set(CMAKE_SYSTEM_NAME "Generic") +set(CMAKE_SYSTEM_PROCESSOR "sh2") + +set_property(DIRECTORY PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) + +set(CMAKE_FIND_ROOT_PATH ${CMAKE_SOURCE_DIR}/toolchain/inst/) + +set(CMAKE_C_FLAGS "-m2 -ml -Os -ffreestanding -nostartfiles") +set(CMAKE_C_LINK_FLAGS "-static -EL -x --gc-sections") + +set(OBJCOPY ${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-objcopy) +set(CMAKE_C_COMPILER "${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-gcc") +set(CMAKE_AR ${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-ar) +set(CMAKE_ASM_COMPILER ${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-as) +set(CMAKE_ASM-ATT_COMPILER ${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-as) +set(CMAKE_LINKER ${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-ld) +set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_SOURCE_DIR}/toolchain/inst/bin/sh-elf-ld <OBJECTS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> -o <TARGET>") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/carl9170fw/genapi.sh b/carl9170fw/genapi.sh new file mode 100755 index 0000000..8912cc6 --- /dev/null +++ b/carl9170fw/genapi.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +cat <<EOF > include/shared/version.h +#ifndef __CARL9170_SHARED_VERSION_H +#define __CARL9170_SHARED_VERSION_H +#define CARL9170FW_VERSION_YEAR $((100`date +%Y`%100)) +#define CARL9170FW_VERSION_MONTH $((100`date +%m`%100)) +#define CARL9170FW_VERSION_DAY $((100`date +%d`%100)) +#define CARL9170FW_VERSION_GIT "`git describe 2>/dev/null`" +#endif /* __CARL9170_SHARED_VERSION_H */ +EOF diff --git a/carl9170fw/include/linux/ch9.h b/carl9170fw/include/linux/ch9.h new file mode 100644 index 0000000..94e01bb --- /dev/null +++ b/carl9170fw/include/linux/ch9.h @@ -0,0 +1,803 @@ +/* + * This file holds USB constants and structures that are needed for + * USB device APIs. These are used by the USB device model, which is + * defined in chapter 9 of the USB 2.0 specification and in the + * Wireless USB 1.0 (spread around). Linux has several APIs in C that + * need these: + * + * - the master/host side Linux-USB kernel driver API; + * - the "usbfs" user space API; and + * - the Linux "gadget" slave/device/peripheral side driver API. + * + * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems + * act either as a USB master/host or as a USB slave/device. That means + * the master and slave side APIs benefit from working well together. + * + * There's also "Wireless USB", using low power short range radios for + * peripheral interconnection but otherwise building on the USB framework. + * + * Note all descriptors are declared '__attribute__((packed))' so that: + * + * [a] they never get padded, either internally (USB spec writers + * probably handled that) or externally; + * + * [b] so that accessing bigger-than-a-bytes fields will never + * generate bus errors on any platform, even when the location of + * its descriptor inside a bundle isn't "naturally aligned", and + * + * [c] for consistency, removing all doubt even when it appears to + * someone that the two other points are non-issues for that + * particular descriptor type. + */ + +#ifndef __LINUX_USB_CH9_H +#define __LINUX_USB_CH9_H + +#include <linux/types.h> /* __u8 etc */ + +/*-------------------------------------------------------------------------*/ + +/* CONTROL REQUEST SUPPORT */ + +/* + * USB directions + * + * This bit flag is used in endpoint descriptors' bEndpointAddress field. + * It's also one of three fields in control requests bRequestType. + */ +#define USB_DIR_MASK 0x80 +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB types, the second of three bRequestType fields + */ +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients, the third of three bRequestType fields + */ +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 +/* From Wireless USB 1.0 */ +#define USB_RECIP_PORT 0x04 +#define USB_RECIP_RPIPE 0x05 + +/* + * Standard requests, for the bRequest field of a SETUP packet. + * + * These are qualified by the bRequestType field, so that for example + * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved + * by a GET_STATUS request. + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */ +#define USB_REQ_GET_ENCRYPTION 0x0E +#define USB_REQ_RPIPE_ABORT 0x0E +#define USB_REQ_SET_HANDSHAKE 0x0F +#define USB_REQ_RPIPE_RESET 0x0F +#define USB_REQ_GET_HANDSHAKE 0x10 +#define USB_REQ_SET_CONNECTION 0x11 +#define USB_REQ_SET_SECURITY_DATA 0x12 +#define USB_REQ_GET_SECURITY_DATA 0x13 +#define USB_REQ_SET_WUSB_DATA 0x14 +#define USB_REQ_LOOPBACK_DATA_WRITE 0x15 +#define USB_REQ_LOOPBACK_DATA_READ 0x16 +#define USB_REQ_SET_INTERFACE_DS 0x17 + +/* The Link Power Management (LPM) ECN defines USB_REQ_TEST_AND_SET command, + * used by hubs to put ports into a new L1 suspend state, except that it + * forgot to define its number ... + */ + +/* + * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and + * are read as a bit array returned by USB_REQ_GET_STATUS. (So there + * are at most sixteen features of each type.) Hubs may also support a + * new USB_REQ_TEST_AND_SET_FEATURE to put ports into L1 suspend. + */ +#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ +#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ +#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ +#define USB_DEVICE_BATTERY 2 /* (wireless) */ +#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ +#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ +#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ +#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ +#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ + +#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ + + +/** + * struct usb_ctrlrequest - SETUP data for a USB device control request + * @bRequestType: matches the USB bmRequestType field + * @bRequest: matches the USB bRequest field + * @wValue: matches the USB wValue field (le16 byte order) + * @wIndex: matches the USB wIndex field (le16 byte order) + * @wLength: matches the USB wLength field (le16 byte order) + * + * This structure is used to send control requests to a USB device. It matches + * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the + * USB spec for a fuller description of the different fields, and what they are + * used for. + * + * Note that the driver for any interface can issue control requests. + * For most devices, interfaces don't coordinate with each other, so + * such requests may be made at any time. + */ +struct usb_ctrlrequest { + __u8 bRequestType; + __u8 bRequest; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* + * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or + * (rarely) accepted by SET_DESCRIPTOR. + * + * Note that all multi-byte values here are encoded in little endian + * byte order "on the wire". Within the kernel and when exposed + * through the Linux-USB APIs, they are not converted to cpu byte + * order; it is the responsibility of the client code to do this. + * The single exception is when device and configuration descriptors (but + * not other descriptors) are read from usbfs (i.e. /proc/bus/usb/BBB/DDD); + * in this case the fields are converted to host endianness by the kernel. + */ + +/* + * Descriptor types ... USB 2.0 spec table 9.5 + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_INTERFACE_POWER 0x08 +/* these are from a minor usb 2.0 revision (ECN) */ +#define USB_DT_OTG 0x09 +#define USB_DT_DEBUG 0x0a +#define USB_DT_INTERFACE_ASSOCIATION 0x0b +/* these are from the Wireless USB spec */ +#define USB_DT_SECURITY 0x0c +#define USB_DT_KEY 0x0d +#define USB_DT_ENCRYPTION_TYPE 0x0e +#define USB_DT_BOS 0x0f +#define USB_DT_DEVICE_CAPABILITY 0x10 +#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11 +#define USB_DT_WIRE_ADAPTER 0x21 +#define USB_DT_RPIPE 0x22 +#define USB_DT_CS_RADIO_CONTROL 0x23 +/* From the USB 3.0 spec */ +#define USB_DT_SS_ENDPOINT_COMP 0x30 + +/* Conventional codes for class-specific descriptors. The convention is + * defined in the USB "Common Class" Spec (3.11). Individual class specs + * are authoritative for their usage, not the "common class" writeup. + */ +#define USB_DT_CS_DEVICE (USB_TYPE_CLASS | USB_DT_DEVICE) +#define USB_DT_CS_CONFIG (USB_TYPE_CLASS | USB_DT_CONFIG) +#define USB_DT_CS_STRING (USB_TYPE_CLASS | USB_DT_STRING) +#define USB_DT_CS_INTERFACE (USB_TYPE_CLASS | USB_DT_INTERFACE) +#define USB_DT_CS_ENDPOINT (USB_TYPE_CLASS | USB_DT_ENDPOINT) + +/* All standard descriptors have these 2 fields at the beginning */ +struct usb_descriptor_header { + __u8 bLength; + __u8 bDescriptorType; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEVICE: Device descriptor */ +struct usb_device_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdUSB; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + __u8 bMaxPacketSize0; + __le16 idVendor; + __le16 idProduct; + __le16 bcdDevice; + __u8 iManufacturer; + __u8 iProduct; + __u8 iSerialNumber; + __u8 bNumConfigurations; +} __attribute__ ((packed)); + +#define USB_DT_DEVICE_SIZE 18 + + +/* + * Device and/or Interface Class codes + * as found in bDeviceClass or bInterfaceClass + * and defined by www.usb.org documents + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b /* chip+ smart card */ +#define USB_CLASS_CONTENT_SEC 0x0d /* content security */ +#define USB_CLASS_VIDEO 0x0e +#define USB_CLASS_WIRELESS_CONTROLLER 0xe0 +#define USB_CLASS_MISC 0xef +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +#define USB_SUBCLASS_VENDOR_SPEC 0xff + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_CONFIG: Configuration descriptor information. + * + * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the + * descriptor type is different. Highspeed-capable devices can look + * different depending on what speed they're currently running. Only + * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG + * descriptors. + */ +struct usb_config_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wTotalLength; + __u8 bNumInterfaces; + __u8 bConfigurationValue; + __u8 iConfiguration; + __u8 bmAttributes; + __u8 bMaxPower; +} __attribute__ ((packed)); + +#define USB_DT_CONFIG_SIZE 9 + +/* from config descriptor bmAttributes */ +#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */ +#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */ +#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */ +#define USB_CONFIG_ATT_BATTERY (1 << 4) /* battery powered */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_STRING: String descriptor */ +struct usb_string_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wData[1]; /* UTF-16LE encoded */ +} __attribute__ ((packed)); + +/* note that "string" zero is special, it holds language codes that + * the device supports, not Unicode characters. + */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_INTERFACE: Interface descriptor */ +struct usb_interface_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + __u8 bNumEndpoints; + __u8 bInterfaceClass; + __u8 bInterfaceSubClass; + __u8 bInterfaceProtocol; + __u8 iInterface; +} __attribute__ ((packed)); + +#define USB_DT_INTERFACE_SIZE 9 + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_ENDPOINT: Endpoint descriptor */ +struct usb_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bEndpointAddress; + __u8 bmAttributes; + __le16 wMaxPacketSize; + __u8 bInterval; +} __attribute__ ((packed)); + +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ + + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_SYNCTYPE 0x0c +#define USB_ENDPOINT_SYNC_NONE (0 << 2) +#define USB_ENDPOINT_SYNC_ASYNC (1 << 2) +#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) +#define USB_ENDPOINT_SYNC_SYNC (3 << 2) + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + +/*-------------------------------------------------------------------------*/ + +/** + * usb_endpoint_num - get the endpoint's number + * @epd: endpoint to be checked + * + * Returns @epd's number: 0 to 15. + */ +static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd) +{ + return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +} + +/** + * usb_endpoint_type - get the endpoint's transfer type + * @epd: endpoint to be checked + * + * Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according + * to @epd's transfer type. + */ +static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd) +{ + return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +} + +/** + * usb_endpoint_dir_in - check if the endpoint has IN direction + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type IN, otherwise it returns false. + */ +static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN); +} + +/** + * usb_endpoint_dir_out - check if the endpoint has OUT direction + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type OUT, otherwise it returns false. + */ +static inline int usb_endpoint_dir_out( + const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); +} + +/** + * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type bulk, otherwise it returns false. + */ +static inline int usb_endpoint_xfer_bulk( + const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK); +} + +/** + * usb_endpoint_xfer_control - check if the endpoint has control transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type control, otherwise it returns false. + */ +static inline int usb_endpoint_xfer_control( + const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL); +} + +/** + * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type interrupt, otherwise it returns + * false. + */ +static inline int usb_endpoint_xfer_int( + const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_INT); +} + +/** + * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type isochronous, otherwise it returns + * false. + */ +static inline int usb_endpoint_xfer_isoc( + const struct usb_endpoint_descriptor *epd) +{ + return (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_ISOC; +} + +/** + * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN + * @epd: endpoint to be checked + * + * Returns true if the endpoint has bulk transfer type and IN direction, + * otherwise it returns false. + */ +static inline int usb_endpoint_is_bulk_in( + const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd); +} + +/** + * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT + * @epd: endpoint to be checked + * + * Returns true if the endpoint has bulk transfer type and OUT direction, + * otherwise it returns false. + */ +static inline int usb_endpoint_is_bulk_out( + const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd); +} + +/** + * usb_endpoint_is_int_in - check if the endpoint is interrupt IN + * @epd: endpoint to be checked + * + * Returns true if the endpoint has interrupt transfer type and IN direction, + * otherwise it returns false. + */ +static inline int usb_endpoint_is_int_in( + const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd); +} + +/** + * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT + * @epd: endpoint to be checked + * + * Returns true if the endpoint has interrupt transfer type and OUT direction, + * otherwise it returns false. + */ +static inline int usb_endpoint_is_int_out( + const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd); +} + +/** + * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN + * @epd: endpoint to be checked + * + * Returns true if the endpoint has isochronous transfer type and IN direction, + * otherwise it returns false. + */ +static inline int usb_endpoint_is_isoc_in( + const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd); +} + +/** + * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT + * @epd: endpoint to be checked + * + * Returns true if the endpoint has isochronous transfer type and OUT direction, + * otherwise it returns false. + */ +static inline int usb_endpoint_is_isoc_out( + const struct usb_endpoint_descriptor *epd) +{ + return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd); +} + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ +struct usb_ss_ep_comp_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bMaxBurst; + __u8 bmAttributes; + __le16 wBytesPerInterval; +} __attribute__ ((packed)); + +#define USB_DT_SS_EP_COMP_SIZE 6 + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */ +struct usb_qualifier_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdUSB; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + __u8 bMaxPacketSize0; + __u8 bNumConfigurations; + __u8 bRESERVED; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_OTG (from OTG 1.0a supplement) */ +struct usb_otg_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bmAttributes; /* support for HNP, SRP, etc */ +} __attribute__ ((packed)); + +/* from usb_otg_descriptor.bmAttributes */ +#define USB_OTG_SRP (1 << 0) +#define USB_OTG_HNP (1 << 1) /* swap host/device roles */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEBUG: for special highspeed devices, replacing serial console */ +struct usb_debug_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + /* bulk endpoints with 8 byte maxpacket */ + __u8 bDebugInEndpoint; + __u8 bDebugOutEndpoint; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */ +struct usb_interface_assoc_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bFirstInterface; + __u8 bInterfaceCount; + __u8 bFunctionClass; + __u8 bFunctionSubClass; + __u8 bFunctionProtocol; + __u8 iFunction; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_SECURITY: group of wireless security descriptors, including + * encryption types available for setting up a CC/association. + */ +struct usb_security_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wTotalLength; + __u8 bNumEncryptionTypes; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_KEY: used with {GET,SET}_SECURITY_DATA; only public keys + * may be retrieved. + */ +struct usb_key_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 tTKID[3]; + __u8 bReserved; + __u8 bKeyData[0]; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_ENCRYPTION_TYPE: bundled in DT_SECURITY groups */ +struct usb_encryption_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bEncryptionType; +#define USB_ENC_TYPE_UNSECURE 0 +#define USB_ENC_TYPE_WIRED 1 /* non-wireless mode */ +#define USB_ENC_TYPE_CCM_1 2 /* aes128/cbc session */ +#define USB_ENC_TYPE_RSA_1 3 /* rsa3072/sha1 auth */ + __u8 bEncryptionValue; /* use in SET_ENCRYPTION */ + __u8 bAuthKeyIndex; +} __attribute__((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_BOS: group of device-level capabilities */ +struct usb_bos_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wTotalLength; + __u8 bNumDeviceCaps; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEVICE_CAPABILITY: grouped with BOS */ +struct usb_dev_cap_header { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; +} __attribute__((packed)); + +#define USB_CAP_TYPE_WIRELESS_USB 1 + +struct usb_wireless_cap_descriptor { /* Ultra Wide Band */ + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; + + __u8 bmAttributes; +#define USB_WIRELESS_P2P_DRD (1 << 1) +#define USB_WIRELESS_BEACON_MASK (3 << 2) +#define USB_WIRELESS_BEACON_SELF (1 << 2) +#define USB_WIRELESS_BEACON_DIRECTED (2 << 2) +#define USB_WIRELESS_BEACON_NONE (3 << 2) + __le16 wPHYRates; /* bit rates, Mbps */ +#define USB_WIRELESS_PHY_53 (1 << 0) /* always set */ +#define USB_WIRELESS_PHY_80 (1 << 1) +#define USB_WIRELESS_PHY_107 (1 << 2) /* always set */ +#define USB_WIRELESS_PHY_160 (1 << 3) +#define USB_WIRELESS_PHY_200 (1 << 4) /* always set */ +#define USB_WIRELESS_PHY_320 (1 << 5) +#define USB_WIRELESS_PHY_400 (1 << 6) +#define USB_WIRELESS_PHY_480 (1 << 7) + __u8 bmTFITXPowerInfo; /* TFI power levels */ + __u8 bmFFITXPowerInfo; /* FFI power levels */ + __le16 bmBandGroup; + __u8 bReserved; +} __attribute__((packed)); + +#define USB_CAP_TYPE_EXT 2 + +struct usb_ext_cap_descriptor { /* Link Power Management */ + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; + __u8 bmAttributes; +#define USB_LPM_SUPPORT (1 << 1) /* supports LPM */ +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with + * each endpoint descriptor for a wireless device + */ +struct usb_wireless_ep_comp_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bMaxBurst; + __u8 bMaxSequence; + __le16 wMaxStreamDelay; + __le16 wOverTheAirPacketSize; + __u8 bOverTheAirInterval; + __u8 bmCompAttributes; +#define USB_ENDPOINT_SWITCH_MASK 0x03 /* in bmCompAttributes */ +#define USB_ENDPOINT_SWITCH_NO 0 +#define USB_ENDPOINT_SWITCH_SWITCH 1 +#define USB_ENDPOINT_SWITCH_SCALE 2 +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_REQ_SET_HANDSHAKE is a four-way handshake used between a wireless + * host and a device for connection set up, mutual authentication, and + * exchanging short lived session keys. The handshake depends on a CC. + */ +struct usb_handshake { + __u8 bMessageNumber; + __u8 bStatus; + __u8 tTKID[3]; + __u8 bReserved; + __u8 CDID[16]; + __u8 nonce[16]; + __u8 MIC[8]; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_REQ_SET_CONNECTION modifies or revokes a connection context (CC). + * A CC may also be set up using non-wireless secure channels (including + * wired USB!), and some devices may support CCs with multiple hosts. + */ +struct usb_connection_context { + __u8 CHID[16]; /* persistent host id */ + __u8 CDID[16]; /* device id (unique w/in host context) */ + __u8 CK[16]; /* connection key */ +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB 2.0 defines three speeds, here's how Linux identifies them */ + +enum usb_device_speed { + USB_SPEED_UNKNOWN = 0, /* enumerating */ + USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ + USB_SPEED_HIGH, /* usb 2.0 */ + USB_SPEED_WIRELESS, /* wireless (usb 2.5) */ + USB_SPEED_SUPER, /* usb 3.0 */ +}; + +enum usb_device_state { + /* NOTATTACHED isn't in the USB spec, and this state acts + * the same as ATTACHED ... but it's clearer this way. + */ + USB_STATE_NOTATTACHED = 0, + + /* chapter 9 and authentication (wireless) device states */ + USB_STATE_ATTACHED, + USB_STATE_POWERED, /* wired */ + USB_STATE_RECONNECTING, /* auth */ + USB_STATE_UNAUTHENTICATED, /* auth */ + USB_STATE_DEFAULT, /* limited function */ + USB_STATE_ADDRESS, + USB_STATE_CONFIGURED, /* most functions */ + + USB_STATE_SUSPENDED + + /* NOTE: there are actually four different SUSPENDED + * states, returning to POWERED, DEFAULT, ADDRESS, or + * CONFIGURED respectively when SOF tokens flow again. + * At this level there's no difference between L1 and L2 + * suspend states. (L2 being original USB 1.1 suspend.) + */ +}; + +#endif /* __LINUX_USB_CH9_H */ diff --git a/carl9170fw/include/linux/compiler.h b/carl9170fw/include/linux/compiler.h new file mode 100644 index 0000000..d331f3c --- /dev/null +++ b/carl9170fw/include/linux/compiler.h @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __SHARED_COMPILER_H +#define __SHARED_COMPILER_H + +#define __noinline __attribute__((noinline)) +#define __noreturn __attribute__((noreturn)) +#define __inline __attribute__((always_inline)) +#define __hot __attribute__((hot)) +#define __cold __attribute__((cold)) +#define __unused __attribute__((unused)) +#define __force __attribute__((force)) +#define __section(s) __attribute__((section("." # s))) +#define __aligned(a) __attribute__((aligned(a))) +#define __packed __attribute__((packed)) + +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) + +#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define __roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + +#define __must_be_array(a) \ + BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0]))) +#define ARRAY_SIZE(arr) (sizeof((arr)) / sizeof((arr)[0]) + __must_be_array(arr)) + +#define BIT(b) (1 << (b)) +#define MASK(w) (BIT(w) - 1) + +#undef offsetof +#ifdef __compiler_offsetof +# define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER) +#else +# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +#define max(x, y) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1 : __min2; }) + +#define max_t(type, x, y) ({ \ + type __max1 = (x); \ + type __max2 = (y); \ + __max1 > __max2 ? __max1 : __max2; }) + + +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) * __mptr = (ptr); \ + (type *)(((unsigned long)__mptr - offsetof(type, member))); }) + +#define MAX_ERRNO 4095 + +#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) + +static inline void *ERR_PTR(long errornr) +{ + return (void *) errornr; +} + +static inline long PTR_ERR(const void *ptr) +{ + return (long) ptr; +} + +static inline long IS_ERR(const void *ptr) +{ + return IS_ERR_VALUE((unsigned long)ptr); +} + +static inline long IS_ERR_OR_NULL(const void *ptr) +{ + return !ptr || IS_ERR_VALUE((unsigned long)ptr); +} + +#endif /* __SHARED_COMPILER_H */ diff --git a/carl9170fw/include/linux/ieee80211.h b/carl9170fw/include/linux/ieee80211.h new file mode 100644 index 0000000..e4448ca --- /dev/null +++ b/carl9170fw/include/linux/ieee80211.h @@ -0,0 +1,1918 @@ +/* + * IEEE 802.11 defines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * <jkmaline@cc.hut.fi> + * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2005, Devicescape Software, Inc. + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_IEEE80211_H +#define __LINUX_IEEE80211_H + +/* + * DS bit usage + * + * TA = transmitter address + * RA = receiver address + * DA = destination address + * SA = source address + * + * ToDS FromDS A1(RA) A2(TA) A3 A4 Use + * ----------------------------------------------------------------- + * 0 0 DA SA BSSID - IBSS/DLS + * 0 1 DA BSSID SA - AP -> STA + * 1 0 BSSID SA DA - AP <- STA + * 1 1 RA TA DA SA unspecified (WDS) + */ + +#define FCS_LEN 4 + +#define IEEE80211_FCTL_VERS 0x0003 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_PROTECTED 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 +#define IEEE80211_STYPE_ACTION 0x00D0 + +/* control */ +#define IEEE80211_STYPE_BACK_REQ 0x0080 +#define IEEE80211_STYPE_BACK 0x0090 +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 +#define IEEE80211_STYPE_QOS_DATA 0x0080 +#define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090 +#define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0 +#define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0 +#define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0 +#define IEEE80211_STYPE_QOS_CFACK 0x00D0 +#define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 +#define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 + + +/* miscellaneous IEEE 802.11 constants */ +#define IEEE80211_MAX_FRAG_THRESHOLD 2352 +#define IEEE80211_MAX_RTS_THRESHOLD 2353 +#define IEEE80211_MAX_AID 2007 +#define IEEE80211_MAX_TIM_LEN 251 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + 802.11e clarifies the figure in section 7.1.2. The frame body is + up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */ +#define IEEE80211_MAX_DATA_LEN 2304 +/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */ +#define IEEE80211_MAX_FRAME_LEN 2352 + +#define IEEE80211_MAX_SSID_LEN 32 + +#define IEEE80211_MAX_MESH_ID_LEN 32 + +#define IEEE80211_QOS_CTL_LEN 2 +/* 1d tag mask */ +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +/* TID mask */ +#define IEEE80211_QOS_CTL_TID_MASK 0x000f +/* EOSP */ +#define IEEE80211_QOS_CTL_EOSP 0x0010 +/* ACK policy */ +#define IEEE80211_QOS_CTL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020 +#define IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL 0x0040 +#define IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK 0x0060 +#define IEEE80211_QOS_CTL_ACK_POLICY_MASK 0x0060 +/* A-MSDU 802.11n */ +#define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080 +/* Mesh Control 802.11s */ +#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 + +/* U-APSD queue for WMM IEs sent by AP */ +#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) +#define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f + +/* U-APSD queues for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f + +/* U-APSD max SP length for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 + +#define IEEE80211_HT_CTL_LEN 4 + +struct ieee80211_hdr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; + u8 addr4[6]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; +} __attribute__ ((packed)); + +struct ieee80211_qos_hdr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; + __le16 qos_ctrl; +} __attribute__ ((packed)); + +/** + * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_tods(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0; +} + +/** + * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_fromds(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0; +} + +/** + * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_a4(__le16 fc) +{ + __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + return (fc & tmp) == tmp; +} + +/** + * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_morefrags(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0; +} + +/** + * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_retry(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0; +} + +/** + * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_pm(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0; +} + +/** + * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_moredata(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0; +} + +/** + * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_protected(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0; +} + +/** + * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_order(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0; +} + +/** + * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_mgmt(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT); +} + +/** + * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ctl(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL); +} + +/** + * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_qos(__le16 fc) +{ + /* + * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need + * to check the one bit + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); +} + +/** + * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_present(__le16 fc) +{ + /* + * mask with 0x40 and test that that bit is clear to only return true + * for the data-containing substypes. + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | 0x40)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); +} + +/** + * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); +} + +/** + * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); +} + +/** + * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); +} + +/** + * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); +} + +/** + * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); +} + +/** + * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_beacon(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); +} + +/** + * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_atim(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); +} + +/** + * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC |