Repository: free-electrons/elixir Branch: master Commit: 7ee53451c17e Files: 167 Total size: 1005.5 KB Directory structure: gitextract_uz81yfub/ ├── .dockerignore ├── .editorconfig ├── .gitignore ├── .mailmap ├── .travis.yml ├── CHANGELOG.adoc ├── COPYING ├── README.adoc ├── docker/ │ ├── 000-default.conf │ ├── Dockerfile │ └── gitconfig ├── elixir/ │ ├── __init__.py │ ├── api.py │ ├── autocomplete.py │ ├── data.py │ ├── filters/ │ │ ├── __init__.py │ │ ├── configin.py │ │ ├── cppinc.py │ │ ├── cpppathinc.py │ │ ├── defconfig.py │ │ ├── dtscompcode.py │ │ ├── dtscompdocs.py │ │ ├── dtscompdts.py │ │ ├── dtsi.py │ │ ├── ident.py │ │ ├── kconfig.py │ │ ├── kconfigidents.py │ │ ├── makefiledir.py │ │ ├── makefiledtb.py │ │ ├── makefilefile.py │ │ ├── makefileo.py │ │ ├── makefilesrctree.py │ │ ├── makefilesubdir.py │ │ ├── projects.py │ │ └── utils.py │ ├── lib.py │ ├── query.py │ ├── web.py │ └── web_utils.py ├── find-file-doc-comments.pl ├── find_compatible_dts.py ├── projects/ │ ├── amazon-freertos.sh │ ├── arm-trusted-firmware.sh │ ├── barebox.sh │ ├── bluez.sh │ ├── busybox.sh │ ├── coreboot.sh │ ├── dpdk.sh │ ├── freebsd.sh │ ├── glibc.sh │ ├── grub.sh │ ├── iproute2.sh │ ├── linux.sh │ ├── llvm.sh │ ├── mesa.sh │ ├── musl.sh │ ├── ofono.sh │ ├── op-tee.sh │ ├── opensbi.sh │ ├── qemu.sh │ ├── toybox.sh │ ├── u-boot.sh │ ├── uclibc-ng.sh │ ├── xen.sh │ └── zephyr.sh ├── requirements.txt ├── samples/ │ └── projects/ │ └── linuxtest.sh ├── script.sh ├── static/ │ ├── autocomplete.js │ ├── dynamic-references.js │ ├── fonts/ │ │ └── ubuntu/ │ │ ├── LICENCE-FAQ.txt │ │ ├── LICENCE.txt │ │ └── copyright.txt │ ├── messages.json │ ├── robots.txt │ ├── script.js │ └── style.css ├── t/ │ ├── 050-testhelpers.t │ ├── 100-basic.t │ ├── 200-api.t │ ├── 300-doc-comments.t │ ├── 400-web.t │ ├── TestClass.pm │ ├── TestEnvironment.pm │ ├── TestHelpers.pm │ ├── api_test.py │ ├── interact.pl │ └── tree/ │ ├── arch/ │ │ ├── arm/ │ │ │ └── xen/ │ │ │ └── hypercall.S │ │ └── x86/ │ │ └── include/ │ │ ├── asm/ │ │ │ ├── acpi.h │ │ │ ├── ist.h │ │ │ ├── orc_types.h │ │ │ └── uprobes.h │ │ └── uapi/ │ │ └── asm/ │ │ └── ist.h │ ├── drivers/ │ │ ├── firmware/ │ │ │ └── broadcom/ │ │ │ └── bcm74xx_sprom.c │ │ └── i2c/ │ │ ├── i2c-boardinfo.c │ │ ├── i2c-core-acpi.c │ │ ├── i2c-core-base.c │ │ ├── i2c-core-of.c │ │ ├── i2c-core-slave.c │ │ ├── i2c-core-smbus.c │ │ ├── i2c-core.h │ │ ├── i2c-dev.c │ │ ├── i2c-smbus.c │ │ └── i2c-stub.c │ ├── include/ │ │ ├── acpi/ │ │ │ ├── acpi_bus.h │ │ │ └── acpi_drivers.h │ │ ├── asm-generic/ │ │ │ ├── barrier.h │ │ │ ├── io.h │ │ │ ├── pci_iomap.h │ │ │ ├── qrwlock.h │ │ │ ├── qspinlock.h │ │ │ └── qspinlock_types.h │ │ ├── linux/ │ │ │ ├── acpi.h │ │ │ ├── apm_bios.h │ │ │ ├── assoc_array.h │ │ │ ├── cred.h │ │ │ ├── i2c-smbus.h │ │ │ ├── i2c.h │ │ │ ├── key.h │ │ │ ├── kmod.h │ │ │ ├── log2.h │ │ │ ├── logic_pio.h │ │ │ ├── memblock.h │ │ │ ├── of.h │ │ │ ├── of_platform.h │ │ │ ├── plist.h │ │ │ ├── pm.h │ │ │ ├── pm_wakeup.h │ │ │ ├── radix-tree.h │ │ │ ├── rbtree.h │ │ │ ├── rcu_node_tree.h │ │ │ ├── rcu_segcblist.h │ │ │ ├── rcu_sync.h │ │ │ ├── rcupdate.h │ │ │ ├── rcutree.h │ │ │ ├── srcu.h │ │ │ ├── srcutree.h │ │ │ ├── stackdepot.h │ │ │ ├── uprobes.h │ │ │ └── xarray.h │ │ ├── trace/ │ │ │ └── events/ │ │ │ └── i2c.h │ │ └── uapi/ │ │ └── linux/ │ │ ├── apm_bios.h │ │ ├── eventpoll.h │ │ ├── i2c.h │ │ └── rseq.h │ ├── issue102.c │ ├── issue131.h │ ├── issue134.c │ ├── issue150.S │ ├── issue186-counterexamples.c │ ├── issue186.c │ ├── issue188.c │ ├── issue192.c │ └── syscall_define.c ├── templates/ │ ├── error.html │ ├── header.html │ ├── ident.html │ ├── layout.html │ ├── sidebar.html │ ├── source.html │ ├── topbar.html │ └── tree.html ├── update.py ├── utils/ │ ├── index │ ├── query.py │ └── speedtest.py └── wsgi.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ # Generated files **/__pycache__ tags /.cache/ .idea/ env venv/ .venv/ **/*.py[cod] **/*.db **/repo **/data .git .gitignore .gitattributes # Editor and other backup files **/*.swp **/*~ **/~* ================================================ FILE: .editorconfig ================================================ # see https://editorconfig.org for the format of this file root = true [*] end_of_line = LF indent_style = space indent_size = 4 ================================================ FILE: .gitignore ================================================ # Generated files __pycache__ tags /.cache/ .idea/ env/ venv/ /data/ # Web-specific http/images http/*.html http/favicon.ico http/robots.txt # Editor and other backup files *.swp *~ ~* .envrc ================================================ FILE: .mailmap ================================================ Christopher White Christopher White Michael Opdenacker Michael Opdenacker Michael Opdenacker Mikaël Bouillot Mikaël Bouillot ================================================ FILE: .travis.yml ================================================ # .travis.yml for Elixir, https://github.com/bootlin/elixir language: python os: - linux dist: - jammy python: - "3.10" before_install: - sudo apt-get -y install libdb-dev python3-pytest libjansson4 universal-ctags - pip install jinja2 pygments bsddb3 falcon script: - prove -v ================================================ FILE: CHANGELOG.adoc ================================================ = Elixir Changelog == 2024-08 - WSGI update * Migrated Elixir from a CGI-based architecture to a WSGI-based architecture. This should improve performance. * Python files were moved from `./http` to `./elixir`. * Made autocomplete use a prefix search that's native for the database. This should massively improve autocomplete performance. Changes to configuration of most HTTP servers will be required. Some HTTP servers may not support WSGI natively and may require a separate WSGI server to proxy to (for example, lighthttpd and nginx do not support WSGI, but you can proxy requests to uWSGI). We also recommend using Python virtual environments to manage dependencies. Check "Manual Installation" section of README for details. == 2025-03 - Query refactor The query tool was extracted to utils/query.py. Example call: `python3 -m utils.query file v6.8 /README'. Version and cmd (file/ident) parameters changed order, to allow for commands that do not take version as a parameter. ================================================ FILE: COPYING ================================================ GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are 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. 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. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. 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 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 work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Affero 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 Affero 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 Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. 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 AGPL, see . ================================================ FILE: README.adoc ================================================ = The Elixir Cross Referencer :doctype: book :pp: {plus}{plus} :toc: :toc-placement!: Elixir is a source code cross-referencer inspired by https://en.wikipedia.org/wiki/LXR_Cross_Referencer[LXR]. It's written in Python and its main purpose is to index every release of a C or C{pp} project (like the Linux kernel) while keeping a minimal footprint. It uses Git as a source-code file store and Berkeley DB for cross-reference data. Internally, it indexes Git _blobs_ rather than trees of files to avoid duplicating work and data. It has a straightforward data structure (reminiscent of older LXR releases) to keep queries simple and fast. You can see it in action on https://elixir.bootlin.com/ link:CHANGELOG.adoc[Changelog] toc::[] = Requirements * Python >= 3.8 * Git >= 1.9 * The Jinja2 and Pygments (>= 2.7) Python libraries * Berkeley DB (and its Python binding) * Universal Ctags * Perl (for non-greedy regexes and automated testing) * Falcon and `mod_wsgi` (for the REST API) = Architecture The shell script (`script.sh`) is the lower layer and provides commands to interact with Git and other Unix utilities. The Python commands use the shell script's services to provide access to the annotated source code and identifier lists (`query.py`) or to create and update the databases (`update.py`). Finally, the web interface (`web.py`) and uses the query interface to generate HTML pages and to answer REST queries, respectively. When installing the system, you should test each layer manually and make sure it works correctly before moving on to the next one. = Manual Installation == Install Dependencies ____ For Debian ____ ---- sudo apt install python3-pip python3-venv libdb-dev python3-dev build-essential universal-ctags perl git apache2 libapache2-mod-wsgi-py3 libjansson4 ---- == Download Elixir Project ---- git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ ---- == Create a virtualenv for Elixir ---- python -m venv /usr/local/elixir/venv . /usr/local/elixir/venv/bin/activate pip install -r /usr/local/elixir/requirements.txt ---- == Create directories for project data ---- mkdir -p /path/elixir-data/linux/repo mkdir -p /path/elixir-data/linux/data ---- == Set environment variables Two environment variables are used to tell Elixir where to find the project's local git repository and its databases: * `LXR_REPO_DIR` (the git repository directory for your project) * `LXR_DATA_DIR` (the database directory for your project) Now open `/etc/profile` and append the following content. ---- export LXR_REPO_DIR=/path/elixir-data/linux/repo export LXR_DATA_DIR=/path/elixir-data/linux/data ---- And then run `source /etc/profile`. == Clone Kernel source code First clone the master tree released by Linus Torvalds: ---- cd /path/elixir-data/linux git clone --bare https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git repo ---- Then, you should also declare a `stable` remote branch corresponding to the `stable` tree, to get all release updates: ---- cd repo git remote add stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git git fetch stable ---- Then, you can also declare an `history` remote branch corresponding to the old Linux versions not present in the other repos, to get all the old version still available: ---- cd repo git remote add history https://github.com/bootlin/linux-history.git git fetch history --tags ---- Feel free to add more remote branches in this way, as Elixir will consider tags from all remote branches. == First Test ---- cd /usr/local/elixir/ ./script.sh list-tags ---- == Create Database ---- . ./venv/bin/activate ./update.py ---- ____ Generating the full database can take a long time: it takes about 15 hours on a Xeon E3-1245 v5 to index 1800 tags in the Linux kernel. For that reason, you may want to tweak the script (for example, by limiting the number of tags with a "head") in order to test the update and query commands. You can even create a new Git repository and just create one tag instead of using the official kernel repository which is very large. ____ == Second Test Verify that the queries work: $ python3 -m utils.query ident v4.10 raw_spin_unlock_irq C $ python3 -m utils.query file v4.10 /kernel/sched/clock.c NOTE: `v4.10` can be replaced with any other tag. NOTE: Don't forget to activate the virtual environment! == Configure httpd The CGI interface (`web.py`) is meant to be called from your web server. Since it includes support for indexing multiple projects, it expects a different variable (`LXR_PROJ_DIR`) which points to a directory with a specific structure: * `` ** `` *** `data` *** `repo` ** `` *** `data` *** `repo` ** `` *** `data` *** `repo` It will then generate the other two variables upon calling the query command. Now replace `/etc/apache2/sites-enabled/000-default.conf` with `docker/000-default.conf`. Note: If using httpd (RedHat/Centos) instead of apache2 (Ubuntu/Debian), the default config file to edit is `/etc/httpd/conf.d/elixir.conf`. Finally, start the httpd server. ---- systemctl restart apache2 ---- == Configure SELinux policy When running systemd with SELinux enabled, httpd server can only visit limited directories. If your /path/elixir-data/ is not one of these allowed directories, you will be responded with 500 status code. To allow httpd server to visit /path/elixir-data/, run following codes: ---- chcon -R -t httpd_sys_rw_content_t /path/elixir-data/ ---- To check if it takes effect, run the following codes: ---- ls -Z /path/elixir-data/ ---- In case you want to check SELinux log related with httpd, run the following codes: ---- audit2why -a | grep httpd | less ---- == Configure systemd log directory By default, the error log of elixir will be put in /tmp/elixir-errors. However, systemd enables PrivateTmp by default. And, the final error directory will be like /tmp/systemd-private-xxxxx-httpd.service-xxxx/tmp/elixir-errors. If you want to disable it, configure httpd.service with the following attribute: ---- PrivateTmp=false ---- == Configuration for other servers Other HTTP servers (like nginx or lighthttpd) may not support WSGI and may require a separate WSGI server, like uWSGI. Information about how to configure uWSGI with Lighthttpd can be found here: https://redmine.lighttpd.net/projects/lighttpd/wiki/HowToPythonWSGI#Python-WSGI-apps-via-uwsgi-SCGI-FastCGI-or-HTTP-using-the-uWSGI-server Pull requests with example uWSGI configuration for Elixir are welcome. = REST API usage After configuring httpd, you can test the API usage: == ident query Send a get request to `/api/ident//?version=&family=`. For example: curl http://127.0.0.1/api/ident/barebox/cdev?version=latest&family=C The response body is of the following structure: ---- { "definitions": [{"path": "commands/loadb.c", "line": 71, "type": "variable"}, ...], "references": [{"path": "arch/arm/boards/cm-fx6/board.c", "line": "64,64,71,72,75", "type": null}, ...] } ---- = Maintenance and enhancements == Using a cache to improve performance At Bootlin, we're using the https://varnish-cache.org/[Varnish http cache] as a front-end to reduce the load on the server running the Elixir code. .-------------. .---------------. .-----------------------. | Http client | --------> | Varnish cache | --------> | Apache running Elixir | '-------------' '---------------' '-----------------------' == Keeping Elixir databases up to date To keep your Elixir databases up to date and index new versions that are released, we're proposing to use a script like `index /srv/elixir-data --all` which is called through a daily cron job. You can set `$ELIXIR_THREADS` if you want to change the number of threads used by update.py for indexing (by default the number of CPUs on your system). = Building Docker images Dockerfiles are provided in the `docker/` directory. To build the image, run the following commands: # git clone https://github.com/bootlin/elixir.git ./elixir # docker build -t elixir --build-arg ELIXIR_VERSION=`git rev-parse --short HEAD` -f ./elixir/docker/Dockerfile ./elixir ELIXIR_VER build argument is optional. Since .git directory is not copied into Docker image by default, the option is used to pass a version string to Elixir. You can then run the image using `docker run`. Here we mount a host directory as Elixir data: # mkdir ./elixir-data # docker run -v ./elixir-data/:/srv/elixir-data -d --name elixir-container elixir The Docker image does not contain any repositories. To index a repository, you can use the `index-repository` script. For example, to add the https://musl.libc.org/[musl] repository, run: # docker exec -it elixir-container index /srv/elixir-data musl Or, to run indexing in a separate container: # docker run -v ./elixir-data/:/srv/elixir-data \ --entrypoint index elixir /srv/elixir-data musl You can also use `index /srv/elixir-data --all` to start indexing all officially supported repositories. After indexing is done, Elixir should be available under the following URL on your host: http://172.17.0.2/musl/latest/source If 172.17.0.2 does not answer, you can check the IP address of the container by running: # docker inspect elixir-container | grep IPAddress == Automatic repository updates The Docker image does not automatically update repositories by itself. You can, for example, start `index /srv/elixir-data --all` in the container (or in a separate container, with Elixir data volume/directory mounted) from cron on the host to periodically update repositories. == Using Docker image as a development server You can easily use the Docker image as a development server by following the steps above, but mounting Elixir source directory from the host into `/usr/local/elixir/` in the container when running `docker run elixir`. Changes in the code made on the host should be automatically reflected in the container. You can use `apache2ctl` to restart Apache. Error logs are available in `/var/log/apache2/error.log` within the container. = Hardware requirements Performance requirements depend mostly on the amount of traffic that you get on your Elixir service. However, a fast server also helps for the initial indexing of the projects. SSD storage is strongly recommended because of the frequent access to git repositories. At Bootlin, here are a few details about the server we're using: * As of July 2019, our Elixir service consumes 17 GB of data (supporting all projects), or for the Linux kernel alone (version 5.2 being the latest), 12 GB for indexing data, and 2 GB for the git repository. * We're using an LXD instance with 8 GB of RAM on a cloud server with 8 CPU cores running at 3.1 GHz. = Contributing to Elixir == Supporting a new project Elixir has a very simple modular architecture that allows to support new source code projects by just adding a new file to the Elixir sources. Elixir's assumptions: * Project sources have to be available in a git repository * All project releases are associated to a given git tag. Elixir only considers such tags. First make an installation of Elixir by following the above instructions. See the `projects` subdirectory for projects that are already supported. Once Elixir works for at least one project, it's time to clone the git repository for the project you want to support: cd /srv/git git clone --bare https://github.com/zephyrproject-rtos/zephyr After doing this, you may also reference and fetch remote branches for this project, for example corresponding to the `stable` tree for the Linux kernel (see the instructions for Linux earlier in this document). Now, in your `LXR_PROJ_DIR` directory, create a new directory for the new project: cd $LXR_PROJ_DIR mkdir -p zephyr/data ln -s /srv/git/zephyr.git repo export LXR_DATA_DIR=$LXR_PROJ_DIR/data export LXR_REPO_DIR=$LXR_PROJ_DIR/repo Now, go back to the Elixir sources and test that tags are correctly extracted: ./script.sh list-tags Depending on how you want to show the available versions on the Elixir pages, you may have to apply substitutions to each tag string, for example to add a `v` prefix if missing, for consistency with how other project versions are shown. You may also decide to ignore specific tags. All this can be done by redefining the default `list_tags()` function in a new `projects/.sh` file. Here's an example (`projects/zephyr.sh` file): list_tags() { echo "$tags" | grep -v '^zephyr-v' } Note that `` *must* match the name of the directory that you created under `LXR_PROJ_DIR`. The next step is to make sure that versions are classified as you wish in the version menu. This classification work is done through the `list_tags_h()` function which generates the output of the `./scripts.sh list-tags -h` command. Here's what you get for the Linux project: v4 v4.16 v4.16 v4 v4.16 v4.16-rc7 v4 v4.16 v4.16-rc6 v4 v4.16 v4.16-rc5 v4 v4.16 v4.16-rc4 v4 v4.16 v4.16-rc3 v4 v4.16 v4.16-rc2 v4 v4.16 v4.16-rc1 ... The first column is the top level menu entry for versions. The second one is the next level menu entry, and the third one is the actual version that can be selected by the menu. Note that this third entry must correspond to the exact name of the tag in git. If the default behavior is not what you want, you will have to customize the `list_tags_h` function. You should also make sure that Elixir properly identifies the most recent versions: ./script.sh get-latest-tags | head If needed, customize the `get_latest_tags()` function. If you want to enable support for `compatible` properties in Devicetree files, add `dts_comp_support=1` at the beginning of `projects/.sh`. You are now ready to generate Elixir's database for your new project: ./update.py You can then check that Elixir works through your http server. == Coding style If you wish to contribute to Elixir's Python code, please follow the https://www.python.org/dev/peps/pep-0008/[official coding style for Python]. == How to send patches The best way to share your contributions with us is to https://github.com/bootlin/elixir/pulls[file a pull request on GitHub]. = Automated testing Elixir includes a simple test suite in `t/`. To run it, from the top-level Elixir directory, run: prove The test suite uses code extracted from Linux v5.4 in `t/tree`. == Licensing of code in `t/tree` The copied code is licensed as described in the https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/COPYING[COPYING] file included with Linux. All the files copied carry SPDX license identifiers of `GPL-2.0+` or `GPL-2.0-or-later`. Per https://www.gnu.org/licenses/gpl-faq.en.html#AllCompatibility[GNU's compatibility table], GPL 2.0+ code can be used under GPLv3 provided the combination is under GPLv3. Moreover, https://www.gnu.org/licenses/license-list.en.html#AGPLv3.0[GNU's overview of AGPLv3] indicates that its terms "effectively consist of the terms of GPLv3" plus the network-use paragraph. Therefore, the developers have a good-faith belief that licensing these files under AGPLv3 is authorized. (See also https://github.com/Freemius/wordpress-sdk/issues/166#issuecomment-310561976[this issue comment] for another example of a similar situation.) = License Elixir is copyright (c) 2017--2020 its contributors. It is licensed AGPLv3. See the `COPYING` file included with Elixir for details. ================================================ FILE: docker/000-default.conf ================================================ AllowOverride None Require all denied Require all granted AllowOverride None Require all granted ServerName MY_LOCAL_IP DocumentRoot /usr/local/elixir/ SetEnv LXR_PROJ_DIR /srv/elixir-data/ # restart-interval is 12 hours WSGIDaemonProcess Elixir processes=16 threads=1 \ display-name=%{GROUP} restart-interval=43200 \ home=/usr/local/elixir/ python-home=/usr/local/elixir/venv/ WSGIApplicationGroup %{GLOBAL} WSGIProcessGroup Elixir WSGIScriptAliasMatch "^/(?!static/)" /usr/local/elixir/wsgi.py/$1 AllowEncodedSlashes On RewriteEngine on RewriteRule "^/$" "/linux/latest/source" [R] RewriteRule "^/robots.txt$" "/static/robots.txt" [L] RewriteRule "^/favicon.ico$" "/static/img/favicon.ico" [L] ================================================ FILE: docker/Dockerfile ================================================ FROM debian:bookworm AS build RUN \ apt-get update && \ apt-get --no-install-recommends -y install \ git build-essential pkg-config autoconf automake \ python3 python3-pip python3-dev python3-docutils \ libdb-dev \ libseccomp-dev libjansson-dev libyaml-dev libxml2-dev WORKDIR /build-berkeleydb/ # NOTE wheel version MUST be sycnhronized with requirements.txt RUN pip wheel berkeleydb==18.1.10 WORKDIR /build-ctags/ RUN git clone --branch v6.1.0 --depth 1 https://github.com/universal-ctags/ctags.git WORKDIR ctags RUN ./autogen.sh && ./configure && make -j $(nproc) FROM debian:bookworm RUN \ apt-get update && \ apt-get --no-install-recommends -y install \ python3 \ python3-pip \ python3-venv \ libdb5.3 \ perl \ git \ apache2 \ libapache2-mod-wsgi-py3 \ libjansson4 \ libyaml-0-2 \ wget COPY ./requirements.txt /usr/local/elixir/ WORKDIR /usr/local/elixir/ COPY --from=build /build-berkeleydb/berkeleydb-*.whl /tmp/build/ RUN python3 -m venv venv && \ . ./venv/bin/activate && \ pip install /tmp/build/berkeleydb-*.whl && \ pip install -r requirements.txt COPY --from=build /build-ctags/ctags/ctags /usr/bin/ctags RUN mkdir -p /srv/elixir-data/ COPY ./docker/000-default.conf /etc/apache2/sites-available/000-default.conf COPY ./docker/gitconfig /etc/gitconfig RUN a2enmod rewrite EXPOSE 80 ARG ELIXIR_VERSION ENV ELIXIR_VERSION=$ELIXIR_VERSION \ ELIXIR_ROOT=/srv/elixir-data \ PATH="/usr/local/elixir/utils:/usr/local/elixir/venv/bin:$PATH" \ PYTHONUNBUFFERED=1 COPY . /usr/local/elixir/ ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] ================================================ FILE: docker/gitconfig ================================================ [safe] directory = * ================================================ FILE: elixir/__init__.py ================================================ ================================================ FILE: elixir/api.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2019--2020 Carmeli Tamir and contributors. # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import json import os import falcon from urllib import parse import sys from .query import get_query from .lib import validFamily from .web_utils import validate_version class ApiIdentGetterResource: def on_get(self, req, resp, project, ident): version = validate_version(req.get_param('version')) if version is None: raise falcon.HTTPInvalidParam('', 'version') family = req.get_param('family') if not validFamily(family): family = 'C' query = get_query(req.context.config.project_dir, project) if not query: resp.status = falcon.HTTP_NOT_FOUND return if version in ('latest', 'latest-rc'): rc = version == 'latest-rc' version = query.get_latest_tag(rc=rc) symbol_definitions, symbol_references, symbol_doccomments, _ = query.search_ident(version, ident, family) resp.status = falcon.HTTP_200 resp.content_type = falcon.MEDIA_JSON resp.media = { 'definitions': [sym.__dict__ for sym in symbol_definitions], 'references': [sym.__dict__ for sym in symbol_references], 'documentations': [sym.__dict__ for sym in symbol_doccomments] } query.close() ================================================ FILE: elixir/autocomplete.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Maxime Chretien # and contributors. # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import sys import os import json from urllib import parse from berkeleydb.db import DB_SET_RANGE import falcon from .lib import autoBytes, validFamily from .query import get_query from .web_utils import validate_project, validate_ident class AutocompleteResource: def on_get(self, req, resp): ident_prefix = req.get_param('q') family = req.get_param('f') project = req.get_param('p') ident_prefix = validate_ident(ident_prefix) if ident_prefix is None: raise falcon.HTTPInvalidParam('', 'ident') project = validate_project(project) if project is None: raise falcon.HTTPInvalidParam('', 'project') if not validFamily(family): family = 'C' query = get_query(req.context.config.project_dir, project) if not query: resp.status = falcon.HTTP_NOT_FOUND return if family == 'B': # DTS identifiers are stored quoted process = lambda x: parse.unquote(x) db = query.db.comps else: process = lambda x: x db = query.db.defs response = [] i = 0 cur = db.db.cursor() query_bytes = autoBytes(parse.quote(ident_prefix)) # Find "the smallest key greater than or equal to the specified key" # https://docs.oracle.com/cd/E17276_01/html/api_reference/C/dbcget.html # In practice this should mean "the key that starts with provided prefix" # See docs about the default comparison function for B-Tree databases: # https://docs.oracle.com/cd/E17276_01/html/api_reference/C/dbset_bt_compare.html key, _ = cur.get(query_bytes, DB_SET_RANGE) while i <= 10: if key.startswith(query_bytes): # If found key starts with the prefix, add to response # and move to the next key i += 1 response.append(process(key.decode("utf-8"))) key, _ = cur.next() else: # If found key does not start with the prefix, stop break resp.status = falcon.HTTP_200 resp.content_type = falcon.MEDIA_JSON resp.media = response query.close() ================================================ FILE: elixir/data.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Mikaël Bouillot # and contributors # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import berkeleydb import re from . import lib import os import os.path import errno deflist_regex = re.compile(b'(\d*)(\w)(\d*)(\w),?') deflist_macro_regex = re.compile('\dM\d+(\w)') ################################################################################## defTypeR = { 'c': 'config', 'd': 'define', 'e': 'enum', 'E': 'enumerator', 'f': 'function', 'l': 'label', 'M': 'macro', 'm': 'member', 'p': 'prototype', 's': 'struct', 't': 'typedef', 'u': 'union', 'v': 'variable', 'x': 'externvar'} defTypeD = {v: k for k, v in defTypeR.items()} ################################################################################## maxId = 999999999 class DefList: '''Stores associations between a blob ID, a type (e.g., "function"), a line number and a file family. Also stores in which families the ident exists for faster tests.''' def __init__(self, data=b'#'): self.data, self.families = data.split(b'#') def iter(self, dummy=False): # Get all element in a list of sublists and sort them entries = deflist_regex.findall(self.data) entries.sort(key=lambda x:int(x[0])) for id, type, line, family in entries: id = int(id) type = defTypeR [type.decode()] line = int(line) family = family.decode() yield id, type, line, family if dummy: yield maxId, None, None, None def append(self, id, type, line, family): if type not in defTypeD: return p = str(id) + defTypeD[type] + str(line) + family if self.data != b'': p = ',' + p self.data += p.encode() self.add_family(family) def pack(self): return self.data + b'#' + self.families def add_family(self, family): family = family.encode() if not family in self.families.split(b','): if self.families != b'': family = b',' + family self.families += family def get_families(self): return self.families.decode().split(',') def get_macros(self): return deflist_macro_regex.findall(self.data.decode()) or '' class PathList: '''Stores associations between a blob ID and a file path. Inserted by update.py sorted by blob ID.''' def __init__(self, data=b''): self.data = data def iter(self, dummy=False): for p in self.data.split(b'\n')[:-1]: id, path = p.split(b' ',maxsplit=1) id = int(id) path = path.decode() yield id, path if dummy: yield maxId, None def append(self, id, path): p = str(id).encode() + b' ' + path + b'\n' self.data += p def pack(self): return self.data class RefList: '''Stores a mapping from blob ID to list of lines and the corresponding family.''' def __init__(self, data=b''): self.data = data def iter(self, dummy=False): # Split all elements in a list of sublists and sort them entries = [x.split(b':') for x in self.data.split(b'\n')[:-1]] entries.sort(key=lambda x:int(x[0])) for b, c, d in entries: b = int(b.decode()) c = c.decode() d = d.decode() yield b, c, d if dummy: yield maxId, None, None def append(self, id, lines, family): p = str(id) + ':' + lines + ':' + family + '\n' self.data += p.encode() def pack(self): return self.data class BsdDB: def __init__(self, filename, readonly, contentType, shared=False): self.filename = filename self.db = berkeleydb.db.DB() flags = berkeleydb.db.DB_THREAD if shared else 0 if readonly: flags |= berkeleydb.db.DB_RDONLY self.db.open(filename, flags=flags) else: flags |= berkeleydb.db.DB_CREATE self.db.open(filename, flags=flags, mode=0o644, dbtype=berkeleydb.db.DB_BTREE) self.ctype = contentType def exists(self, key): key = lib.autoBytes(key) return self.db.exists(key) def get(self, key): key = lib.autoBytes(key) p = self.db.get(key) return self.ctype(p) if p is not None else None def get_keys(self): return self.db.keys() def put(self, key, val, sync=False): key = lib.autoBytes(key) val = lib.autoBytes(val) if type(val) is not bytes: val = val.pack() self.db.put(key, val) if sync: self.db.sync() def close(self): self.db.close() def __len__(self): return self.db.stat()["nkeys"] class DB: def __init__(self, dir, readonly=True, dtscomp=False, shared=False): if os.path.isdir(dir): self.dir = dir else: raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), dir) ro = readonly self.vars = BsdDB(dir + '/variables.db', ro, lambda x: int(x.decode()), shared=shared) # Key-value store of basic information self.blob = BsdDB(dir + '/blobs.db', ro, lambda x: int(x.decode()), shared=shared) # Map hash to sequential integer serial number self.hash = BsdDB(dir + '/hashes.db', ro, lambda x: x, shared=shared) # Map serial number back to hash self.file = BsdDB(dir + '/filenames.db', ro, lambda x: x.decode(), shared=shared) # Map serial number to filename self.vers = BsdDB(dir + '/versions.db', ro, PathList, shared=shared) self.defs = BsdDB(dir + '/definitions.db', ro, DefList, shared=shared) self.defs_cache = {} NOOP = lambda x: x self.defs_cache['C'] = BsdDB(dir + '/definitions-cache-C.db', ro, NOOP, shared=shared) self.defs_cache['K'] = BsdDB(dir + '/definitions-cache-K.db', ro, NOOP, shared=shared) self.defs_cache['D'] = BsdDB(dir + '/definitions-cache-D.db', ro, NOOP, shared=shared) self.defs_cache['M'] = BsdDB(dir + '/definitions-cache-M.db', ro, NOOP, shared=shared) assert sorted(self.defs_cache.keys()) == sorted(lib.CACHED_DEFINITIONS_FAMILIES) self.refs = BsdDB(dir + '/references.db', ro, RefList, shared=shared) self.docs = BsdDB(dir + '/doccomments.db', ro, RefList, shared=shared) self.dtscomp = dtscomp if dtscomp: self.comps = BsdDB(dir + '/compatibledts.db', ro, RefList, shared=shared) self.comps_docs = BsdDB(dir + '/compatibledts_docs.db', ro, RefList, shared=shared) # Use a RefList in case there are multiple doc comments for an identifier def close(self): self.vars.close() self.blob.close() self.hash.close() self.file.close() self.vers.close() self.defs.close() self.defs_cache['C'].close() self.defs_cache['K'].close() self.defs_cache['D'].close() self.defs_cache['M'].close() self.refs.close() self.docs.close() if self.dtscomp: self.comps.close() self.comps_docs.close() ================================================ FILE: elixir/filters/__init__.py ================================================ from typing import List from .utils import Filter, FilterContext from .projects import project_filters, default_filters # Returns a list of applicable filters for project_name under provided filter context def get_filters(ctx: FilterContext, project_name: str) -> List[Filter]: filter_classes = project_filters.get(project_name, default_filters) filters = [] for filter_cls in filter_classes: if type(filter_cls) == tuple and len(filter_cls) == 2: cls, kwargs = filter_cls filters.append(cls(**kwargs)) elif type(filter_cls) == type: filters.append(filter_cls()) else: raise ValueError(f"Invalid filter: {filter_cls}, " \ "should be either a two element tuple or a type. " \ "Make sure project_filters in project.py is valid.") return [f for f in filters if f.check_if_applies(ctx)] ================================================ FILE: elixir/filters/configin.py ================================================ import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for Config.in includes # source "path/file" # Example: uclibc-ng/v1.0.47/source/extra/Configs/Config.in#L176 class ConfigInFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.configin = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Config'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_configin(m): self.configin.append(m.group(4)) return f'{ m.group(1) }{ m.group(2) }{ m.group(3) }"__KEEPCONFIGIN__{ encode_number(len(self.configin)) }"' return re.sub('^(\s*)(source)(\s*)\"(.*)\"', keep_configin, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_configin(m): w = self.configin[decode_number(m.group(1)) - 1] return f'{ w }' return re.sub('__KEEPCONFIGIN__([A-J]+)', replace_configin, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/cppinc.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number, extension_matches # Filters for cpp includes like these: # #include "file" # Example: musl/v1.2.5/source/src/dirent/dirfd.c#L2 # #include "__dirent.h" class CppIncFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.cppinc = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ extension_matches(ctx.filepath, {'dts', 'dtsi', 'c', 'cc', 'cpp', 'c++', 'cxx', 'h', 's'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_cppinc(m): self.cppinc.append(m.group(3)) return f'{ m.group(1) }#include{ m.group(2) }"__KEEPCPPINC__{ encode_number(len(self.cppinc)) }"' return re.sub('^(\s*)#include(\s*)\"(.*?)\"', keep_cppinc, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_cppinc(m): w = self.cppinc[decode_number(m.group(1)) - 1] url = ctx.get_relative_source_url(w) return f'{ w }' return re.sub('__KEEPCPPINC__([A-J]+)', replace_cppinc, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/cpppathinc.py ================================================ import re from typing import List from .utils import Filter, FilterContext, encode_number, decode_number, extension_matches # Filters for cpp includes like these: # #include # Such filters work typically for standalone projects (like kernels and bootloaders) # If we make references to other projects, we could # end up with links to headers which are outside the project # Example: u-boot/v2023.10/source/env/embedded.c#L16 # prefix_path: a list of paths, will be used to replace the prefix path during the # untransform_formatted_code step class CppPathIncFilter(Filter): def __init__(self, prefix_path: List[str] = ["include"], *args, **kwargs): self.prefix_path = prefix_path super().__init__(*args, **kwargs) self.cpppathinc = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ extension_matches(ctx.filepath, {'dts', 'dtsi', 'c', 'cc', 'cpp', 'c++', 'cxx', 'h', 's'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_cpppathinc(m): m1 = m.group(1) m2 = m.group(2) inc = m.group(3) if re.match('^asm/.*', inc): # Keep the original string in case the path contains "asm/" # Because there are then multiple include possibilites, one per architecture return m.group(0) else: self.cpppathinc.append(inc) return f'{ m1 }#include{ m2 }<__KEEPCPPPATHINC__{ encode_number(len(self.cpppathinc)) }>' return re.sub('^(\s*)#include(\s*)<(.*?)>', keep_cpppathinc, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_cpppathinc(m): w = self.cpppathinc[decode_number(m.group(1)) - 1] for p in self.prefix_path: path = f"/{p}/{w}" if ctx.query.file_exists(ctx.tag, path): return f'{ w }' return w return re.sub('__KEEPCPPPATHINC__([A-J]+)', replace_cpppathinc, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/defconfig.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number, extension_matches # Filter for kconfig identifier in defconfigs # Replaces defconfig identifiers with links to definitions/references # `CONFIG_OPTION=y` # Example: u-boot/v2023.10/source/configs/A13-OLinuXino_defconfig#L1 class DefConfigIdentsFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.defconfigidents = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ ctx.filepath.endswith('defconfig') def transform_raw_code(self, ctx, code: str) -> str: def keep_defconfigidents(m): self.defconfigidents.append(m.group(1)) return '__KEEPDEFCONFIGIDENTS__' + encode_number(len(self.defconfigidents)) return re.sub('(CONFIG_[\w]+)', keep_defconfigidents, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_defconfigidents(m): i = self.defconfigidents[decode_number(m.group(1)) - 1] return f'{ i }' return re.sub('__KEEPDEFCONFIGIDENTS__([A-J]+)', replace_defconfigidents, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/dtscompcode.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number, extension_matches # Filter for DT compatible strings in code (C family) files # Finds assigments to properties and variables named 'compatible' and recognized by # Query.get_tokenized_file() # .compatible = "device" # Example: u-boot/v2023.10/source/drivers/phy/nop-phy.c#L84 class DtsCompCodeFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.dtscompC = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ ctx.query.dts_comp_support and \ extension_matches(ctx.filepath, {'c', 'cc', 'cpp', 'c++', 'cxx', 'h', 's'}) def transform_raw_code(self, ctx, code: str) -> str: # quit early if source file does not contain any strings that could be an assignment to a 'compatible' property # this is much faster than the match-and-replace regex, especially for big files compatible_search = re.search('\.(\033\[31m)?compatible(\033\[0m)?\s*=', code, flags=re.MULTILINE) if compatible_search is None: return code def keep_dtscompC(m): self.dtscompC.append(m.group(4)) return f'{ m.group(1) }"__KEEPDTSCOMPC__{ encode_number(len(self.dtscompC)) }"' return re.sub('(\s*{*\s*\.(\033\[31m)?compatible(\033\[0m)?\s*=\s*)\"(.+?)\"', keep_dtscompC, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_dtscompC(m): i = self.dtscompC[decode_number(m.group(1)) - 1] return f'{ i }' return re.sub('__KEEPDTSCOMPC__([A-J]+)', replace_dtscompC, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/dtscompdocs.py ================================================ import re from urllib.parse import quote from .utils import Filter, FilterContext, encode_number, decode_number # Filter for DT compatible strings in documentation (B family) files # syscon # Example: linux/v6.9.4/source/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml#L17 # Note that this also finds strings in comments, descriptions and other potentially unrelated properties class DtsCompDocsFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.dtscompB = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ ctx.query.dts_comp_support and \ ctx.filepath.startswith('/Documentation/devicetree/bindings') def transform_raw_code(self, ctx, code: str) -> str: def keep_dtscompB(m): text = m.group(1) if ctx.query.dts_comp_exists(quote(text)): self.dtscompB.append(text) return f'__KEEPDTSCOMPB__{ encode_number(len(self.dtscompB)) }' else: return m.group(0) return re.sub('([\w-]+,?[\w-]+)', keep_dtscompB, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_dtscompB(m): i = self.dtscompB[decode_number(m.group(1)) - 1] return f'{ i }' return re.sub('__KEEPDTSCOMPB__([A-J]+)', replace_dtscompB, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/dtscompdts.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number, extension_matches # Filter for DT compatible strings in DTS (D family) files # compatible = "device" # Example: u-boot/v2023.10/source/arch/arm/dts/ac5-98dx35xx-rd.dts#L37 class DtsCompDtsFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.dtscompD = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ ctx.query.dts_comp_support and \ extension_matches(ctx.filepath, {'dts', 'dtsi'}) def transform_raw_code(self, ctx, code: str) -> str: def sub_func(m): match = m.group(0) strings = re.findall("\"(.+?)\"", m.group(1)) for string in strings: self.dtscompD.append(string) match = match.replace(string, '__KEEPDTSCOMPD__' + encode_number(len(self.dtscompD))) return match return re.sub('\s*compatible(.*?)$', sub_func, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_dtscompD(m): i = self.dtscompD[decode_number(m.group(1)) - 1] return f'{ i }' return re.sub('__KEEPDTSCOMPD__([A-J]+)', replace_dtscompD, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/dtsi.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number, extension_matches # Filters for dts includes as follows: # Replaces include directives in dts/dtsi files with links to source # /include/ "file" # Example: u-boot/v2023.10/source/arch/powerpc/dts/t1023si-post.dtsi#L12 class DtsiFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.dtsi = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ extension_matches(ctx.filepath, {'dts', 'dtsi'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_dtsi(m): self.dtsi.append(m.group(3)) return f'{ m.group(1) }/include/{ m.group(2) }"__KEEPDTSI__{ encode_number(len(self.dtsi)) }"' return re.sub('^(\s*)/include/(\s*)\"(.*?)\"', keep_dtsi, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_dtsi(m): w = self.dtsi[decode_number(m.group(1)) - 1] return f'{ w }' return re.sub('__KEEPDTSI__([A-J]+)', replace_dtsi, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/ident.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number # Filter for identifier links # Replaces identifiers marked by Query.get_tokenized_file() with links to ident page. # If Query.get_tokenized_file() detects that a file belongs to a family that can contain # indexed identifiers, it processes the file by adding unprintable markers # ('\033[31m' + token + b'\033[0m') to tokens that have an entry in the definitions # database. This filter replaces these marked tokens with links to their ident pages, # unless the token starts with CONFIG_ - these tokens are handled by the Kconfig filter. class IdentFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.idents = [] def transform_raw_code(self, ctx, code: str) -> str: def sub_func(m): self.idents.append(m.group(1)) return '__KEEPIDENTS__' + encode_number(len(self.idents)) return re.sub('\033\[31m(?!CONFIG_)(.*?)\033\[0m', sub_func, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def sub_func(m): i = self.idents[decode_number(m.group(2)) - 1] link = f'{ i }' return str(m.group(1) or '') + link return re.sub('__(<.+?>)?KEEPIDENTS__([A-J]+)', sub_func, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/kconfig.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number, filename_without_ext_matches # Filters for Kconfig includes # Replaces KConfig includes (source keyword) with links to included files # `source "path/file"` # Example: u-boot/v2023.10/source/Kconfig#L10 class KconfigFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.kconfig = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Kconfig'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_kconfig(m): self.kconfig.append(m.group(4)) return f'{ m.group(1) }{ m.group(2) }{ m.group(3) }"__KEEPKCONFIG__{ encode_number(len(self.kconfig)) }"' return re.sub('^(\s*)(source)(\s*)\"([\w/_\.-]+)\"', keep_kconfig, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_kconfig(m): w = self.kconfig[decode_number(m.group(1)) - 1] return f'{ w }' return re.sub('__KEEPKCONFIG__([A-J]+)', replace_kconfig, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/kconfigidents.py ================================================ import re from .utils import Filter, FilterContext, encode_number, decode_number # Filter for kconfig identifier links # Replaces KConfig identifiers with links to definitions and references # `config OPTION` # Example: u-boot/v2023.10/source/Kconfig#L17 # Note: Prepends identifier with CONFIG_ class KconfigIdentsFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.kconfigidents = [] def transform_raw_code(self, ctx, code: str) -> str: def keep_kconfigidents(m): self.kconfigidents.append(m.group(1)) return f'__KEEPKCONFIGIDENTS__{ encode_number(len(self.kconfigidents)) }' return re.sub('\033\[31m(?=CONFIG_)(.*?)\033\[0m', keep_kconfigidents, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_kconfigidents(m): i = self.kconfigidents[decode_number(m.group(2)) - 1] n = i #Remove the CONFIG_ when we are in a Kconfig file if ctx.family == 'K': n = n[7:] return f'{ m.group(1) or "" }{ n }' return re.sub('__(<.+?>)?KEEPKCONFIGIDENTS__([A-J]+)', replace_kconfigidents, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/makefiledir.py ================================================ from os.path import dirname import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for Makefile directory includes as follows: # obj-$(VALUE) += dir/ # Example: u-boot/v2023.10/source/Makefile#L867 class MakefileDirFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.makefiledir = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Makefile'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_makefiledir(m): filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' if ctx.query.file_exists(ctx.tag, filedir + m.group(1) + '/Makefile'): self.makefiledir.append(m.group(1)) return f'__KEEPMAKEFILEDIR__{ encode_number(len(self.makefiledir)) }/{ m.group(2) }' else: return m.group(0) return re.sub('(?<=\s)([-\w/]+)/(\s+|$)', keep_makefiledir, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_makefiledir(m): w = self.makefiledir[decode_number(m.group(1)) - 1] filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' fpath = f'{ filedir }{ w }/Makefile' return f'{ w }/' return re.sub('__KEEPMAKEFILEDIR__([A-J]+)/', replace_makefiledir, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/makefiledtb.py ================================================ from os.path import dirname import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for Makefile file includes like these: # dtb-y += file.dtb # Example: u-boot/v2023.10/source/Makefile#L992 class MakefileDtbFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.makefiledtb = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Makefile'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_makefiledtb(m): self.makefiledtb.append(m.group(1)) return f'__KEEPMAKEFILEDTB__{ encode_number(len(self.makefiledtb)) }.dtb' return re.sub('(?<=\s)([-\w/+\.]+)\.dtb', keep_makefiledtb, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_makefiledtb(m): w = self.makefiledtb[decode_number(m.group(1)) - 1] filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' npath = f'{ filedir }{ w }.dts' return f'{ w }.dtb' return re.sub('__KEEPMAKEFILEDTB__([A-J]+)\.dtb', replace_makefiledtb, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/makefilefile.py ================================================ from os.path import dirname import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for files listed in Makefiles # path/file # Example: u-boot/v2023.10/source/Makefile#L1509 class MakefileFileFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.makefilefile = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Makefile'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_makefilefile(m): filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' if ctx.query.file_exists(ctx.tag, filedir + m.group(1)): self.makefilefile.append(m.group(1)) return f'__KEEPMAKEFILEFILE__{ encode_number(len(self.makefilefile)) }{ m.group(2) }' else: return m.group(0) return re.sub('(?:(?<=\s|=)|(?<=-I))(?!/)([-\w/]+/[-\w\.]+)(\s+|\)|$)', keep_makefilefile, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_makefilefile(m): w = self.makefilefile[decode_number(m.group(1)) - 1] filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' npath = filedir + w return f'{ w }' return re.sub('__KEEPMAKEFILEFILE__([A-J]+)', replace_makefilefile, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/makefileo.py ================================================ from os.path import dirname import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for Makefile file includes like these: # file.o # Example: u-boot/v2023.10/source/Makefile#L1767 class MakefileOFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.makefileo = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Makefile'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_makefileo(m): self.makefileo.append(m.group(1)) return f'__KEEPMAKEFILEO__{ encode_number(len(self.makefileo)) }.o' return re.sub('(?<=\s)([-\w/]+)\.o(?!\w)(?! :?=)', keep_makefileo, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_makefileo(m): w = self.makefileo[decode_number(m.group(1)) - 1] filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' npath = f'{ filedir }{ w }.c' return f'{ w }.o' return re.sub('__KEEPMAKEFILEO__([A-J]+)\.o', replace_makefileo, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/makefilesrctree.py ================================================ import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for files listed in Makefiles using $(srctree) # $(srctree)/Makefile # Example: u-boot/v2023.10/source/Makefile#L1983 class MakefileSrcTreeFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.makefilesrctree = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Makefile'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_makefilesrctree(m): if ctx.query.file_exists(ctx.tag, '/' + m.group(1)): self.makefilesrctree.append(m.group(1)) return f'__KEEPMAKEFILESRCTREE__{ encode_number(len(self.makefilesrctree)) }{ m.group(2) }' else: return m.group(0) return re.sub('(?:(?<=\s|=)|(?<=-I))(?!/)\$\(srctree\)/((?:[-\w/]+/)?[-\w\.]+)(\s+|\)|$)', keep_makefilesrctree, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_makefilesrctree(m): w = self.makefilesrctree[decode_number(m.group(1)) - 1] url = ctx.get_absolute_source_url(w) return f'$(srctree)/{ w }' return re.sub('__KEEPMAKEFILESRCTREE__([A-J]+)', replace_makefilesrctree, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/makefilesubdir.py ================================================ from os.path import dirname import re from .utils import Filter, FilterContext, decode_number, encode_number, filename_without_ext_matches # Filters for Makefile directory includes as follows: # subdir-y += dir # Example: u-boot/v2023.10/source/examples/Makefile#L9 class MakefileSubdirFilter(Filter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.makefilesubdir = [] def check_if_applies(self, ctx) -> bool: return super().check_if_applies(ctx) and \ filename_without_ext_matches(ctx.filepath, {'Makefile'}) def transform_raw_code(self, ctx, code: str) -> str: def keep_makefilesubdir(m): self.makefilesubdir.append(m.group(5)) n = encode_number(len(self.makefilesubdir)) return f'{ m.group(1) }{ m.group(2) }{ m.group(3) }{ m.group(4) }__KEEPMAKESUBDIR__{ n }{ m.group(6) }' return re.sub('(subdir-y)(\s+)(\+=|:=)(\s+)([-\w]+)(\s*|$)', keep_makefilesubdir, code, flags=re.MULTILINE) def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: def replace_makefilesubdir(m): w = self.makefilesubdir[decode_number(m.group(1)) - 1] filedir = dirname(ctx.filepath) if filedir != '/': filedir += '/' npath = f'{ filedir }{ w }/Makefile' return f'{ w }' return re.sub('__KEEPMAKESUBDIR__([A-J]+)', replace_makefilesubdir, html, flags=re.MULTILINE) ================================================ FILE: elixir/filters/projects.py ================================================ from .ident import IdentFilter from .cppinc import CppIncFilter from .cpppathinc import CppPathIncFilter from .defconfig import DefConfigIdentsFilter from .configin import ConfigInFilter from .kconfig import KconfigFilter from .kconfigidents import KconfigIdentsFilter from .dtsi import DtsiFilter from .dtscompdocs import DtsCompDocsFilter from .dtscompcode import DtsCompCodeFilter from .dtscompdts import DtsCompDtsFilter from .makefileo import MakefileOFilter from .makefiledtb import MakefileDtbFilter from .makefiledir import MakefileDirFilter from .makefilesubdir import MakefileSubdirFilter from .makefilefile import MakefileFileFilter from .makefilesrctree import MakefileSrcTreeFilter from .makefilesubdir import MakefileSubdirFilter # List of filters applied to all projects default_filters = [ DtsCompCodeFilter, DtsCompDtsFilter, DtsCompDocsFilter, IdentFilter, CppIncFilter, ] # List of filters for Kconfig files common_kconfig_filters = [ KconfigFilter, KconfigIdentsFilter, DefConfigIdentsFilter, ] # List of filters for Makefiles common_makefile_filters = [ MakefileOFilter, MakefileDtbFilter, MakefileDirFilter, MakefileFileFilter, MakefileSubdirFilter, MakefileSrcTreeFilter, ] # Dictionary of custom per-projects filters. # Projects not present in this dictionary only use default_filters. # Use `*` to unpack filter lists defined above, # you can pass additional options to filters by putting a Filter # class and a dictionary with options in a tuple, like this: # (FilterCls, {"option": True}). # Check filter files and utils.py for information about available options project_filters = { 'amazon-freertos': [ *default_filters, MakefileSubdirFilter, ], 'arm-trusted-firmware': [ *default_filters, CppPathIncFilter, ], 'barebox': [ *default_filters, DtsiFilter, *common_kconfig_filters, CppPathIncFilter, *common_makefile_filters, ], 'coreboot': [ *default_filters, DtsiFilter, *common_kconfig_filters, *common_makefile_filters, ], 'iproute2': [ *default_filters, *common_makefile_filters, ], 'linux': [ *default_filters, DtsiFilter, *common_kconfig_filters, *common_makefile_filters, # include/uapi contains includes to user headers under #ifndef __KERNEL__ # Our solution is to ignore all includes in such paths (CppPathIncFilter, {"path_exceptions": {'^/include/uapi/.*'}}), ], 'opensbi': [ *default_filters, *common_kconfig_filters, ], 'qemu': [ *default_filters, *common_kconfig_filters, ], 'u-boot': [ *default_filters, DtsiFilter, *common_kconfig_filters, CppPathIncFilter, *common_makefile_filters, ], 'uclibc-ng': [ *default_filters, ConfigInFilter, ], 'vpp': [ *default_filters, (CppPathIncFilter, {"prefix_path": ['src', 'src/plugins', 'src/vpp-api', 'src/vpp-api/vapi']}), MakefileFileFilter, ], 'zephyr': [ *default_filters, DtsiFilter, *common_kconfig_filters, CppPathIncFilter, ], } ================================================ FILE: elixir/filters/utils.py ================================================ import re import os from dataclasses import dataclass from typing import Callable, List from ..query import Query # Context data used by Filters # tag: browsed version, unqoted # family: family of file # path: path of file # get_ident_url: function that returns URL to identifier passed as argument # get_absolute_source_url: function that returns a URL to file with absolute path passed as an argument # get_relative_source_url: function that returns a URL to file in directory of current file @dataclass class FilterContext: query: Query tag: str family: str filepath: str get_ident_url: Callable[[str], str] get_absolute_source_url: Callable[[str], str] get_relative_source_url: Callable[[str], str] # Filter interface/base class # Filters are used to add extra information, like links, to code formatted into HTML by Pygments. # Filters consist of two parts: the first part runs on unformatted code, transforming it # to mark interesting identifiers, for example keywords. How the identifiers are marked is # up to the filter, but it's important to be careful to not break formatting. # The second part runs on HTML, replacing markings left by the first part with HTML code. # path_exceptions: list of regexes, disables filter if path of the filtered file matches a regex from the list class Filter: def __init__(self, path_exceptions: List[str] = []): self.path_exceptions = path_exceptions # Return True if filter can be applied to file with path def check_if_applies(self, ctx: FilterContext) -> bool: for p in self.path_exceptions: if re.match(p, ctx.filepath): return False return True # Add information required by filter by transforming raw source code. # Known identifiers are marked by '\033[31m' and '\033[0m'. Note that these marked # identifiers are usually handled by IdentFilter or KconfigIdentsFilter. def transform_raw_code(self, ctx: FilterContext, code: str) -> str: return code # Replace information left by `transform_raw_code` with target HTML # html: HTML output from code formatter def untransform_formatted_code(self, ctx: FilterContext, html: str) -> str: return html # Returns true if filename from filepath, with removed extension, is in the # allowed_filenames_without_ext iterable def filename_without_ext_matches(filepath: str, allowed_filenames_without_ext) -> bool: filename = os.path.basename(filepath) filename_without_ext, _ = os.path.splitext(filename) return filename_without_ext in allowed_filenames_without_ext # Returns true if extension of filename from filepath is in the # allowed_extensions iterable def extension_matches(filepath: str, allowed_extensions) -> bool: _, file_ext_dot = os.path.splitext(filepath) file_ext = file_ext_dot[1:].lower() return file_ext in allowed_extensions # Encodes an integer into a string of characters (A-J) # encode_number(10239) = 'BACDJ' def encode_number(number): result = '' while number != 0: number, rem = divmod(number, 10) rem = chr(ord('A') + rem) result = rem + result return result # Decodes a string of characters returned by encode_number into an integer # decode_number('BACDJ') = 10239 def decode_number(string): result = '' while string != '': string, char = string[:-1], string[-1] char = str(ord(char) - ord('A')) result = char + result return int(result) ================================================ FILE: elixir/lib.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017 Mikaël Bouillot # # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import sys import logging import subprocess, os logger = logging.getLogger(__name__) CURRENT_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../') def script(*args, env=None): args = (os.path.join(CURRENT_DIR, 'script.sh'),) + args # subprocess.run was introduced in Python 3.5 # fall back to subprocess.check_output if it's not available if hasattr(subprocess, 'run'): p = subprocess.run(args, stdout=subprocess.PIPE, env=env) p = p.stdout else: p = subprocess.check_output(args) return p def run_cmd(*args, env=None): p = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) if len(p.stderr) != 0: logger.error('command %s printed to stderr: \n%s', str(args), p.stderr.decode('utf-8')) return p.stdout, p.returncode # Invoke ./script.sh with the given arguments # Returns the list of output lines def scriptLines(*args, env=None): p = script(*args, env=env) p = p.split(b'\n') del p[-1] return p def unescape(bstr): subs = ( ('\1','\n'), ) for a,b in subs: a = a.encode() b = b.encode() bstr = bstr.replace(a, b) return bstr def decode(byte_object): # decode('ascii') fails on special chars # FIXME: major hack until we handle everything as bytestrings try: return byte_object.decode('utf-8') except UnicodeDecodeError: return byte_object.decode('iso-8859-1') # List of tokens which we don't want to consider as identifiers # Typically for very frequent variable names and things redefined by #define # TODO: allow to have per project blacklists blacklist = ( b'NULL', b'__', b'adapter', b'addr', b'arg', b'attr', b'base', b'bp', b'buf', b'buffer', b'c', b'card', b'char', b'chip', b'cmd', b'codec', b'const', b'count', b'cpu', b'ctx', b'data', b'default', b'define', b'desc', b'dev', b'driver', b'else', b'end', b'endif', b'entry', b'err', b'error', b'event', b'extern', b'failed', b'flags', b'h', b'host', b'hw', b'i', b'id', b'idx', b'if', b'index', b'info', b'inline', b'int', b'irq', b'j', b'len', b'length', b'list', b'lock', b'long', b'mask', b'mode', b'msg', b'n', b'name', b'net', b'next', b'offset', b'ops', b'out', b'p', b'pdev', b'port', b'priv', b'ptr', b'q', b'r', b'rc', b'rdev', b'reg', b'regs', b'req', b'res', b'result', b'ret', b'return', b'retval', b'root', b's', b'sb', b'size', b'sizeof', b'sk', b'skb', b'spec', b'start', b'state', b'static', b'status', b'struct', b't', b'tmp', b'tp', b'type', b'val', b'value', b'vcpu', b'x' ) def isIdent(bstr): if (len(bstr) < 2 or bstr in blacklist or bstr.startswith(b'~')): return False else: return True def autoBytes(arg): if type(arg) is str: arg = arg.encode() elif type(arg) is int: arg = str(arg).encode() return arg def getDataDir(): try: return os.environ['LXR_DATA_DIR'] except KeyError: print(sys.argv[0] + ': LXR_DATA_DIR needs to be set') exit(1) def getRepoDir(): try: return os.environ['LXR_REPO_DIR'] except KeyError: print(sys.argv[0] + ': LXR_REPO_DIR needs to be set') exit(1) def currentProject(): return os.path.basename(os.path.dirname(getDataDir())) # List all families supported by Elixir families = ['A', 'B', 'C', 'D', 'K', 'M'] # Those families have databases that cache the content of definitions.db. # This allows faster lookup. CACHED_DEFINITIONS_FAMILIES = ['C', 'K', 'D', 'M'] def validFamily(family): return family in families def getFileFamily(filename): name, ext = os.path.splitext(filename) name, ext = name.lower(), ext.lower() if ext in ['.c', '.cc', '.cpp', '.c++', '.cxx', '.h', '.s'] : return 'C' # C file family and ASM elif ext in ['.dts', '.dtsi'] : return 'D' # Devicetree files elif name[:7] == 'kconfig' and ext != '.rst': # Some files are named like Kconfig-nommu so we only check the first 7 letters # We also exclude documentation files that can be named kconfig return 'K' # Kconfig files elif name[:8] == 'makefile' and ext != '.rst' or ext == '.mk': return 'M' # Makefiles else : return None # 1 char values are file families # 2 chars values with a M are macros families compatibility_list = { 'C' : ['C', 'K'], 'K' : ['K'], 'D' : ['D', 'CM'], 'M' : ['K'] } # Check if families are compatible # First argument can be a list of different families # Second argument is the key for choosing the right array in the compatibility list def compatibleFamily(file_family, requested_family): return any(item in file_family for item in compatibility_list[requested_family]) # Check if a macro is compatible with the requested family # First argument can be a list of different families # Second argument is the key for choosing the right array in the compatibility list def compatibleMacro(macro_family, requested_family): result = False for item in macro_family: item += 'M' result = result or item in compatibility_list[requested_family] return result ================================================ FILE: elixir/query.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Mikaël Bouillot # and contributors # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . from .lib import script, scriptLines, decode from . import lib from . import data import os from collections import OrderedDict from urllib import parse from io import BytesIO class SymbolInstance(object): def __init__(self, path, line, type=None): self.path = path self.line = line self.type = type def __repr__(self): type_repr = "" if self.type: type_repr = f" , type: {self.type}" return f"Symbol in path: {self.path}, line: {self.line}" + type_repr def __str__(self): return self.__repr__() # Returns a Query class instance or None if project data directory does not exist # basedir: absolute path to parent directory of all project data directories, ex. "/srv/elixir-data/" # project: name of the project, directory in basedir, ex. "linux" def get_query(basedir, project): datadir = basedir + '/' + project + '/data' repodir = basedir + '/' + project + '/repo' if not os.path.exists(datadir) or not os.path.exists(repodir): return None return Query(datadir, repodir) class Query: def __init__(self, data_dir, repo_dir): self.repo_dir = repo_dir self.data_dir = data_dir self.dts_comp_support = int(self.script('dts-comp')) self.db = data.DB(data_dir, readonly=True, dtscomp=self.dts_comp_support) self.file_cache = {} def script(self, *args): return script(*args, env=self.getEnv()) def scriptLines(self, *args): return scriptLines(*args, env=self.getEnv()) def getEnv(self): return { **os.environ, "LXR_REPO_DIR": self.repo_dir, "LXR_DATA_DIR": self.data_dir, } def close(self): self.db.close() # Check if a dts compatible string exists def dts_comp_exists(self, ident): if self.dts_comp_support: return self.db.comps.exists(ident) else: return False # Returns True if file exists def file_exists(self, version, path): if version not in self.file_cache: version_cache = set() last_dir = None for _, path in self.db.vers.get(version).iter(): dirname, filename = os.path.split(path) if dirname != last_dir: last_dir = dirname version_cache.add(dirname) version_cache.add(path) self.file_cache[version] = version_cache return path.strip('/') in self.file_cache[version] # Returns the contents of the specified file # Tokens are marked for further processing # Example: v3.1-rc10 /Makefile def get_tokenized_file(self, version, path): filename = os.path.basename(path) family = lib.getFileFamily(filename) if family != None: assert family in lib.CACHED_DEFINITIONS_FAMILIES, f"family {family} must have its definitions cached" buffer = BytesIO() tokens = self.scriptLines('tokenize-file', version, path, family) even = True prefix = b'' if family == 'K': prefix = b'CONFIG_' for tok in tokens: even = not even tok2 = prefix + tok if even and self.db.defs_cache[family].exists(tok2): tok = b'\033[31m' + tok2 + b'\033[0m' else: tok = lib.unescape(tok) buffer.write(tok) return decode(buffer.getvalue()) else: return decode(self.script('get-file', version, path)) # Returns the contents (trees or blobs) of the specified directory # Example: v3.1-rc10 /arch def get_dir_contents(self, version, path): entries_str = decode(self.script('get-dir', version, path)) return entries_str.split("\n")[:-1] # Returns indexed versions, as a tree of OrderedDict. # It has a depth of 3, for example: v3 v3.1 v3.1-rc10. def get_versions(self): versions = OrderedDict() for line in self.scriptLines('list-tags', '-h'): taginfo = decode(line).split(' ') num = len(taginfo) topmenu, submenu = 'FIXME', 'FIXME' if num == 1: tag, = taginfo elif num == 2: submenu, tag = taginfo elif num == 3: topmenu, submenu, tag = taginfo else: raise Exception("unexpected number of fields in taginfo") if self.db.vers.exists(tag): if topmenu not in versions: versions[topmenu] = OrderedDict() if submenu not in versions[topmenu]: versions[topmenu][submenu] = [] versions[topmenu][submenu].append(tag) return versions # Returns the type (blob or tree) associated to # the given path. Example: # > ./query.py type v3.1-rc10 /Makefile # blob # > ./query.py type v3.1-rc10 /arch # tree def get_file_type(self, version, path): return decode(self.script('get-type', version, path)).strip() # Returns identifier search results def search_ident(self, version, ident, family): # DT bindings compatible strings are handled differently if family == 'B': return self.get_idents_comps(version, ident) else: return self.get_idents_defs(version, ident, family) # Returns the latest tag that is included in the database. # This excludes release candidates if `rc` is False. def get_latest_tag(self, rc): if rc: sorted_tags = reversed(self.scriptLines('list-tags')) else: sorted_tags = self.scriptLines('get-latest-tags') for tag in sorted_tags: if self.db.vers.exists(tag): return tag.decode() # return the oldest tag, even if it does not exist in the database return sorted_tags[-1].decode() def get_file_raw(self, version, path): return decode(self.script('get-file', version, path)) def get_idents_comps(self, version, ident): # DT bindings compatible strings are handled differently # They are defined in C files # Used in DT files # Documented in documentation files symbol_c = [] symbol_dts = [] symbol_docs = [] # DT compatible strings are quoted in the database ident = parse.quote(ident) if not self.dts_comp_support or not self.db.comps.exists(ident): return symbol_c, symbol_dts, symbol_docs, False files_this_version = self.db.vers.get(version).iter() comps = self.db.comps.get(ident).iter(dummy=True) if self.db.comps_docs.exists(ident): comps_docs = self.db.comps_docs.get(ident).iter(dummy=True) else: comps_docs = data.RefList().iter(dummy=True) comps_idx, comps_lines, comps_family = next(comps) comps_docs_idx, comps_docs_lines, comps_docs_family = next(comps_docs) compsCBuf = [] # C/CPP/ASM files compsDBuf = [] # DT files compsBBuf = [] # DT bindings docs files for file_idx, file_path in files_this_version: while comps_idx < file_idx: comps_idx, comps_lines, comps_family = next(comps) while comps_docs_idx < file_idx: comps_docs_idx, comps_docs_lines, comps_docs_family = next(comps_docs) if comps_idx == file_idx: if comps_family == 'C': compsCBuf.append((file_path, comps_lines)) elif comps_family == 'D': compsDBuf.append((file_path, comps_lines)) if comps_docs_idx == file_idx: compsBBuf.append((file_path, comps_docs_lines)) for path, cline in sorted(compsCBuf): symbol_c.append(SymbolInstance(path, cline, 'compatible')) for path, dlines in sorted(compsDBuf): symbol_dts.append(SymbolInstance(path, dlines)) for path, blines in sorted(compsBBuf): symbol_docs.append(SymbolInstance(path, blines)) return symbol_c, symbol_dts, symbol_docs, True def get_idents_defs(self, version, ident, family): symbol_definitions = [] symbol_references = [] symbol_doccomments = [] if not self.db.defs.exists(ident): return symbol_definitions, symbol_references, symbol_doccomments, False if not self.db.vers.exists(version): return symbol_definitions, symbol_references, symbol_doccomments, True files_this_version = self.db.vers.get(version).iter() this_ident = self.db.defs.get(ident) defs_this_ident = this_ident.iter(dummy=True) macros_this_ident = this_ident.get_macros() # FIXME: see why we can have a discrepancy between defs_this_ident and refs if self.db.refs.exists(ident): refs = self.db.refs.get(ident).iter(dummy=True) else: refs = data.RefList().iter(dummy=True) if self.db.docs.exists(ident): docs = self.db.docs.get(ident).iter(dummy=True) else: docs = data.RefList().iter(dummy=True) # vers, defs, refs, and docs are all populated by update.py in order of # idx, and there is a one-to-one mapping between blob hashes and idx # values. Therefore, we can sequentially step through the defs, refs, # and docs for each file in a version. def_idx, def_type, def_line, def_family = next(defs_this_ident) ref_idx, ref_lines, ref_family = next(refs) doc_idx, doc_line, doc_family = next(docs) dBuf = [] rBuf = [] docBuf = [] for file_idx, file_path in files_this_version: # Advance defs, refs, and docs to the current file while def_idx < file_idx: def_idx, def_type, def_line, def_family = next(defs_this_ident) while ref_idx < file_idx: ref_idx, ref_lines, ref_family = next(refs) while doc_idx < file_idx: doc_idx, doc_line, doc_family = next(docs) # Copy information about this identifier into dBuf, rBuf, and docBuf. while def_idx == file_idx: if (def_family == family or family == 'A' or lib.compatibleMacro(macros_this_ident, family)): dBuf.append((file_path, def_type, def_line)) def_idx, def_type, def_line, def_family = next(defs_this_ident) if ref_idx == file_idx: if lib.compatibleFamily(family, ref_family) or family == 'A': rBuf.append((file_path, ref_lines)) if doc_idx == file_idx: # TODO should this be a `while`? docBuf.append((file_path, doc_line)) # Sort dBuf by path name before sorting by type in the loop dBuf.sort() for path, type, dline in sorted(dBuf, key=lambda d: d[1], reverse=True): symbol_definitions.append(SymbolInstance(path, dline, type)) for path, rlines in sorted(rBuf): symbol_references.append(SymbolInstance(path, rlines)) for path, docline in sorted(docBuf): symbol_doccomments.append(SymbolInstance(path, docline)) return symbol_definitions, symbol_references, symbol_doccomments, True ================================================ FILE: elixir/web.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Mikaël Bouillot # and contributors. # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import logging import os import sys import re import threading import time import datetime from collections import OrderedDict, namedtuple from re import search, sub from typing import Any, Callable, NamedTuple, Tuple from urllib import parse import falcon import jinja2 from .lib import validFamily, getFileFamily from .query import Query, SymbolInstance from .filters import get_filters from .filters.utils import FilterContext from .autocomplete import AutocompleteResource from .api import ApiIdentGetterResource from .query import get_query from .web_utils import ProjectConverter, IdentConverter, validate_version, validate_project, validate_ident, \ get_elixir_version_string, get_elixir_repo_url, RequestContext, Config VERSION_CACHE_DURATION_SECONDS = 2 * 60 # 2 minutes ADD_ISSUE_LINK = "https://github.com/bootlin/elixir/issues/new" ELIXIR_VERSION_STRING = get_elixir_version_string() ELIXIR_REPO_LINK = get_elixir_repo_url(ELIXIR_VERSION_STRING) DEFAULT_PROJECT = 'linux' # Error with extra information about browsed project, # to be used in project/version URLs class ElixirProjectError(falcon.errors.HTTPError): def __init__(self, title, description, project=None, version=None, query=None, status=falcon.HTTP_BAD_REQUEST, extra_template_args={}, **kwargs): self.project = project self.version = version self.query = query self.extra_template_args = extra_template_args super().__init__(status, title=title, description=description, **kwargs) # Generate a summary of error details for a bug report def generate_error_details(req, resp, title, details): return f"Request date: {datetime.datetime.now()}\n" + \ f"Path: {req.path}\n" + \ f"Query string: {req.query_string}\n" + \ f"Method: {req.method}\n" + \ f"Status code: {resp.status}\n" + \ f"Error title: {title}\n" + \ f"Error details: {details}\n" def get_github_issue_url(details: str): body = ("TODO: add information on how you reached the error here and " + "validate the details below.\n\n" + "---\n\n" + details) return ADD_ISSUE_LINK + "?body=" + parse.quote(body) # Generate an error page from ElixirProjectError def get_project_error_page(req, resp, exception: ElixirProjectError): report_error_details = generate_error_details(req, resp, exception.title, exception.description) template_ctx = { 'projects': get_projects(req.context.config.project_dir), 'topbar_families': TOPBAR_FAMILIES, 'current_version_path': (None, None, None), 'current_family': 'A', 'source_base_url': '/', 'elixir_version_string': req.context.config.version_string, 'elixir_repo_url': req.context.config.repo_url, 'referer': req.referer if req.referer != req.uri else None, 'bug_report_url': get_github_issue_url(report_error_details), 'home_page_url': '/', 'report_error_details': report_error_details, 'error_title': exception.title, } if exception.project is not None and exception.query is not None: # Add details about current project query = exception.query project = exception.project version = exception.version versions_raw = get_versions_cached(query, req.context, project) get_url_with_new_version = lambda v: stringify_source_path(project, v, '/') versions, current_version_path = get_versions(versions_raw, get_url_with_new_version, version) if current_version_path[2] is None: # If details about current version are not available, make base links # point to latest. # current_tag is not set to latest to avoid latest being highlighted in the sidebar version = query.get_latest_tag() template_ctx = { **template_ctx, 'current_project': project, 'current_tag': version, 'versions': versions, 'current_version_path': current_version_path, 'home_page_url': get_source_base_url(project, version), 'source_base_url': get_source_base_url(project, version), 'ident_base_url': get_ident_base_url(project, version), } if exception.description is not None: template_ctx['error_details'] = exception.description template_ctx = { **template_ctx, **exception.extra_template_args, } template = req.context.jinja_env.get_template('error.html') result = template.render(template_ctx) if exception.query is not None: exception.query.close() return result # Generate an error page from falcon exceptions def get_error_page(req, resp, exception: ElixirProjectError): report_error_details = generate_error_details(req, resp, exception.title, exception.description) template_ctx = { 'projects': get_projects(req.context.config.project_dir), 'topbar_families': TOPBAR_FAMILIES, 'current_version_path': (None, None, None), 'current_family': 'A', 'source_base_url': '/', 'referer': req.referer, 'bug_report_url': ADD_ISSUE_LINK + parse.quote(report_error_details), 'report_error_details': report_error_details, 'error_title': exception.title, } if exception.description is not None: template_ctx['error_details'] = exception.description template = req.context.jinja_env.get_template('error.html') return template.render(template_ctx) # Validates project and version, returns project, version and query. # To be used in project/version URLs def validate_project_and_version(ctx, project, version): project = validate_project(parse.unquote(project)) if project is None: raise ElixirProjectError('Error', 'Invalid project name') query = get_query(ctx.config.project_dir, project) if not query: raise ElixirProjectError('Error', 'Unknown project', status=falcon.HTTP_NOT_FOUND) version = validate_version(parse.unquote(version)) if version is None: raise ElixirProjectError('Error', 'Invalid version', project=project, query=query) return project, version, query # Returns base url of source pages # project and version are assumed to be unquoted def get_source_base_url(project: str, version: str) -> str: return f'/{ parse.quote(project, safe="") }/{ parse.quote(version, safe="") }/source' # Converts ParsedSourcePath to a string with corresponding URL path def stringify_source_path(project: str, version: str, path: str) -> str: if not path.startswith('/'): path = '/' + path path = f'{ get_source_base_url(project, version) }{ path }' return path.rstrip('/') # Handles the '/' URL class IndexResource: def on_get(self, req, resp): ctx = req.context project = DEFAULT_PROJECT query = get_query(ctx.config.project_dir, project) if not query: raise ElixirProjectError('Error', f'Unknown default project: {project}', status=falcon.HTTP_INTERNAL_SERVER_ERROR) version = query.get_latest_tag() resp.status = falcon.HTTP_FOUND resp.location = stringify_source_path(project, version, '/') return # Handles source URLs # Path parameters are asssumed to be unquoted by converters class SourceResource: def on_get(self, req, resp, project: str, version: str, path: str): project, version, query = validate_project_and_version(req.context, project, version) if not path.startswith('/') and len(path) != 0: path = f'/{ path }' if path.endswith('/'): resp.status = falcon.HTTP_MOVED_PERMANENTLY resp.location = stringify_source_path(project, version, path) return # Check if path contains only allowed characters if not search('^[A-Za-z0-9_/.,+-]*$', path): raise ElixirProjectError('Error', 'Path contains characters that are not allowed', project=project, version=version, query=query) if version in ('latest', 'latest-rc'): rc = version == 'latest-rc' version = query.get_latest_tag(rc=rc) resp.status = falcon.HTTP_FOUND resp.location = stringify_source_path(project, version, path) return raw_param = req.get_param('raw') if raw_param is not None and raw_param.strip() != '0': generate_raw_source(resp, query, project, version, path) else: resp.content_type = falcon.MEDIA_HTML resp.status, resp.text = generate_source_page(req.context, query, project, version, path) query.close() # Handles source URLs without a path, ex. '/u-boot/v2023.10/source'. # Note lack of trailing slash class SourceWithoutPathResource(SourceResource): def on_get(self, req, resp, project: str, version: str): return super().on_get(req, resp, project, version, '') # Returns base url of ident pages # project and version assumed unquoted def get_ident_base_url(project: str, version: str, family: str|None = None) -> str: project = parse.quote(project, safe="") version = parse.quote(version, safe="") if family is not None: return f'/{ project }/{ version }/{ parse.quote(family, safe="") }/ident' else: return f'/{ project }/{ version }/ident' # Converts ParsedIdentPath to a string with corresponding URL path def stringify_ident_path(project, version, family, ident) -> str: path = f'{ get_ident_base_url(project, version, family) }/{ parse.quote(ident, safe="") }' return path.rstrip('/') # Handles redirect from ident with form (POST/GET with query parameters) # to default ident URL format class IdentPostRedirectResource: def on_get(self, req, resp, project: str, version: str, family: str|None = None, _ident: str|None = None): get_ident = req.get_param('i', required=False) get_family = req.get_param('f', required=False) if get_ident is None: project, version, _ = validate_project_and_version(req.context, project, version) resp.status = falcon.HTTP_FOUND resp.location = stringify_source_path(project, version, "") else: return self.handle(req, resp, project, version, get_ident, get_family) def on_post(self, req, resp, project: str, version: str, family: str|None = None, _ident: str|None = None): form = req.get_media() post_ident = form.get('i') post_family = form.get('f') return self.handle(req, resp, project, version, post_ident, post_family) def handle(self, req, resp, project: str, version: str, ident: str, family: str): project, version, query = validate_project_and_version(req.context, project, version) if not validFamily(family): family = 'C' if not ident: raise ElixirProjectError('Error', 'Invalid identifier', project=project, version=version, query=query, extra_template_args={ 'searched_ident': parse.unquote(ident), 'current_family': family, }) ident = ident.strip() resp.status = falcon.HTTP_MOVED_PERMANENTLY resp.location = stringify_ident_path(project, version, family, ident) query.close() # Handles ident URLs when family is specified in the URL, both POST and GET # See IdentPostRedirectResource for behavior on POST # Path parameters are asssumed to be unquoted by converters class IdentResource(IdentPostRedirectResource): def on_get(self, req, resp, project: str, version: str, family: str, ident: str): project, version, query = validate_project_and_version(req.context, project, version) family = parse.unquote(family) if not validFamily(family): family = 'C' ident = parse.unquote(ident) validated_ident = validate_ident(ident) if validated_ident is None: raise ElixirProjectError('Error', 'Invalid identifier', project=project, version=version, query=query, extra_template_args={ 'searched_ident': ident, 'current_family': family, }) ident = validated_ident if version in ('latest', 'latest-rc'): rc = version == 'latest-rc' version = query.get_latest_tag(rc=rc) resp.status = falcon.HTTP_FOUND resp.location = stringify_ident_path(project, version, family, ident) return resp.content_type = falcon.MEDIA_HTML resp.status, resp.text = generate_ident_page(req.context, query, project, version, family, ident) query.close() # Handles ident URLs when family is not specified in the URL # Also handles POST requests for ident URLs without family - IdentPostRedirectResource is # inherited from IdentResource class IdentWithoutFamilyResource(IdentResource): def on_get(self, req, resp, project: str, version: str, ident: str): super().on_get(req, resp, project, version, 'C', ident) # Handles /{project}/{version} URL, without path class IncompleteURLRedirectResource: def on_get(self, req, resp, project: str, version: str = "latest"): ctx = req.context query = get_query(ctx.config.project_dir, project) if not query: raise ElixirProjectError('Error', f'Unknown default project: {project}', status=falcon.HTTP_INTERNAL_SERVER_ERROR) if version in ('latest', 'latest-rc') or len(version) == 0: rc = version == 'latest-rc' version = query.get_latest_tag(rc=rc) resp.status = falcon.HTTP_FOUND resp.location = stringify_source_path(project, version, '/') # Handles /{project}/{version}/... URLs with unknown "command" class UnknownPathResource: def on_get(self, req, resp, project: str, version: str, family: str = "", subcmd: str = "", path: str = ""): project, version, query = validate_project_and_version(req.context, project, version) raise ElixirProjectError('Error', 'Invalid path', project=project, version=version, query=query, extra_template_args={ 'current_family': 'A', }) # File families available in the dropdown next to search input in the topbar TOPBAR_FAMILIES = { 'A': 'All symbols', 'C': 'C/CPP/ASM', 'K': 'Kconfig', 'D': 'Devicetree', 'B': 'DT compatible', } # Returns a list of names of top-level directories in basedir def get_directories(basedir: str) -> list[str]: directories = [] for filename in os.listdir(basedir): filepath = os.path.join(basedir, filename) if os.path.isdir(filepath): directories.append(filename) return sorted(directories) # Tuple of project name and URL to root of that project # Used to render project list ProjectEntry = namedtuple('ProjectEntry', 'name, url') # Returns a list of ProjectEntry tuples of projects stored in directory basedir def get_projects(basedir: str) -> list[ProjectEntry]: return [ProjectEntry(p, f"/{p}/latest/source") for p in get_directories(basedir)] # Tuple of version name and URL to chosen resource with that version # Used to render version list in the sidebar VersionEntry = namedtuple('VersionEntry', 'version, url') # Takes result of Query.get_versions() and prepares it for the sidebar template. # Returns an OrderedDict with version information and optionally a triple with # (major, minor, version) of current_version. The triple is useful, because sometimes # the major or minor of a version (in this context) is a custom string (ex. FIXME). # versions: OrderedDict with major parts of versions as keys, values are OrderedDicts # with minor version parts as keys and complete version strings as values # get_url: function that takes a version string and returns the URL # for that version. Meaning of the URL can depend on the context # current_version: string with currently browsed version def get_versions(versions: OrderedDict[str, OrderedDict[str, str]], get_url: Callable[[str], str], current_version: str) -> Tuple[dict[str, dict[str, list[VersionEntry]]], Tuple[str|None, str|None, str|None]]: result = OrderedDict() current_version_path = (None, None, None) for major, minor_verions in versions.items(): for minor, patch_versions in minor_verions.items(): for v in patch_versions: if major not in result: result[major] = OrderedDict() if minor not in result[major]: result[major][minor] = [] result[major][minor].append(VersionEntry(v, get_url(v))) if v == current_version: current_version_path = (major, minor, v) return result, current_version_path # Caches get_versions result in a context object def get_versions_cached(q, ctx, project): with ctx.versions_cache_lock: if project not in ctx.versions_cache: ctx.versions_cache[project] = (time.time(), q.get_versions()) cached_versions = ctx.versions_cache[project] else: cached_versions = ctx.versions_cache[project] if time.time()-cached_versions[0] > VERSION_CACHE_DURATION_SECONDS: ctx.versions_cache[project] = (time.time(), q.get_versions()) cached_versions = ctx.versions_cache[project] return cached_versions[1] # Retruns template context used by the layout template # get_url_with_new_version: see get_url parameter of get_versions # project: name of the project # version: version of the project def get_layout_template_context(q: Query, ctx: RequestContext, get_url_with_new_version: Callable[[str], str], project: str, version: str) -> dict[str, Any]: versions_raw = get_versions_cached(q, ctx, project) versions, current_version_path = get_versions(versions_raw, get_url_with_new_version, version) return { 'projects': get_projects(ctx.config.project_dir), 'versions': versions, 'current_version_path': current_version_path, 'topbar_families': TOPBAR_FAMILIES, 'elixir_version_string': ctx.config.version_string, 'elixir_repo_url': ctx.config.repo_url, 'source_base_url': get_source_base_url(project, version), 'ident_base_url': get_ident_base_url(project, version), 'current_project': project, 'current_tag': parse.unquote(version), 'current_family': 'A', } # Generate raw source response def generate_raw_source(resp, query, project, version, path): type = query.get_file_type(version, path) if type != 'blob': raise ElixirProjectError('File not found', 'This file does not exist.', query=query, project=project, version=version) else: code = query.get_file_raw(version, path) resp.content_type = 'application/octet-stream' resp.text = code resp.downloadable_as = path.split('/')[-1] # Cache for 24 hours resp.cache_control = ('max-age=86400',) # Sandbox result just in case resp.headers['Content-Security-Policy'] = "sandbox; default-src 'none'" # Guesses file format based on filename, returns code formatted as HTML def format_code(filename: str, code: str) -> str: import pygments import pygments.lexers import pygments.formatters from pygments.lexers.asm import GasLexer from pygments.lexers.r import SLexer try: lexer = pygments.lexers.guess_lexer_for_filename(filename, code) if filename.endswith('.S') and isinstance(lexer, SLexer): lexer = GasLexer() except pygments.util.ClassNotFound: lexer = pygments.lexers.get_lexer_by_name('text') lexer.stripnl = False formatter = pygments.formatters.HtmlFormatter( # Adds line numbers column to output linenos=True, # Wraps line numbers in link (a) tags anchorlinenos=True, # Wraps each line in a span tag with id='codeline-{line_number}' linespans='codeline', ) return pygments.highlight(code, lexer, formatter) # Generate formatted HTML of a file, apply filters (for ex. to add identifier links) # q: Query object # project: name of the requested project # version: requested version of the project # path: path to the file in the repository def generate_source(q: Query, project: str, version: str, path: str) -> str: code = q.get_tokenized_file(version, path) _, fname = os.path.split(path) _, extension = os.path.splitext(fname) extension = extension[1:].lower() family = getFileFamily(fname) source_base_url = get_source_base_url(project, version) def get_ident_url(ident, ident_family=None): if ident_family is None: ident_family = family return stringify_ident_path(project, version, ident_family, ident) filter_ctx = FilterContext( q, version, family, path, get_ident_url, lambda path: f'{ source_base_url }{ "/" if not path.startswith("/") else "" }{ path }', lambda rel_path: f'{ source_base_url }{ os.path.dirname(path) }/{ rel_path }', ) filters = get_filters(filter_ctx, project) # Apply filters for f in filters: code = f.transform_raw_code(filter_ctx, code) html_code_block = format_code(fname, code) # Replace line numbers by links to the corresponding line in the current file html_code_block = sub('href="#codeline-(\d+)', 'name="L\\1" id="L\\1" href="#L\\1', html_code_block) for f in filters: html_code_block = f.untransform_formatted_code(filter_ctx, html_code_block) return html_code_block # Represents a file entry in git tree # type : either tree (directory), blob (file) or symlink # name: filename of the file # path: path of the file, path to the target in case of symlinks # url: absolute URL of the file # size: int, file size in bytes, None for directories and symlinks DirectoryEntry = namedtuple('DirectoryEntry', 'type, name, path, url, size') # Returns a list of DirectoryEntry objects with information about files in a directory # base_url: file URLs will be created by appending file path to this URL. It shouldn't end with a slash # tag: requested repository tag # path: path to the directory in the repository def get_directory_entries(q: Query, base_url, tag: str, path: str) -> list[DirectoryEntry]: dir_entries = [] lines = q.get_dir_contents(tag, path) for l in lines: type, name, size, perm = l.split(' ') file_path = f"{ path }/{ name }" if type == 'tree': dir_entries.append(DirectoryEntry('tree', name, file_path, f"{ base_url }{ file_path }", None)) elif type == 'blob': # 120000 permission means it's a symlink if perm == '120000': dir_path = path if path.endswith('/') else path + '/' link_contents = q.get_file_raw(tag, file_path) link_target_path = os.path.abspath(dir_path + link_contents) dir_entries.append(DirectoryEntry('symlink', name, link_target_path, f"{ base_url }{ link_target_path }", size)) else: dir_entries.append(DirectoryEntry('blob', name, file_path, f"{ base_url }{ file_path }", size)) return dir_entries # Generates response (status code and optionally HTML) of the `source` route def generate_source_page(ctx: RequestContext, q: Query, project: str, version: str, path: str) -> tuple[int, str]: status = falcon.HTTP_OK source_base_url = get_source_base_url(project, version) type = q.get_file_type(version, path) # Generate breadcrumbs path_split = path.split('/')[1:] path_temp = '' breadcrumb_urls = [] for p in path_split: path_temp += '/'+p breadcrumb_urls.append((p, f'{ source_base_url }{ path_temp }')) if type == 'tree': back_path = os.path.dirname(path[:-1]) if back_path == '/': back_path = '' template_ctx = { 'dir_entries': get_directory_entries(q, source_base_url, version, path), 'back_url': f'{ source_base_url }{ back_path }' if path != '' else None, } template = ctx.jinja_env.get_template('tree.html') elif type == 'blob': template_ctx = { 'code': generate_source(q, project, version, path), 'path': path, } template = ctx.jinja_env.get_template('source.html') else: raise ElixirProjectError('File not found', 'This file does not exist.', status=falcon.HTTP_NOT_FOUND, query=q, project=project, version=version, extra_template_args={'breadcrumb_urls': breadcrumb_urls}) # Create titles like this: # root path: "Linux source code (v5.5.6) - Bootlin" # first level path: "arch - Linux source code (v5.5.6) - Bootlin" # deeper paths: "Makefile - arch/um/Makefile - Linux source code (v5.5.6) - Bootlin" if path == '': title_path = '' elif len(path_split) == 1: title_path = f'{ path_split[0] } - ' else: title_path = f'{ path_split[-1] } - { "/".join(path_split) } - ' get_url_with_new_version = lambda v: stringify_source_path(project, v, path) # Create template context data = { **get_layout_template_context(q, ctx, get_url_with_new_version, project, version), 'title_path': title_path, 'path': path, 'breadcrumb_urls': breadcrumb_urls, **template_ctx, } return (status, template.render(data)) # Represents line in a file with URL to that line LineWithURL = namedtuple('LineWithURL', 'lineno, url') # Represents a symbol occurrence to be rendered by ident template # type : type of the symbol # path: path of the file that contains the symbol # line: list of LineWithURL SymbolEntry = namedtuple('SymbolEntry', 'type, path, lines') # Converts SymbolInstance into SymbolEntry # path of SymbolInstance will be appended to base_url def symbol_instance_to_entry(base_url: str, symbol: SymbolInstance) -> SymbolEntry: # TODO this should be a responsibility of Query if type(symbol.line) is str: line_numbers = symbol.line.split(',') else: line_numbers = [symbol.line] lines = [ LineWithURL(l, f'{ base_url }/{ symbol.path }#L{ l }') for l in line_numbers ] return SymbolEntry(symbol.type, symbol.path, lines) # Generates response (status code and optionally HTML) of the `ident` route # basedir: path to data directory, ex: "/srv/elixir-data" def generate_ident_page(ctx: RequestContext, q: Query, project: str, version: str, family: str, ident: str) -> tuple[int, str]: status = falcon.HTTP_OK source_base_url = get_source_base_url(project, version) symbol_definitions, symbol_references, symbol_doccomments, symbol_exists = q.search_ident( version, ident, family) symbol_sections = [] if len(symbol_definitions) or len(symbol_references): if len(symbol_doccomments): symbol_sections.append({ 'title': 'Documented', 'symbols': {'_unknown': [symbol_instance_to_entry(source_base_url, sym) for sym in symbol_doccomments]}, }) if len(symbol_definitions): defs_by_type = OrderedDict({}) # TODO this should be a responsibility of Query for sym in symbol_definitions: if sym.type not in defs_by_type: defs_by_type[sym.type] = [symbol_instance_to_entry(source_base_url, sym)] else: defs_by_type[sym.type].append(symbol_instance_to_entry(source_base_url, sym)) symbol_sections.append({ 'title': 'Defined', 'symbols': defs_by_type, }) else: symbol_sections.append({ 'message': 'No definitions found in the database', }) if len(symbol_references): symbol_sections.append({ 'title': 'Referenced', 'symbols': {'_unknown': [symbol_instance_to_entry(source_base_url, sym) for sym in symbol_references]}, }) else: symbol_sections.append({ 'message': 'No references found in the database', }) elif ident != '': status = falcon.HTTP_NOT_FOUND get_url_with_new_version = lambda v: stringify_ident_path(project, v, family, ident) data = { **get_layout_template_context(q, ctx, get_url_with_new_version, project, version), 'searched_ident': ident, 'current_family': family, 'symbol_sections': symbol_sections, 'symbol_exists': symbol_exists, } template = ctx.jinja_env.get_template('ident.html') return (status, template.render(data)) def get_jinja_env(): script_dir = os.path.dirname(os.path.realpath(__file__)) templates_dir = os.path.join(script_dir, '../templates/') loader = jinja2.FileSystemLoader(templates_dir) return jinja2.Environment(loader=loader) # see https://falcon.readthedocs.io/en/v3.1.2/user/recipes/raw-url-path.html # Replaces the default, unquoted URL with a quoted version # NOTE: this is non-standard and it's not guaranteed to work on all WSGI servers class RawPathComponent: def process_request(self, req, _): raw_uri = req.env.get('RAW_URI') or req.env.get('REQUEST_URI') if raw_uri: req.path, _, _ = raw_uri.partition('?') # Adds request context to all requests class RequestContextMiddleware: def __init__(self, jinja_env): self.jinja_env = jinja_env self.versions_cache = {} self.versions_cache_lock = threading.Lock() def process_request(self, req, _resp): req.context = RequestContext( Config(req.env['LXR_PROJ_DIR'], ELIXIR_VERSION_STRING, ELIXIR_REPO_LINK), self.jinja_env, logging.getLogger(__name__), self.versions_cache, self.versions_cache_lock, ) # Serialies caught exceptions to JSON or HTML # See https://falcon.readthedocs.io/en/stable/api/app.html#falcon.App.set_error_serializer def error_serializer(req, resp, exception): preferred = req.client_prefers((falcon.MEDIA_HTML, falcon.MEDIA_JSON)) if preferred is not None: if preferred == falcon.MEDIA_JSON: resp.data = exception.to_json() resp.content_type = falcon.MEDIA_JSON elif preferred == falcon.MEDIA_HTML: if isinstance(exception, ElixirProjectError): resp.text = get_project_error_page(req, resp, exception) else: resp.text = get_error_page(req, resp, exception) resp.content_type = falcon.MEDIA_HTML resp.append_header('Vary', 'Accept') # Builds and returns the Falcon application def get_application(): app = falcon.App(middleware=[ RawPathComponent(), RequestContextMiddleware(get_jinja_env()), ]) app.router_options.converters['project'] = ProjectConverter app.router_options.converters['ident'] = IdentConverter app.set_error_serializer(error_serializer) app.add_route('/', IndexResource()) app.add_route('/{project}/{version}/source/{path:path}', SourceResource()) app.add_route('/{project}/{version}/source', SourceWithoutPathResource()) app.add_route('/{project}/{version}/ident', IdentPostRedirectResource()) app.add_route('/{project}/{version}/ident/{ident}', IdentWithoutFamilyResource()) app.add_route('/{project}/{version}/{family}/ident/{ident}', IdentResource()) app.add_route('/acp', AutocompleteResource()) app.add_route('/api/ident/{project:project}/{ident:ident}', ApiIdentGetterResource()) app.add_route('/{project}', IncompleteURLRedirectResource()) app.add_route('/{project}/{version}', IncompleteURLRedirectResource()) app.add_route('/{project}/{version}/', IncompleteURLRedirectResource()) app.add_route('/{project}/{version}/{family}', UnknownPathResource()) app.add_route('/{project}/{version}/{family}/{subcmd}', UnknownPathResource()) app.add_route('/{project}/{version}/{family}/{subcmd}/{path:path}', UnknownPathResource()) return app application = get_application() ================================================ FILE: elixir/web_utils.py ================================================ import os import re import logging import threading from urllib import parse from typing import Any, Dict, NamedTuple import falcon import jinja2 from .lib import validFamily, run_cmd ELIXIR_DIR = os.path.normpath(os.path.dirname(__file__) + "/../") ELIXIR_REPO_LINK = 'https://github.com/bootlin/elixir/' def get_elixir_version_string(): version = os.environ.get('ELIXIR_VERSION') if version is not None and len(version) != 0: return version try: # try to get Elixir version from git result, return_code = run_cmd('git', '-C', ELIXIR_DIR, '-c', f'safe.directory={ ELIXIR_DIR }', 'rev-parse', '--short', 'HEAD' ) if return_code == 0: return result.decode('utf-8') except Exception: logging.exception("failed to get elixir commit hash") return '' def get_elixir_repo_url(version): if re.match('^[0-9a-f]{5,12}$', version) or version.startswith('v'): return ELIXIR_REPO_LINK + f'tree/{ version }' else: return ELIXIR_REPO_LINK # Elixir config, currently contains only path to directory with projects class Config(NamedTuple): project_dir: str version_string: str repo_url: str # Basic information about handled request - current Elixir configuration, configured Jinja environment # and logger class RequestContext(NamedTuple): config: Config jinja_env: jinja2.Environment logger: logging.Logger versions_cache: Dict[str, str] versions_cache_lock: threading.Lock def validate_project(project: str) -> str|None: if project is not None and re.match(r'^[a-zA-Z0-9_.,:/-]+$', project): return project.strip() # Validates and unquotes project parameter class ProjectConverter(falcon.routing.BaseConverter): def convert(self, value: str) -> str: value = parse.unquote(value) project = validate_project(value) if project is None: raise falcon.HTTPBadRequest('Error', 'Invalid project name') return project def validate_version(version) -> str|None: if version is not None and re.match(r'^[a-zA-Z0-9_.,:/-]+$', version): return version.strip() def validate_ident(ident: str) -> str|None: if ident is not None and re.match(r'^[A-Za-z0-9_,.+?#-]+$', ident): return ident.strip() # Validates and unquotes identifier parameter class IdentConverter(falcon.routing.BaseConverter): def convert(self, value: str) -> str|None: value = parse.unquote(value) return validate_ident(value) ================================================ FILE: find-file-doc-comments.pl ================================================ #!/usr/bin/env perl # find-file-doc-comments.pl: Find the doc comments for a file. # Usage: find-file-doc-comments.pl # This file is part of Elixir, a source code cross-referencer. # # By Christopher White # Copyright (c) 2019--2020 D3 Engineering, LLC. # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . use 5.010001; use strict; use warnings; use autodie; my $VERBOSE = $ENV{V}; exit main(@ARGV); sub main { die "Need a filename" unless @_; my $filename = shift; die "Could not read $filename" unless -r $filename; say "Processing file $filename" if $VERBOSE; # Fatalize all warnings, and log which file triggered the warning. local $SIG{__WARN__} = sub { die "While processing $filename: $_[0]\n"; }; # Do `script.sh parse-defs` on the file my @ctags = qx{ ctags -x --c-kinds=+p-m --language-force=C "$filename" | grep -av "^operator " | awk '{print \$1" "\$2" "\$3}' }; die "Could not get ctags: $!" if $!; say "No ctags results" if $VERBOSE && !@ctags; return 0 unless @ctags; # Make a list of [name, type, line] arrays my @ctags_parsed = map { [split] } @ctags; # Flip it around to index functions and types by line. Don't index anything # by name, since there can be multiple names with different types/lines (#186). my %definition_lines; my %definition_types; for my $tag (@ctags_parsed) { $definition_lines{$tag->[2]} = $tag->[0]; $definition_types{$tag->[2]} = $tag->[1] // ''; } if($VERBOSE) { for my $tag (sort { $a->[2] <=> $b->[2] } @ctags_parsed) { say $tag->[2], ': ', $tag->[0], ' is a(n) ', $tag->[1]; } } # Read the source file open my $fh, '<', $filename; my @source_lines = (undef, <$fh>); # undef => indices in @source_lines match ctags's 1-based linenos close $fh; # Work backwards through the file and look for doc comments my %doc_comments; my $doc_comment_opener = qr{^\h*\/\*\*(?:\h|$)}; # Start of doc comment my $comment_leader = qr{\h+\*\h+(?:(?:struct|enum|union|typedef)\h+)?}; for(my $lineno = $#source_lines ; $lineno >= 1 ; --$lineno) { next unless exists $definition_lines{$lineno}; my $definition_name = $definition_lines{$lineno}; my $definition_type = $definition_types{$lineno}; say "\nChecking $definition_type $definition_name @ $lineno" if $VERBOSE; # Comment header: be liberal in what we accept. For example, do not # check the type of the definition/declaration against the type in # the comment header. I don't think this will be a problem. my $this_doc_comment_header = qr{^$comment_leader\Q$definition_name\E(?:\h|\(|:|$)}; say " Regex is -$this_doc_comment_header-" if $VERBOSE; # Make sure we get back past the first line of multiline definitions if($definition_type eq 'macro') { --$lineno while $lineno && $source_lines[$lineno] !~ /^\h*#\h*define/; } elsif($definition_type eq 'function') { # Try to handle the case of "int\nfoo()" if($source_lines[$lineno] =~ /^\h*\Q$definition_name\E\b/) { --$lineno while $lineno && $source_lines[$lineno] =~ /^[a-z_]/i; } } # Assume cflags gave us the first line of the definition, or we got # back to it manually. Move to the first line that might be a doc comment. --$lineno; # If we ran off the beginning of the file, there's no doc comment. next if $lineno <= 0; # TODO make sure we're not still in the definition. # E.g., memblock.h:for_each_mem_range(). The defintion is reported # on the second line of the #define, not the first line. say " Starting search for docs at line $lineno" if $VERBOSE; # Find the last line that could be a doc-comment header # for this function. while($lineno && $source_lines[$lineno] =~ qr{ ^\h*$ # Empty line | ^\h+\*\/ # End of comment | ^\h+\*(?:\h|$) # Continuation of comment | $this_doc_comment_header }x) { if($VERBOSE) { my $line = $source_lines[$lineno]; chomp $line; say " skipped $lineno '$line'"; } --$lineno; } ++$lineno; # Check the last line that matched, # because we may have just skipped past $this_doc_comment_header # Is it actually a header for this function? say " Checking line $lineno for header" if $VERBOSE; next unless $source_lines[$lineno] =~ $this_doc_comment_header; # We have found a header. Confirm it's a doc comment. --$lineno; next unless $lineno > 0 && $source_lines[$lineno] =~ $doc_comment_opener; say " * Match" if $VERBOSE; # We have found a doc comment for this function! Record it. push @{$doc_comments{$definition_name}}, $lineno; } # foreach line in reverse order # Report the doc comments for each function for my $definition_name (keys %doc_comments) { my $comment_lines = $doc_comments{$definition_name}; say "$definition_name $_" foreach @$comment_lines; } return 0; } #main ================================================ FILE: find_compatible_dts.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Maxime Chretien # and contributors # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import re from urllib import parse from elixir.lib import decode class FindCompatibleDTS: def __init__(self): # Compile regexes self.regex_c = re.compile("\s*{*\s*\.compatible\s*=\s*\"(.+?)\"") self.regex_dts1 = re.compile("\s*compatible") self.regex_dts2 = re.compile("\"(.+?)\"") self.regex_bindings = re.compile("([\w-]+,?[\w-]+)") def parse_c(self, content): return self.regex_c.findall(content) def parse_dts(self, content): ret = [] if self.regex_dts1.match(content) != None: ret = self.regex_dts2.findall(content) return ret def parse_bindings(self, content): # There are a lot of wrong results # but we don't apply that to a lot of files # so it should be fine return self.regex_bindings.findall(content) def run(self, file_lines, family): ident_list = [] # Iterate though lines and search for idents for num, line in enumerate(file_lines, 1): line = decode(line) if family == 'C': ret = self.parse_c(line) elif family == 'D': ret = self.parse_dts(line) elif family == 'B': ret = self.parse_bindings(line) for i in range(len(ret)): ident_list.append(str(parse.quote(ret[i])) + ' ' + str(num)) return ident_list ================================================ FILE: projects/amazon-freertos.sh ================================================ # Elixir definitions for Amazon FreeRTOS list_tags_h() { echo "$tags" | grep -v '^v' | tac | sed -r 's/^([0-9][0-9][0-9][0-9])([0-9][0-9])(.*)$/\1 \1\2 \1\2\3/' echo "$tags" | grep '^v' | tac | sed -r 's/^(v[0-9]*)\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' } get_latest_tags() { git tag | grep '^20' | sort -Vr } ================================================ FILE: projects/arm-trusted-firmware.sh ================================================ # Elixir definitions for Arm Trusted Firmware # https://github.com/ARM-software/arm-trusted-firmware # Enable DT bindings compatible strings support dts_comp_support=1 list_tags_h() { echo "$tags" | grep -v 'for-v0\.4' | tac | sed -r 's/^(v[0-9]*)\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' echo "$tags" | grep 'for-v0\.4' | tac | sed -r 's/^/custom for-v0.4 /' } ================================================ FILE: projects/barebox.sh ================================================ # Elixir definitions for Barebox # Enable DT bindings compatible strings support dts_comp_support=1 list_tags_h() { echo "$tags" | grep '^v20' | tac | sed -r 's/^(v20..)\.([0-9][0-9])\.(.*)$/\1 \1.\2 \1.\2.\3/' echo "$tags" | grep '^v2\.0' | tac | sed -r 's/^(v2\.0)(.*)$/old \1 \1\2/' echo "$tags" | grep '^freescale' | tac | sed -r 's/^(freescale)(.*)$/old \1 \1\2/' } ================================================ FILE: projects/bluez.sh ================================================ # Elixir definitions for BlueZ list_tags() { echo "$tags" | grep '^[0-9]' } list_tags_h() { echo "$tags" | grep '^[0-9]' | sort -rV | sed -E 's/^([0-9]*)\.([0-9]*)$/v\1 v\1.\2 \1.\2/' } get_latest_tags() { git tag | grep '^[0-9]\.' | sort -Vr } ================================================ FILE: projects/busybox.sh ================================================ # Elixir definitions for BusyBox version_dir() { tr '_.' '._'; } version_rev() { tr '._' '_.'; } list_tags_h() { echo "$tags" | tac | sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 \1.\2 \1.\2\3/' } ================================================ FILE: projects/coreboot.sh ================================================ # Elixir definitions for Coreboot list_tags_h() { echo "$tags" | tac | sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 v\1.\2 \1.\2\3/' } ================================================ FILE: projects/dpdk.sh ================================================ # Elixir definitions for DPDK list_tags_h() { echo "$tags" | grep -vE '^v1\.|^v2\.' | tac | sed -r 's/^v([0-9]*)\.([0-9]*)(.*)$/v\1 v\1.\2 v\1.\2\3/' echo "$tags" | grep -E '^v1\.|^v2\.' | tac | sed -r 's/^v(1|2)\.([0-9])(.*)$/old v\1.\2 v\1.\2\3/' } ================================================ FILE: projects/freebsd.sh ================================================ # FreeBSD version_dir() { grep "^release/[0-9]*\.[0-9]*\.[0-9]*$" | sed -e 's,^release/,v,' | sed -e 's,\.0$,,'; } version_rev() { grep "^v" | sed -e 's,v[0-9]*\.[0-9]*$,&\.0,' | sed -e 's,^v,release/,'; } ================================================ FILE: projects/glibc.sh ================================================ # Elixir definitions for glibc list_tags() { echo "$tags" | grep -v 'cvs' } list_tags_h() { echo "$tags" | grep "glibc" | grep -v "fedora" | grep -v "cvs" | tac | sed -r 's/^glibc-([0-9]*)(\.[0-9]*)(.*)$/v\1 v\1\2 glibc-\1\2\3/' echo "$tags" | grep -v "cvs" | grep "fedora" | tac | sed -r 's/^fedora\/glibc-([0-9]*)(\.[0-9]*)(.*)$/fedora v\1\2 fedora\/glibc-\1\2\3/' } ================================================ FILE: projects/grub.sh ================================================ # Elixir definitions for Grub list_tags_h() { echo "$tags" | tac | sed -r 's/^(grub-)?([0-9]+).([0-9]+)([A-Za-z0-9\.-]*)$/\2 \2.\3 \1\2.\3\4/' } ================================================ FILE: projects/iproute2.sh ================================================ ================================================ FILE: projects/linux.sh ================================================ # Elixir definitions for Linux # Enable DT bindings compatible strings support dts_comp_support=1 get_tags() { git tag | version_dir | sed -r 's/^(pre|lia64-|)(v?[0-9\.]*)(pre|-[^pf].*?|)(alpha|-[pf].*?|)([0-9]*)(.*?)$/\2#\3@\4@\5@\60@\1.0/' | sort -V | sed -r 's/^(.*?)#(.*?)@(.*?)@(.*?)@(.*?)0@(.*?)\.0$/\6\1\2\3\4\5/' } list_tags_h() { echo "$tags" | tac | sed -r 's/^(pre|lia64-|)(v?)([0-9]*)\.([0-9]*)(.*)$/v\3 v\3.\4 \1\2\3.\4\5/' } ================================================ FILE: projects/llvm.sh ================================================ # Elixir definitions for LLVM list_tags() { echo "$tags" | tac | grep ^llvmorg-[0-9]*[\.][0-9]* } list_tags_h() { echo "$tags" | grep ^llvmorg | grep -v init | tac | sed -r 's/^llvmorg-([0-9]*)\.([0-9]*)(.*)$/v\1 v\1.\2 llvmorg-\1.\2\3/' } get_latest_tags() { git tag | grep 'llvmorg' | grep -v init | sort -Vr } ================================================ FILE: projects/mesa.sh ================================================ # Elixir definitions for Mesa list_tags() { echo "$tags" | tac | grep ^mesa-[0-9]*[\.][0-9]* } list_tags_h() { echo "$tags" | grep ^mesa-[0-9]*[\.][0-9]* | tac | sed -r 's/^mesa-([0-9]*)(\.[0-9]*)(.*)$/v\1 v\1\2 mesa-\1\2\3/' } get_latest_tags() { git tag | version_dir | grep ^mesa-[0-9]*[\.][0-9]* | grep -v '\-rc' | sort -Vr } ================================================ FILE: projects/musl.sh ================================================ # Elixir definitions for Musl # Using the default ones so far! ================================================ FILE: projects/ofono.sh ================================================ # Elixir definitions for Ofono list_tags_h() { echo "$tags" | tac | sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 v\1.\2 \1.\2\3/' } ================================================ FILE: projects/op-tee.sh ================================================ # Elixir definitions for OP-TEE Trusted OS list_tags_h() { echo "$tags" | grep '^[0-9]\.' | tac | sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 \1.\2 \1.\2\3/' } list_tags() { echo "$tags" | grep '^[0-9]\.' } get_latest_tags() { git tag | grep '^[0-9]\.' | grep -v '\-rc' | sort -Vr } ================================================ FILE: projects/opensbi.sh ================================================ ================================================ FILE: projects/qemu.sh ================================================ # Elixir definitions for QEMU list_tags_h() { echo "$tags" | grep -E "^v[0-9].*" | tac | sed -r 's/^(v[0-9])\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' echo "$tags" | grep "release" | tac | sed -r 's/^(release)_([0-9_]*)$/old \1 \1_\2/' echo "old initial initial" } ================================================ FILE: projects/toybox.sh ================================================ # Elixir definitions for Toybox list_tags_h() { echo "$tags" | tac | sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' } ================================================ FILE: projects/u-boot.sh ================================================ # Elixir definitions for U-Boot # Enable DT bindings compatible strings support dts_comp_support=1 list_tags_h() { echo "$tags" | grep '^v20' | tac | sed -r 's/^(v20..)\.([0-9][0-9])(.*)$/\1 \1.\2 \1.\2\3/' echo "$tags" | grep -E '^(v1|U)' | tac | sed -r 's/^/old by-version /' echo "$tags" | grep -E '^(LABEL|DENX)' | tac | sed -r 's/^/old by-date /' } ================================================ FILE: projects/uclibc-ng.sh ================================================ # Elixir definitions for uclibc-ng # Using the default ones so far ================================================ FILE: projects/xen.sh ================================================ # Xen hypervisor version_dir() { grep "^RELEASE" | sed -e 's/^RELEASE-/v/'; } version_rev() { grep "^v" | sed -e 's/^v/RELEASE-/'; } ================================================ FILE: projects/zephyr.sh ================================================ # Elixir definitions for Zephyr # Enable DT bindings compatible strings support dts_comp_support=1 list_tags() { echo "$tags" | grep -v '^zephyr-v' } list_tags_h() { echo "$tags" | grep -v '^zephyr-v' | tac | sed -r 's/^(v[0-9]*)\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' } get_latest_tags() { git tag | grep -v '^zephyr-v' | version_dir | grep -v '\-rc' | sort -Vr } ================================================ FILE: requirements.txt ================================================ Jinja2~=3.1.5 Pygments~=2.18.0 Falcon~=4.0.2 pytest==7.2.1 # NOTE binary wheels of berkeleydb are not distributed - on Debian this may # require installing build-essentials, python3-dev and libdb-dev # NOTE keep in sync with wheel version in the Dockerfile berkeleydb==18.1.10 ================================================ FILE: samples/projects/linuxtest.sh ================================================ # Elixir definitions for Linux testing (only one tag) # Copy this file to the "projects" directory list_tags() { echo "v5.6.1" } list_tags_h() { echo "v5 v5.6 v5.6.1" } get_latest_tags() { echo "v5.6.1" } ================================================ FILE: script.sh ================================================ #!/bin/sh # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Mikaël Bouillot # and contributors # Portions copyright (c) 2019 D3 Engineering, LLC # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . if [ ! -d "$LXR_REPO_DIR" ]; then echo "$0: Can't find repository" exit 1 fi # Get our path so we can find peer find-file-doc-comments.pl later cur_dir=`pwd` script_path=`realpath "$0"` cd `dirname "$script_path"` script_dir=`pwd` cd "$cur_dir" dts_comp_support=0 # DT bindings compatible strings support (disable by default) version_dir() { cat; } version_rev() { cat; } get_tags() { git tag | version_dir | sed 's/$/.0/' | sort -V | sed 's/\.0$//' } list_tags() { echo "$tags" } list_tags_h() { echo "$tags" | tac | sed -r 's/^(v[0-9]*)\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' } get_latest_tags() { git tag | version_dir | grep -v '\-rc' | sort -Vr } get_type() { v=`echo $opt1 | version_rev` git cat-file -t "$v:`denormalize $opt2`" 2>/dev/null } get_blob() { git cat-file blob $opt1 } get_file() { v=`echo $opt1 | version_rev` git cat-file blob "$v:`denormalize $opt2`" 2>/dev/null } get_dir() { v=`echo $opt1 | version_rev` git ls-tree -l "$v:`denormalize $opt2`" 2>/dev/null | awk '{print $2" "$5" "$4" "$1}' | grep -v ' \.' | sort -t ' ' -k 1,1r -k 2,2 } tokenize_file() { if [ "$opt1" = -b ]; then ref=$opt2 else v=`echo $opt1 | version_rev` ref="$v:`denormalize $opt2`" fi if [ $opt3 = "D" ]; then #Don't cut around '-' in devicetrees regex='s%((/\*.*?\*/|//.*?\001|[^'"'"']"(\\.|.)*?"|# *include *<.*?>|[^\w-])+)([\w-]+)?%\1\n\4\n%g' else regex='s%((/\*.*?\*/|//.*?\001|[^'"'"']"(\\.|.)*?"|# *include *<.*?>|\W)+)(\w+)?%\1\n\4\n%g' fi git cat-file blob $ref 2>/dev/null | tr '\n' '\1' | perl -pe "$regex" | head -n -1 } list_blobs() { v=`echo $opt2 | version_rev` if [ "$opt1" = '-p' ]; then # "path" option: return blob hash and full path format='\1 \2' elif [ "$opt1" = '-f' ]; then # "file" option: return blob hash and file name (without its path) format='\1 \4' else # default option: return only blob hash format='\1' v=`echo $opt1 | version_rev` fi git ls-tree -r "$v" | sed -r "s/^\S* blob (\S*)\t(([^/]*\/)*(.*))$/$format/; /^\S* commit .*$/d" } untokenize() { tr -d '\n' | sed 's/>/\*\//g' | sed 's/' } parse_defs() { case $opt3 in "C") parse_defs_C ;; "K") parse_defs_K ;; "D") parse_defs_D ;; esac } parse_defs_C() { tmp=`mktemp -d` full_path=$tmp/$opt2 git cat-file blob "$opt1" > "$full_path" # Use ctags to parse most of the defs ctags -x --kinds-c=+p+x --extras='-{anonymous}' "$full_path" | grep -avE -e "^operator " -e "^CONFIG_" | awk '{print $1" "$2" "$3}' # Parse function macros, e.g., in .S files perl -ne '/^\s*ENTRY\((\w+)\)/ and print "$1 function $.\n"' "$full_path" perl -ne '/^SYSCALL_DEFINE[0-9]\(\s*(\w+)\W/ and print "sys_$1 function $.\n"' "$full_path" rm "$full_path" rmdir $tmp } parse_defs_K() { tmp=`mktemp -d` full_path=$tmp/$opt2 git cat-file blob "$opt1" > "$full_path" ctags -x --language-force=kconfig --kinds-kconfig=c --extras-kconfig=-{configPrefixed} "$full_path" | awk '{print "CONFIG_"$1" "$2" "$3}' rm "$full_path" rmdir $tmp } parse_defs_D() { tmp=`mktemp -d` full_path=$tmp/$opt2 git cat-file blob "$opt1" > "$full_path" ctags -x --language-force=dts "$full_path" | awk '{print $1" "$2" "$3}' rm "$full_path" rmdir $tmp } parse_docs() { tmpfile=`mktemp` git cat-file blob "$opt1" > "$tmpfile" "$script_dir/find-file-doc-comments.pl" "$tmpfile" || exit "$?" rm -rf "$tmpfile" } dts_comp() { echo $dts_comp_support } project=$(basename `dirname $LXR_REPO_DIR`) plugin=$script_dir/projects/$project.sh if [ -f "$plugin" ] ; then . $plugin fi cd "$LXR_REPO_DIR" test $# -gt 0 || set help cmd=$1 opt1=$2 opt2=$3 opt3=$4 shift denormalize() { echo $1 | cut -c 2- } case $cmd in list-tags) tags=`get_tags` if [ "$opt1" = '-h' ]; then list_tags_h else list_tags fi ;; get-latest-tags) get_latest_tags ;; get-type) get_type ;; get-blob) get_blob ;; get-file) get_file ;; get-dir) get_dir ;; list-blobs) list_blobs ;; tokenize-file) tokenize_file ;; untokenize) untokenize ;; parse-defs) parse_defs ;; parse-docs) parse_docs ;; dts-comp) dts_comp ;; help) echo "Usage: $0 subcommand [args]..." exit 1 ;; *) echo "$0: Unknown subcommand: $cmd" exit 1 esac ================================================ FILE: static/autocomplete.js ================================================ /* * @license MIT * * Autocomplete.js v2.7.1 * Developed by Baptiste Donaux * http://autocomplete-js.com * * (c) 2017, Baptiste Donaux * * Built with Browserify and modified by * Maxime Chretien * for the needs of Elixir Cross Referencer * https://github.com/bootlin/elixir * */ var currentScript = document.currentScript; (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i document.getElementById("search-form").requestSubmit(), }, "#search-input"); },{"autocomplete-js":2}],2:[function(require,module,exports){ (function (global){ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AutoComplete = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i @baptistedonaux */ var AutoComplete = /** @class */ (function () { // Constructor function AutoComplete(params, selector) { if (params === void 0) { params = {}; } if (selector === void 0) { selector = "[data-autocomplete]"; } if (Array.isArray(selector)) { selector.forEach(function (s) { new AutoComplete(params, s); }); } else if (typeof selector == "string") { var elements = document.querySelectorAll(selector); Array.prototype.forEach.call(elements, function (input) { new AutoComplete(params, input); }); } else { var specificParams = AutoComplete.merge(AutoComplete.defaults, params, { DOMResults: document.createElement("div") }); AutoComplete.prototype.create(specificParams, selector); return specificParams; } } AutoComplete.prototype.create = function (params, element) { params.Input = element; if (params.Input.nodeName.match(/^INPUT$/i) && (params.Input.hasAttribute("type") === false || params.Input.getAttribute("type").match(/^TEXT|SEARCH$/i))) { params.Input.setAttribute("autocomplete", "off"); params._Position(params); params.Input.parentNode.appendChild(params.DOMResults); params.$Listeners = { blur: params._Blur.bind(params), destroy: AutoComplete.prototype.destroy.bind(null, params), focus: params._Focus.bind(params), keyup: AutoComplete.prototype.event.bind(null, params, EventType.KEYUP), keydown: AutoComplete.prototype.event.bind(null, params, EventType.KEYDOWN), position: params._Position.bind(params) }; for (var event in params.$Listeners) { params.Input.addEventListener(event, params.$Listeners[event]); } } // Init cache for all families var e = document.getElementsByName("f")[0]; for (var i = 0; i < e.options.length; i++) { var family = encodeURIComponent(e.options[i].value); params.$Cache[family] = {}; } }; AutoComplete.prototype.getEventsByType = function (params, type) { var mappings = {}; for (var key in params.KeyboardMappings) { var event = EventType.KEYUP; if (params.KeyboardMappings[key].Event !== undefined) { event = params.KeyboardMappings[key].Event; } if (event == type) { mappings[key] = params.KeyboardMappings[key]; } } return mappings; }; AutoComplete.prototype.event = function (params, type, event) { var eventIdentifier = function (condition) { if ((match === true && mapping.Operator == ConditionOperator.AND) || (match === false && mapping.Operator == ConditionOperator.OR)) { condition = AutoComplete.merge({ Not: false }, condition); if (condition.hasOwnProperty("Is")) { if (condition.Is == event.keyCode) { match = !condition.Not; } else { match = condition.Not; } } else if (condition.hasOwnProperty("From") && condition.hasOwnProperty("To")) { if (event.keyCode >= condition.From && event.keyCode <= condition.To) { match = !condition.Not; } else { match = condition.Not; } } } }; for (var name in AutoComplete.prototype.getEventsByType(params, type)) { var mapping = AutoComplete.merge({ Operator: ConditionOperator.AND }, params.KeyboardMappings[name]), match = ConditionOperator.AND == mapping.Operator; mapping.Conditions.forEach(eventIdentifier); if (match === true) { mapping.Callback.call(params, event); } } }; AutoComplete.prototype.makeRequest = function (params, callback, callbackErr) { var propertyHttpHeaders = Object.getOwnPropertyNames(params.HttpHeaders), request = new XMLHttpRequest(), method = params._HttpMethod(), url = params._Url(), queryParams = params._Pre(), queryParamsStringify = encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(queryParams); if (method.match(/^GET$/i)) { if (url.indexOf("?") !== -1) { url += "&" + queryParamsStringify; } else { url += "?" + queryParamsStringify; } } // Send select family to display only relevant elements var e = document.getElementsByName("f")[0]; var family = encodeURIComponent(e.options[e.selectedIndex].value); url += "&f=" + encodeURIComponent(family); // Send project name url += "&p=" + encodeURIComponent(currentScript.getAttribute('project')); request.open(method, url, true); for (var i = propertyHttpHeaders.length - 1; i >= 0; i--) { request.setRequestHeader(propertyHttpHeaders[i], params.HttpHeaders[propertyHttpHeaders[i]]); } request.onreadystatechange = function () { if (request.readyState == 4 && request.status == 200) { var e = document.getElementsByName("f")[0]; var family = encodeURIComponent(e.options[e.selectedIndex].value); params.$Cache[family][queryParams] = request.response; callback(request.response); } else if (request.status >= 400) { callbackErr(); } }; return request; }; AutoComplete.prototype.ajax = function (params, request, timeout) { if (timeout === void 0) { timeout = true; } if (params.$AjaxTimer) { window.clearTimeout(params.$AjaxTimer); } if (timeout === true) { params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay); } else { if (params.Request) { params.Request.abort(); } params.Request = request; params.Request.send(params._QueryArg() + "=" + params._Pre()); } }; AutoComplete.prototype.cache = function (params, callback, callbackErr) { var e = document.getElementsByName("f")[0]; var family = encodeURIComponent(e.options[e.selectedIndex].value); var response = params._Cache(family, params._Pre()); if (response === undefined) { var request = AutoComplete.prototype.makeRequest(params, callback, callbackErr); AutoComplete.prototype.ajax(params, request); } else { callback(response); } }; AutoComplete.prototype.destroy = function (params) { for (var event in params.$Listeners) { params.Input.removeEventListener(event, params.$Listeners[event]); } params.DOMResults.parentNode.removeChild(params.DOMResults); }; AutoComplete.merge = function () { var merge = {}, tmp; for (var i = 0; i < arguments.length; i++) { for (tmp in arguments[i]) { merge[tmp] = arguments[i][tmp]; } } return merge; }; AutoComplete.defaults = { Delay: 150, EmptyMessage: "No result here", Highlight: { getRegex: function (value) { return new RegExp(value, "ig"); }, transform: function (value) { return "" + value + ""; } }, HttpHeaders: { "Content-type": "application/x-www-form-urlencoded" }, Limit: 0, MinChars: 0, HttpMethod: "GET", QueryArg: "q", Url: null, SelectCallback: null, KeyboardMappings: { "Enter": { Conditions: [{ Is: 13, Not: false }], Callback: function (event) { if (this.DOMResults.getAttribute("class").indexOf("open") != -1) { var liActive = this.DOMResults.querySelector("li.active"); if (liActive !== null) { event.preventDefault(); this._Select(liActive); this.DOMResults.setAttribute("class", "autocomplete"); } } }, Operator: ConditionOperator.AND, Event: EventType.KEYDOWN }, "KeyUpAndDown_down": { Conditions: [{ Is: 38, Not: false }, { Is: 40, Not: false }], Callback: function (event) { event.preventDefault(); }, Operator: ConditionOperator.OR, Event: EventType.KEYDOWN }, "KeyUpAndDown_up": { Conditions: [{ Is: 38, Not: false }, { Is: 40, Not: false }], Callback: function (event) { event.preventDefault(); var first = this.DOMResults.querySelector("li:first-child:not(.locked)"), last = this.DOMResults.querySelector("li:last-child:not(.locked)"), active = this.DOMResults.querySelector("li.active"); if (active) { var currentIndex = Array.prototype.indexOf.call(active.parentNode.children, active), position = currentIndex + (event.keyCode - 39), lisCount = this.DOMResults.getElementsByTagName("li").length; if (position < 0 || position >= lisCount) { active.classList.remove("active"); active = null; } if (active) { active.classList.remove("active"); active.parentElement.children.item(position).classList.add("active"); } } else if (last && event.keyCode == 38) { last.classList.add("active"); } else if (first) { first.classList.add("active"); } }, Operator: ConditionOperator.OR, Event: EventType.KEYUP }, "AlphaNum": { Conditions: [{ Is: 13, Not: true }, { From: 35, To: 40, Not: true }], Callback: function () { var oldValue = this.Input.getAttribute("data-autocomplete-old-value"), currentValue = this._Pre(); if (currentValue !== "" && currentValue.length >= this._MinChars()) { if (!oldValue || currentValue != oldValue) { this.DOMResults.setAttribute("class", "autocomplete open"); } AutoComplete.prototype.cache(this, function (response) { this._Render(this._Post(response)); this._Open(); }.bind(this), this._Error); } else { this._Close(); } }, Operator: ConditionOperator.AND, Event: EventType.KEYUP } }, DOMResults: null, Request: null, Input: null, /** * Return the message when no result returns */ _EmptyMessage: function () { var emptyMessage = ""; if (this.Input.hasAttribute("data-autocomplete-empty-message")) { emptyMessage = this.Input.getAttribute("data-autocomplete-empty-message"); } else if (this.EmptyMessage !== false) { emptyMessage = this.EmptyMessage; } else { emptyMessage = ""; } return emptyMessage; }, /** * Returns the maximum number of results */ _Limit: function () { var limit = this.Input.getAttribute("data-autocomplete-limit"); if (isNaN(limit) || limit === null) { return this.Limit; } return parseInt(limit, 10); }, /** * Returns the minimum number of characters entered before firing ajax */ _MinChars: function () { var minchars = this.Input.getAttribute("data-autocomplete-minchars"); if (isNaN(minchars) || minchars === null) { return this.MinChars; } return parseInt(minchars, 10); }, /** * Apply transformation on labels response */ _Highlight: function (label) { return label.replace(this.Highlight.getRegex(this._Pre()), this.Highlight.transform); }, /** * Returns the HHTP method to use */ _HttpMethod: function () { if (this.Input.hasAttribute("data-autocomplete-method")) { return this.Input.getAttribute("data-autocomplete-method"); } return this.HttpMethod; }, /** * Returns the query param to use */ _QueryArg: function () { if (this.Input.hasAttribute("data-autocomplete-param-name")) { return this.Input.getAttribute("data-autocomplete-param-name"); } return this.QueryArg; }, /** * Returns the URL to use for AJAX request */ _Url: function () { if (this.Input.hasAttribute("data-autocomplete")) { return this.Input.getAttribute("data-autocomplete"); } return this.Url; }, /** * Manage the close */ _Blur: function (now) { if (now === void 0) { now = false; } if (now) { this._Close(); } else { var params = this; setTimeout(function () { params._Blur(true); }, 150); } }, /** * Manage the cache */ _Cache: function (family, value) { return this.$Cache[family][value]; }, /** * Manage the open */ _Focus: function () { var oldValue = this.Input.getAttribute("data-autocomplete-old-value"); if ((!oldValue || this.Input.value != oldValue) && this._MinChars() <= this.Input.value.length) { this.DOMResults.setAttribute("class", "autocomplete open"); } }, /** * Bind all results item if one result is opened */ _Open: function () { var params = this; Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) { if (li.getAttribute("class") != "locked") { li.onclick = function () { params._Select(li); }; } }); }, _Close: function () { this.DOMResults.setAttribute("class", "autocomplete"); }, /** * Position the results HTML element */ _Position: function () { this.DOMResults.setAttribute("class", "autocomplete"); this.DOMResults.setAttribute("style", "top:" + (this.Input.offsetTop + this.Input.offsetHeight) + "px;left:" + this.Input.offsetLeft + "px;width:" + this.Input.clientWidth + "px;"); }, /** * Execute the render of results DOM element */ _Render: function (response) { var ul; if (typeof response == "string") { ul = this._RenderRaw(response); } else { ul = this._RenderResponseItems(response); } if (this.DOMResults.hasChildNodes()) { this.DOMResults.removeChild(this.DOMResults.childNodes[0]); } this.DOMResults.appendChild(ul); }, /** * ResponseItems[] rendering */ _RenderResponseItems: function (response) { var ul = document.createElement("ul"), li = document.createElement("li"), limit = this._Limit(); // Order if (limit < 0) { response = response.reverse(); } else if (limit === 0) { limit = response.length; } for (var item = 0; item < Math.min(Math.abs(limit), response.length); item++) { li.innerHTML = response[item].Label; li.setAttribute("data-autocomplete-value", response[item].Value); ul.appendChild(li); li = document.createElement("li"); } return ul; }, /** * string response rendering (RAW HTML) */ _RenderRaw: function (response) { var ul = document.createElement("ul"), li = document.createElement("li"); if (response.length > 0) { this.DOMResults.innerHTML = response; } else { var emptyMessage = this._EmptyMessage(); if (emptyMessage !== "") { li.innerHTML = emptyMessage; li.setAttribute("class", "locked"); ul.appendChild(li); } } return ul; }, /** * Deal with request response */ _Post: function (response) { try { var returnResponse = []; //JSON return var json = JSON.parse(response); if (Object.keys(json).length === 0) { return ""; } if (Array.isArray(json)) { for (var i = 0; i < Object.keys(json).length; i++) { returnResponse[returnResponse.length] = { "Value": json[i], "Label": this._Highlight(json[i]) }; } } else { for (var value in json) { returnResponse.push({ "Value": value, "Label": this._Highlight(json[value]) }); } } return returnResponse; } catch (event) { //HTML return return response; } }, /** * Return the autocomplete value to send (before request) */ _Pre: function () { return this.Input.value; }, /** * Choice one result item */ _Select: function (item) { if (item.hasAttribute("data-autocomplete-value")) { this.Input.value = item.getAttribute("data-autocomplete-value"); } else { this.Input.value = item.innerHTML; } this.Input.setAttribute("data-autocomplete-old-value", this.Input.value); if (this.SelectCallback) { this.SelectCallback(); } }, /** * Handle HTTP error on the request */ _Error: function () { }, $AjaxTimer: null, $Cache: {}, $Listeners: {} }; return AutoComplete; }()); module.exports = AutoComplete; },{}]},{},[1])(1) }); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}]},{},[1]); ================================================ FILE: static/dynamic-references.js ================================================ "use strict"; function identUrl(project, ident, version, family) { return `/api/ident/${project}/${ident}?version=${version}&family=${family}`; } /* "definitions": [ { "path": "arch/x86_64/kstat.h", "line": 1, "type": "struct" } ], "references": [ { "path": "src/stat/fstatat.c", "line": "78,142", "type": null } ], "documentations": [ { "path": "Documentation/devicetree/bindings/arm/arm,cci-400.yaml", "line": "15,17,18", "type": null } ] */ function generateSymbolDefinitionsHTML(symbolDefinitions, project, version) { let result = ""; let typesCount = {}; let previous_type = ""; if(symbolDefinitions.length == 0) { return '

No definitions found in the database

'; } for(let symbolDefinition of symbolDefinitions) { if (symbolDefinition.type in typesCount) { typesCount[symbolDefinition.type] += 1; } else { typesCount[symbolDefinition.type] = 1; } } for (let sd of symbolDefinitions) { if (sd.type != previous_type) { if (previous_type != '') { result += ''; } result += '

Defined in ' + typesCount[sd.type].toString() + ' files as a ' + sd.type + ':

'; result += ''; return result; } function generateSymbolReferencesHTML(symbolReferences, project, version) { let result = ""; if(symbolReferences.length == 0) { return '

No references found in the database

'; } result += '

Referenced in ' + symbolReferences.length.toString() + ' files:

'; result += '
    '; for (let sr of symbolReferences) { let ln = sr.line.split(','); if (ln.length == 1) { let n = ln[0]; result += `
  • ${sr.path}, line ${n}`; } else { if(symbolReferences.length > 100) { let n = ln.length; result += `
  • ${sr.path}, ${n} times`; } else { result += `
  • ${sr.path}`; result += '
      ' for(let n of ln) { result += `
    • line ${n}` } result += '
    ' } } } result += '
' return result; } function generateDocCommentsHTML(symbolDocComments, project, version) { let result = ""; if (symbolDocComments.length == 0) { return result; } result += '

Documented in ' + symbolDocComments.length.toString() + ' files:

'; result += '
    '; for(let sd of symbolDocComments) { let ln = sd.line.split(','); if(ln.length == 1) { let n = ln[0]; result += `
  • ${sd.path}, line ${n}`; } else { if(symbolDocComments.length > 100) { let n = ln.length; result += `
  • ${sd.path}, ${n} times`; } else { result += `
  • ${sd.path}`; result += '
      '; for(let n of ln) { result += `
    • line ${n}`; } result += '
    '; } } } result += '
'; return result; } function generateReferencesHTML(data, project, version) { let symbolDefinitions = data["definitions"]; let symbolReferences = data["references"]; let symbolDocumentations = data["documentations"]; return '
' + generateDocCommentsHTML(symbolDocumentations, project, version) + generateSymbolDefinitionsHTML(symbolDefinitions, project, version) + generateSymbolReferencesHTML(symbolReferences, project, version) + '
'; } function showPopup(referencePopup, target) { let targetRect = target.getBoundingClientRect(); let x = target.offsetLeft; let y = target.offsetTop + targetRect.height; referencePopup.style.visibility = "visible"; referencePopup.style.display = "block"; referencePopup.style.left = `${x}px`; referencePopup.style.top = `${y}px`; referencePopup.scrollTop = 0; referencePopup.scrollLeft = 0; let referenceRect = referencePopup.getBoundingClientRect(); if((referenceRect.top + referenceRect.height) > window.innerHeight) { referencePopup.style.top = `${target.offsetTop - referenceRect.height}px`; } if((referenceRect.left + referenceRect.width) > window.innerWidth) { x -= ((referenceRect.left + referenceRect.width) - window.innerWidth); referencePopup.style.left = `${x}px`; } } function hidePopup(referencePopup) { referencePopup.style.visibility = "hidden"; referencePopup.style.display = "none"; } document.addEventListener("DOMContentLoaded", _ => { let referencePopup = document.getElementById("reference-popup"); let loadingPopup = document.getElementById("loading-popup"); var loadingTimer; var popupId = 0; var abortController; document.body.querySelectorAll(".ident").forEach(el => { el.addEventListener("click", async ev => { if (ev.ctrlKey || ev.metaKey || ev.shiftKey) { return; } ev.preventDefault(); let splitPath = ev.target.pathname.split("/"); let [_, project, version, family, _i, ident] = splitPath; let currentPopupId = ++popupId; if(abortController !== undefined) abortController.abort(); abortController = new AbortController(); hidePopup(loadingPopup); clearTimeout(loadingTimer); loadingTimer = setTimeout(() => { showPopup(loadingPopup, ev.target); }, 200); function cancelLoadingPopup() { if (currentPopupId == popupId) { hidePopup(loadingPopup); clearTimeout(loadingTimer); } } try { let result = await fetch(identUrl(project, ident, version, family), { signal: abortController.signal }) .then(r => r.json()); if(currentPopupId == popupId) { referencePopup.innerHTML = generateReferencesHTML(result, project, version); showPopup(referencePopup, ev.target); } } catch(e) { if(e.name !== "AbortError") { cancelLoadingPopup(); throw e; } } cancelLoadingPopup(); }); }); referencePopup.addEventListener("click", ev => ev.stopPropagation()); document.body.addEventListener("click", _ => hidePopup(referencePopup)); document.body.addEventListener("keydown", ev => { if (ev.key === "Escape") { hidePopup(referencePopup); } }); }); ================================================ FILE: static/fonts/ubuntu/LICENCE-FAQ.txt ================================================ Ubuntu Font Family Licensing FAQ Stylistic Foundations The Ubuntu Font Family is the first time that a libre typeface has been designed professionally and explicitly with the intent of developing a public and long-term community-based development process. When developing an open project, it is generally necessary to have firm foundations: a font needs to maintain harmony within itself even across many type designers and writing systems. For the [1]Ubuntu Font Family, the process has been guided with the type foundry Dalton Maag setting the project up with firm stylistic foundation covering several left-to-right scripts: Latin, Greek and Cyrillic; and right-to-left scripts: Arabic and Hebrew (due in 2011). With this starting point the community will, under the supervision of [2]Canonical and [3]Dalton Maag, be able to build on the existing font sources to expand their character coverage. Ultimately everybody will be able to use the Ubuntu Font Family in their own written languages across the whole of Unicode (and this will take some time!). Licensing The licence chosen by any free software project is one of the foundational decisions that sets out how derivatives and contributions can occur, and in turn what kind of community will form around the project. Using a licence that is compatible with other popular licences is a powerful constraint because of the [4]network effects: the freedom to share improvements between projects allows free software to reach high-quality over time. Licence-proliferation leads to many incompatible licences, undermining the network effect, the freedom to share and ultimately making the libre movement that Ubuntu is a part of less effective. For all kinds of software, writing a new licence is not to be taken lightly and is a choice that needs to be thoroughly justified if this path is taken. Today it is not clear to Canonical what the best licence for a font project like the Ubuntu Font Family is: one that starts life designed by professionals and continues with the full range of community development, from highly commercial work in new directions to curious beginners' experimental contributions. The fast and steady pace of the Ubuntu release cycle means that an interim libre licence has been necessary to enable the consideration of the font family as part of Ubuntu 10.10 operating system release. Before taking any decision on licensing, Canonical as sponsor and backer of the project has reviewed the many existing licenses used for libre/open fonts and engaged the stewards of the most popular licenses in detailed discussions. The current interim licence is the first step in progressing the state-of-the-art in licensing for libre/open font development. The public discussion must now involve everyone in the (comparatively new) area of the libre/open font community; including font users, software freedom advocates, open source supporters and existing libre font developers. Most importantly, the minds and wishes of professional type designers considering entering the free software business community must be taken on board. Conversations and discussion has taken place, privately, with individuals from the following groups (generally speaking personally on behalf of themselves, rather than their affiliations): * [5]SIL International * [6]Open Font Library * [7]Software Freedom Law Center * [8]Google Font API Document embedding One issue highlighted early on in the survey of existing font licences is that of document embedding. Almost all font licences, both free and unfree, permit embedding a font into a document to a certain degree. Embedding a font with other works that make up a document creates a "combined work" and copyleft would normally require the whole document to be distributed under the terms of the font licence. As beautiful as the font might be, such a licence makes a font too restrictive for useful general purpose digital publishing. The situation is not entirely unique to fonts and is encountered also with tools such as GNU Bison: a vanilla GNU GPL licence would require anything generated with Bison to be made available under the terms of the GPL as well. To avoid this, Bison is [9]published with an additional permission to the GPL which allows the output of Bison to be made available under any licence. The conflict between licensing of fonts and licensing of documents, is addressed in two popular libre font licences, the SIL OFL and GNU GPL: * [10]SIL Open Font Licence: When OFL fonts are embedded in a document, the OFL's terms do not apply to that document. (See [11]OFL-FAQ for details. * [12]GPL Font Exception: The situation is resolved by granting an additional permission to allow documents to not be covered by the GPL. (The exception is being reviewed). The Ubuntu Font Family must also resolve this conflict, ensuring that if the font is embedded and then extracted it is once again clearly under the terms of its libre licence. Long-term licensing Those individuals involved, especially from Ubuntu and Canonical, are interested in finding a long-term libre licence that finds broad favour across the whole libre/open font community. The deliberation during the past months has been on how to licence the Ubuntu Font Family in the short-term, while knowingly encouraging everyone to pursue a long-term goal. * [13]Copyright assignment will be required so that the Ubuntu Font Family's licensing can be progressively expanded to one (or more) licences, as best practice continues to evolve within the libre/open font community. * Canonical will support and fund legal work on libre font licensing. It is recognised that the cost and time commitments required are likely to be significant. We invite other capable parties to join in supporting this activity. The GPL version 3 (GPLv3) will be used for Ubuntu Font Family build scripts and the CC-BY-SA for associated documentation and non-font content: all items which do not end up embedded in general works and documents. Ubuntu Font Licence For the short-term only, the initial licence is the [14]Ubuntu Font License (UFL). This is loosely inspired from the work on the SIL OFL 1.1, and seeks to clarify the issues that arose during discussions and legal review, from the perspective of the backers, Canonical Ltd. Those already using established licensing models such as the GPL, OFL or Creative Commons licensing should have no worries about continuing to use them. The Ubuntu Font Licence (UFL) and the SIL Open Font Licence (SIL OFL) are not identical and should not be confused with each other. Please read the terms precisely. The UFL is only intended as an interim license, and the overriding aim is to support the creation of a more suitable and generic libre font licence. As soon as such a licence is developed, the Ubuntu Font Family will migrate to it—made possible by copyright assignment in the interium. Between the OFL 1.1, and the UFL 1.0, the following changes are made to produce the Ubuntu Font Licence: * Clarification: 1. Document embedding (see [15]embedding section above). 2. Apply at point of distribution, instead of receipt 3. Author vs. copyright holder disambiguation (type designers are authors, with the copyright holder normally being the funder) 4. Define "Propagate" (for internationalisation, similar to the GPLv3) 5. Define "Substantially Changed" 6. Trademarks are explicitly not transferred 7. Refine renaming requirement Streamlining: 8. Remove "not to be sold separately" clause 9. Remove "Reserved Font Name(s)" declaration A visual demonstration of how these points were implemented can be found in the accompanying coloured diff between SIL OFL 1.1 and the Ubuntu Font Licence 1.0: [16]ofl-1.1-ufl-1.0.diff.html References 1. http://font.ubuntu.com/ 2. http://www.canonical.com/ 3. http://www.daltonmaag.com/ 4. http://en.wikipedia.org/wiki/Network_effect 5. http://scripts.sil.org/ 6. http://openfontlibrary.org/ 7. http://www.softwarefreedom.org/ 8. http://code.google.com/webfonts 9. http://www.gnu.org/licenses/gpl-faq.html#CanIUseGPLToolsForNF 10. http://scripts.sil.org/OFL_web 11. http://scripts.sil.org/OFL-FAQ_web 12. http://www.gnu.org/licenses/gpl-faq.html#FontException 13. https://launchpad.net/~uff-contributors 14. http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt 15. http://font.ubuntu.com/ufl/FAQ.html#embedding 16. http://font.ubuntu.com/ufl/ofl-1.1-ufl-1.0.diff.html ================================================ FILE: static/fonts/ubuntu/LICENCE.txt ================================================ ------------------------------- UBUNTU FONT LICENCE Version 1.0 ------------------------------- PREAMBLE This licence allows the licensed fonts to be used, studied, modified and redistributed freely. The fonts, including any derivative works, can be bundled, embedded, and redistributed provided the terms of this licence are met. The fonts and derivatives, however, cannot be released under any other licence. The requirement for fonts to remain under this licence does not require any document created using the fonts or their derivatives to be published under this licence, as long as the primary purpose of the document is not to be a vehicle for the distribution of the fonts. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this licence and clearly marked as such. This may include source files, build scripts and documentation. "Original Version" refers to the collection of Font Software components as received under this licence. "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Copyright Holder(s)" refers to all individuals and companies who have a copyright ownership of the Font Software. "Substantially Changed" refers to Modified Versions which can be easily identified as dissimilar to the Font Software by users of the Font Software comparing the Original Version with the Modified Version. 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 and with or without charging a redistribution fee), making available to the public, and in some countries other activities as well. PERMISSION & CONDITIONS This licence does not grant any rights under trademark law and all such rights are reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to propagate the Font Software, subject to the below conditions: 1) Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine- readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 2) The font name complies with the following: (a) The Original Version must retain its name, unmodified. (b) Modified Versions which are Substantially Changed must be renamed to avoid use of the name of the Original Version or similar names entirely. (c) Modified Versions which are not Substantially Changed must be renamed to both (i) retain the name of the Original Version and (ii) add additional naming elements to distinguish the Modified Version from the Original Version. The name of such Modified Versions must be the name of the Original Version, with "derivative X" where X represents the name of the new work, appended to that name. 3) The name(s) of the Copyright Holder(s) and any contributor to the Font Software shall not be used to promote, endorse or advertise any Modified Version, except (i) as required by this licence, (ii) to acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with their explicit written permission. 4) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this licence, and must not be distributed under any other licence. The requirement for fonts to remain under this licence does not affect any document created using the Font Software, except any version of the Font Software extracted from a document created using the Font Software may only be distributed under this licence. TERMINATION This licence becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: static/fonts/ubuntu/copyright.txt ================================================ Copyright 2010,2011 Canonical Ltd. This Font Software is licensed under the Ubuntu Font Licence, Version 1.0. https://launchpad.net/ubuntu-font-licence ================================================ FILE: static/messages.json ================================================ [ { "title": "Embedded Linux training (on-line)", "body": "Apr 13-23, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/189" }, { "title": "Embedded Linux training (on-line)", "body": "Nov 2-12, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/188" }, { "title": "Linux kernel drivers training (on-line)", "body": "Apr 13-23, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/183" }, { "title": "Yocto / OpenEmbedded training (on-line)", "body": "Apr 20-23, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/186" }, { "title": "Linux debugging, profiling, tracing and performance analysis training", "body": "Apr 20-23, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/185" }, { "title": "Buildroot System Development training", "body": "Dec 7-11, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/170" }, { "title": "Embedded Linux Networking training", "body": "Apr 13-17, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/181" }, { "title": "Embedded Linux Audio training", "body": "Sep 21-25, 2026", "action": "Register", "link": "https://shop.bootlin.com/en/event/190" }, { "title": "Linux BSP development engineering services", "body": "Need help to port Linux and bootloaders to your hardware?", "link": "https://bootlin.com/engineering/linux-board-support-package/" }, { "title": "Open-source upstreaming", "body": "Need help get the support for your hardware in upstream Linux?", "link": "https://bootlin.com/engineering/upstreaming/" }, { "title": "Linux BSP upgrade and security maintenance", "body": "Need help to get security updates for your Linux BSP?", "link": "https://bootlin.com/engineering/linux-board-support-package/" }, { "title": "Yocto distribution development and maintenance", "body": "Need a Yocto distribution for your embedded project?", "link": "https://bootlin.com/engineering/linux-board-support-package/" }, { "title": "Buildroot integration, development and maintenance", "body": "Need a Buildroot system for your embedded project?", "link": "https://bootlin.com/engineering/linux-board-support-package/" }, { "title": "Zephyr development and porting", "body": "Building a Zephyr-based project?", "link": "https://bootlin.com/engineering/zephyr/" } ] ================================================ FILE: static/robots.txt ================================================ User-Agent: * Allow: / Crawl-Delay: 2 ================================================ FILE: static/script.js ================================================ "use strict"; /* Tags menu filter */ // Get a dictionary of tag name -> tag link from HTML function getTags() { const tags = {}; const list = document.querySelectorAll('.versions a'); for (const el of list) { tags[el.innerText.trim()] = el.href; } return tags; } // Generate tag search results based on input // filter: current filter input text // tags: dictionary of tag name -> tag link function generateResults(filter, tags) { const searchResults = document.createDocumentFragment(); const filterRegex = new RegExp(filter, 'i'); for (let key in tags) { if (tags.hasOwnProperty(key)) { let tagFound = false; const tagHighlight = key.replace(filterRegex, result => { if (result) tagFound = true; return '' + result + ''; }) if (tagFound) { const tagLink = document.createElement('a'); tagLink.href = tags[key]; tagLink.innerHTML = tagHighlight; searchResults.appendChild(tagLink); } } } return searchResults; } // Setup tags filter input function setupVersionsFilter() { const input = document.querySelector('.filter-input'); const results = document.querySelector('.filter-results'); const versions = document.querySelector('.versions'); const tags = getTags(); input.addEventListener('input', e => { if (e.target.value === '') { versions.classList.remove('hide'); results.innerHTML = ''; } else { versions.classList.add('hide'); results.innerHTML = ''; results.appendChild(generateResults(e.target.value, tags)); } }); } // Setup expanding/collapsing versions tree on click function setupVersionsTree() { const versions = document.querySelector('.versions'); versions.addEventListener('click', e => { if (e.target && e.target.nodeName == 'SPAN') { e.target.classList.toggle('active') } }); } function isWidescreen() { return getComputedStyle(document.documentElement).getPropertyValue('--is-widescreen') === 'true'; } // Toggles sidebar visibility, handles widescreen and mobile layouts function toggleMenu() { const isWidescreen = getComputedStyle(document.documentElement).getPropertyValue('--is-widescreen') === 'true'; if(isWidescreen) { const hasShowMenu = document.documentElement.classList.contains('show-menu'); window.localStorage.setItem('show-sidebar', !hasShowMenu); document.documentElement.classList.toggle('show-menu'); } else { document.documentElement.classList.toggle('show-menu-mobile'); } } // Setup sidebar hamburger menu button, close button and mobile sidebar backdrop events function setupSidebarSwitch() { const tag = document.querySelector('.version em'); const openMenu = document.querySelector('.open-menu'); const sidebar = document.querySelector('.sidebar'); // toggle on hamburger menu click openMenu.addEventListener('click', e => { e.preventDefault(); toggleMenu(); }); // toggle on footer tag icon click tag.addEventListener('click', e => { e.preventDefault(); toggleMenu(); }); // close on close-menu/backdrop click sidebar.addEventListener('click', e => { if (e.target === sidebar && isWidescreen()) { document.documentElement.classList.remove('show-menu'); window.localStorage.setItem('show-sidebar', false); } else if (e.target === sidebar || e.target.classList.contains('close-menu')) { document.documentElement.classList.remove('show-menu-mobile'); } }); } // Parse and validate line identifier in format L${number} function parseLineId(lineId) { if (lineId[0] != "L") { return; } let lineIdNum = parseInt(lineId.substring(1)); console.assert(!isNaN(lineIdNum), "Invalid line id"); let lineElement = document.getElementById(lineId); if (lineElement === null || lineElement.tagName !== "A") { return; } return lineIdNum; } // Parse and validate line range anchor in format #L${lineRangeStart}-L${lineRangeEnd} function parseLineRangeAnchor(hashStr) { const hash = hashStr.substring(1).split("-"); if (hash.length < 1 || hash.length > 2) { return; } let firstLine = parseLineId(hash[0]); let lastLine = hash.length === 2 ? parseLineId(hash[1]) : firstLine; if (firstLine === undefined || lastLine === undefined) { return; } // Swap line numbers to support "#L2-L1" format. Postel's law. if (firstLine > lastLine) { const lineTmp = lastLine; lastLine = firstLine; firstLine = lineTmp; } return [firstLine, lastLine]; } // Highlights line number elements from firstLine to lastLine function highlightFromTo(firstLine, lastLine) { const firstLineElement = document.getElementById(`L${ firstLine }`); const lastLineElement = document.getElementById(`L${ lastLine }`); const firstCodeLine = document.getElementById(`codeline-${ firstLine }`); const lastCodeLine = document.getElementById(`codeline-${ lastLine }`); addClassToRangeOfElements(firstLineElement.parentNode, lastLineElement.parentNode, "line-highlight"); addClassToRangeOfElements(firstCodeLine, lastCodeLine, "line-highlight"); } function clearRangeHighlight() { const highlightElements = Array.from(document.getElementsByClassName("line-highlight")); for (let el of highlightElements) { el.classList.remove("line-highlight"); } } function addClassToRangeOfElements(first, last, class_name) { let element = first; const elementAfterLast = last !== null ? last.nextElementSibling : null; while (element !== null && element != elementAfterLast) { element.classList.add(class_name); element = element.nextElementSibling; } } // Sets up listeners on element that contains line numbers to handle // shift-clicks for range highlighting function setupLineRangeHandlers() { // Check if page contains the element with line numbers // If not, then likely script is not executed in context of the source page const linenodiv = document.querySelector(".linenodiv"); if (linenodiv === null) { return; } let rangeStartLine, rangeEndLine; const parseFromHash = () => { const highlightedRange = parseLineRangeAnchor(window.location.hash); // Set range start/end to elements from hash if (highlightedRange !== undefined) { rangeStartLine = highlightedRange[0]; rangeEndLine = highlightedRange[1]; highlightFromTo(rangeStartLine, rangeEndLine); document.getElementById(`L${rangeStartLine}`).scrollIntoView(); } else if (location.hash !== "" && location.hash[1] === "L") { rangeStartLine = parseLineId(location.hash.substring(1)); } } window.addEventListener("hashchange", _ => { clearRangeHighlight(); parseFromHash(); }); parseFromHash(); linenodiv.addEventListener("click", ev => { if (ev.ctrlKey || ev.metaKey) { return; } ev.preventDefault(); // Handler is set on the element that contains all line numbers, check if the // event is directed at an actual line number element const el = ev.target; if (typeof(el.id) !== "string" || el.id[0] !== "L" || el.tagName !== "A") { return; } clearRangeHighlight(); if (rangeStartLine === undefined || !ev.shiftKey) { rangeStartLine = parseLineId(el.id); rangeEndLine = undefined; highlightFromTo(rangeStartLine, rangeStartLine); window.location.hash = el.id; } else if (ev.shiftKey) { if (rangeEndLine === undefined) { rangeEndLine = parseLineId(el.id); } const newLine = parseLineId(el.id); console.assert(newLine !== undefined, "parseLineId for clicked line is undefined"); // Swap range elements if range end that was previously undefined is now // before range start if (rangeStartLine > rangeEndLine) { const lineTmp = rangeStartLine; rangeStartLine = rangeEndLine; rangeEndLine = lineTmp; } if (newLine < rangeStartLine) { // Expand if element above range rangeStartLine = newLine; } else if (newLine > rangeEndLine) { // Expand if element below range rangeEndLine = newLine; } else { // Shrink moving the edge that's closest to the selection. // Move end if center was selected. const distanceFromStart = Math.abs(rangeStartLine-newLine); const distanceFromEnd = Math.abs(rangeEndLine-newLine); if (distanceFromStart < distanceFromEnd) { rangeStartLine = newLine; } else { rangeEndLine = newLine; } } highlightFromTo(rangeStartLine, rangeEndLine); window.location.hash = `L${rangeStartLine}-L${rangeEndLine}`; } }); } /* Other fixes */ // prevent chrome from auto-scrolling to input elements function setupAutoscrollingPrevention() { const wrapper = document.querySelector('.wrapper'); Array.prototype.forEach.call(document.querySelectorAll('input'), el => { el.addEventListener('keydown', _ => { const before = wrapper.scrollTop; const reset = () => wrapper.scrollTop = before; window.requestAnimationFrame(reset); setTimeout(reset, 0); }); }); } // Scrolls the page after each anchor change to prevent selected line from // hiding under the topbar after a line number click. function setupAnchorOffsetHandler() { const wrapper = document.querySelector('.wrapper'); const anchorChangeHandler = e => { if (e && e.preventDefault) e.preventDefault(); if (location.hash.length !== 0) { const el = document.querySelector(location.hash); if (el) { const offsetTop = el.offsetTop; wrapper.scrollTop = offsetTop < 100 ? 200 : offsetTop + 100; } } }; window.requestAnimationFrame(anchorChangeHandler); window.addEventListener('hashchange', anchorChangeHandler); } function setupGoToTop() { const wrapper = document.querySelector('.wrapper'); const goToTop = document.querySelector('.go-top'); goToTop.addEventListener('click', e => { wrapper.scrollTop = 0; wrapper.scrollLeft = 0; }); } function randomChoice(arr) { return arr[Math.floor(Math.random() * arr.length)]; } function updateBannerContents(msg) { const banner = document.querySelector('.message-banner'); banner.querySelector('.title').innerText = msg.title; banner.querySelector('.subtitle').innerHTML = msg.body.replace('\n', '
'); document.querySelector('.message-link').href = msg.link; const actionElement = banner.querySelector('.action'); const actionInner = actionElement.querySelector('.action-inner'); if (msg.action) { actionInner.innerText = msg.action; actionElement.classList.add("action-visible"); } else { actionElement.classList.remove("action-visible"); } } function updateMessageBanner() { fetch('/static/messages.json') .then(r => r.json()) .then(messages => { // TODO compatibility with old messages format, remove after ~march 2025 const pickedMsg = randomChoice(messages); const msg = pickedMsg.desktop ? pickedMsg.desktop : pickedMsg; updateBannerContents(msg); }); } function cycleBanner(delay=500) { fetch('/static/messages.json') .then(r => r.json()) .then(messages => { cycleBannerWithData(messages, delay); }); } function sleep(duration) { return new Promise(resolve => setTimeout(resolve, duration)); } async function cycleBannerWithData(data, delay=500) { for (const msg of data) { updateBannerContents(msg); await sleep(delay); } console.log("cycle finished"); } document.addEventListener('DOMContentLoaded', _ => { updateMessageBanner(); setupVersionsFilter(); setupVersionsTree(); setupSidebarSwitch(); setupLineRangeHandlers(); setupAutoscrollingPrevention(); setupAnchorOffsetHandler(); setupGoToTop(); document.getElementById('clear-search').addEventListener('click', _ => document.getElementById('search-input').value = '' ); }); ================================================ FILE: static/style.css ================================================ @charset "UTF-8"; /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:#fff0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} /* commons */ html { -webkit-box-sizing: border-box; box-sizing: border-box; font-size: 16px; line-height: 1.2; } *, *:before, *:after { -webkit-box-sizing: inherit; box-sizing: inherit; } *::-webkit-scrollbar { width: 9px; height: 9px; } .sidebar nav::-webkit-scrollbar-corner { background: #222; } .sidebar nav::-webkit-scrollbar-track { background: #222; } .sidebar nav::-webkit-scrollbar-thumb { background: #666; } body::-webkit-scrollbar-corner { background: #666; } body::-webkit-scrollbar-track { background: #666; } body::-webkit-scrollbar-thumb { background: #ccc; } .wrapper::-webkit-scrollbar-corner { background: #666; } .wrapper::-webkit-scrollbar-track { background: #666; } .wrapper::-webkit-scrollbar-thumb { background: #ccc; } body { background: #fff; font-family: 'Ubuntu', sans-serif; } pre { font-family: 'Ubuntu Mono', monospace; font-size: 0.9em; line-height: 1.2; padding: 0; color: #787878; } table { border-collapse: collapse; } table td { padding: 0; height: 100%; } a { color: #000; text-decoration: none; } a::before, button::before { -webkit-transition: color 0.2s, background-color 0.2s; transition: color 0.2s, background-color 0.2s; } input { -webkit-transition: background-color 0.2s; transition: background-color 0.2s; } a:focus { outline: 1px; outline-style: dotted; outline-offset: -1px; } h2 { color: #aaa; font-weight: 100; margin-top: 0; margin-bottom: 1rem; } .link { color: #6d7dd2; text-decoration: underline; } /* oocss */ .hide { display: none; } .screenreader { font-size: 0; } /* IE 9 */ .browserupgrade { position: absolute; top: 0em; left: 0; right: 0; width: auto; height: auto; padding: 1em; margin: 0; z-index: 10; background-color: #440a0a; color: #fff; font-size: 0.7em; text-align: center; } .browserupgrade a { color: #fff; text-decoration: underline; } /* sub-header */ .sub-header { height: 2.7em; margin-top: -2.7em; text-align: center; } .select-projects { padding: 0.5rem 0.6rem; border: 0 none; color: #fff; font-family: 'Ubuntu Mono', monospace; font-weight: 700; font-style: italic; font-size: 1.6em; padding-right: 1.5em; /* custom select design */ -webkit-appearance: none; -moz-appearance: none; appearance: none; background-color: transparent; background-image: url("data:image/svg+xml;utf8,"); background-repeat: no-repeat; background-position: bottom 0.45em right 0.3em; background-size: 9%; opacity: 0.7; } .select-projects:-moz-focusring { color: #fff; color: rgba(255,255,255,0); text-shadow: 0 0 0 #fff; outline: 1px; outline-style: dotted; outline-offset: -1px; } .select-projects:focus { outline: 1px; outline-style: dotted; outline-offset: -1px; outline-color: #fff; } .select-projects:hover, .select-projects:focus { opacity: 1; } .select-projects option { color: #000; } /* topbar */ .topbar { background: #c0c0c0; z-index: 20; } .breadcrumb { display: inline-block; color: #888; padding: 0.9rem 1.3rem; font-size: 0.8em; } .breadcrumb a { color: #555; padding: 0.35em; } .breadcrumb a:hover { color: #000; } .project { font-weight: 700; } /* filter / search */ .filter, .search { position: relative; } .search input { height: 100%; } .filter input, .search input { font-family: 'Ubuntu Mono', monospace; font-size: 0.9em; width: 100%; border: 0 none; padding: 0.45em; margin: 0; } .filter input:focus, .search input:focus { outline: 0 none; } .filter button { position: absolute; right: 0; bottom: 0; border: 0 none; font-size: 1.7em; height: 1.5em; background: transparent; color: #999; cursor: pointer; } .search button { border: 0 none; font-size: 1.7em; height: 1.5em; background: transparent; color: #555; cursor: pointer; width: 50px; } .search { position: relative; padding: 0.5em; padding-top: 0; } .search form { display: flex; flex-direction: row; } .search button:focus, .search button:hover { color: #000; outline: 0 none; } .search input { color: #000; background: #ddd; padding-right: 3em; min-width: 0; flex: 3.5; } .search input:focus { background: #eee; } .search select { -moz-appearance: none; -webkit-appearance: none; appearance: none; font-family: 'Ubuntu Mono', monospace; font-size: 0.9em; text-align: center; text-align-last: center; min-height: 32px; padding: 7px; padding-right: 20px; text-decoration: none; background-color: #aaa; background-image: url("/static/img/arrow-dropdown-16.svg"); background-position: right 2px center; background-repeat: no-repeat; opacity: 0.9; border: 0 none; margin: 0; min-width: 0; flex: 1; } .search select:-moz-focusring { /* Remove unwanted firefox borders */ color: #000; color: rgba(0,0,0,0); text-shadow: 0 0 0 #000; outline: 1px; outline-style: dotted; outline-offset: -1px; } .search select:focus { outline: 1px; outline-style: dotted; outline-offset: -1px; outline-color: #000; } .search select:hover, .search select:focus { opacity: 1; } .search option { /* Firefox ignores that on linux but the default color is close */ background-color: #ccc; } .search .search-controls { flex: 3; display: flex; flex-direction: row; align-items: center; } .filter { padding: 0.5em; background: #222; } .filter input { color: #fff; background: #333; } .filter input:focus { background: #444; } .filter button { pointer-events: none; /* filter button is for decoration only */ color: #666; font-size: 1.3em; height: 2em; right: 0.15em; } /* footer */ .footer { height: 25px; padding: 0.3rem 2.5rem; padding-left: 1.5rem; font-size: 0.8em; background: #aaa; color: #555; } .footer a { font-style: normal; padding-left: 0.3em; } .version a { color: #444; font-weight: 700; } .version em { margin-left: 0.3em; } .version em::before { margin-right: 0.3em; opacity: 0.5; } .go-top, .poweredby { float: right; } .poweredby { font-style: italic; float: right; } .go-top { margin-left: 1rem; line-height: 0; position: absolute; top: 0; right: 0; } .go-top:focus { outline: 0 none; } .go-top::before { color: #ccc; font-size: 1.4rem; background: #888; display: inline-block; text-align: center; width: 2rem; height: 1.4rem; top: 0.1rem; position: relative; -webkit-box-shadow: 0 0 0 1px #888; box-shadow: 0 0 0 1px #888; } .go-top:focus::before, .go-top:hover::before { color: #fff; background: #666; -webkit-box-shadow: 0 0 0 1px #666; box-shadow: 0 0 0 1px #666; } #file-download-link { margin-left: 1rem; } /* sidebar */ .sidebar { height: auto; z-index: 10; } .sidebar nav { background: #111; color: #fff; } .sidebar nav a { color: #fff; } .sidebar nav a:hover, .sidebar nav a:hover strong{ color: #6d7dd2; } .sidebar nav { font-size: 0.9em; padding: 1rem; } .sidebar nav ul { list-style: none; margin: 0; } .sidebar nav li.active a { color: #eee; color: #6d7dd2; font-weight: 700; } .sidebar nav ul { padding: 0; } /* reference popup */ #reference-popup-wrapper { position: absolute; display: inline-block; } #reference-popup { position: absolute; visibility: hidden; display: none; border: 2px solid black; background: white; padding: 5px; z-index: 10; overflow-y: scroll; min-height: 25vh; min-width: 40vw; max-height: 50vh; max-width: 75vw; @media only screen and (max-width: 1000px) { min-width: 100vw; } } #reference-popup::-webkit-scrollbar-corner { background: #666; } #reference-popup::-webkit-scrollbar-track { background: #666; } #reference-popup::-webkit-scrollbar-thumb { background: #ccc; } #loading-popup { position: absolute; visibility: hidden; border: 2px solid black; background: white; color: gray; padding: 5px; z-index: 10; } /* if javascript on/off */ .js .sidebar { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } .js .sidebar nav { will-change: transform; overflow-y: auto; -webkit-box-flex: 1; -ms-flex: 1; flex: 1; } .js .sidebar li li::before { color: #444; font-family: 'Ubuntu Mono', monospace; font-size: 1.29em; line-height: 1; } .js .sidebar li li::before { content: '├╴'; } .js .sidebar li li:last-of-type::before { content: '└╴'; } .js .sidebar li li li::before { content: '│\00a0├╴'; } .js .sidebar li li li:last-of-type::before { content: '│\00a0└╴'; } .js .sidebar li li:last-of-type li::before { content: '\00a0\00a0├╴'; } .js .sidebar li li:last-of-type li:last-of-type::before { content: '\00a0\00a0└╴'; } .js .sidebar .versions > li > span{ margin-left: 0.5em; } .js .sidebar .versions { margin-left: -0.4em; } .js .sidebar li { display: block; line-height: 1.5; } .js .sidebar .versions li span, .js .sidebar .versions li a { font-size: 0.9em; vertical-align: 0.12em; } .js .sidebar li span { color: #888; cursor: pointer; } .js .sidebar li span:hover::before { color: #fff; } .js .sidebar li span::before { color: #444; font-family: 'Ubuntu Mono', monospace; font-size: 1.3em; line-height: 1em; content: '▸'; position: relative; top: -0.05em; left: -0.35em; margin-right: -0.2em; } .js .sidebar li span.active::before { content: '▾'; } .js .sidebar .li-link { display: -webkit-box; display: -ms-flexbox; display: flex; } .js .sidebar .li-link::before { -webkit-box-flex: 0; -ms-flex: 0; flex: 0; } .js .sidebar .li-link a { word-break: break-word; -webkit-box-flex: 1; -ms-flex: 1; flex: 1; } /* tag menu tree closed */ .js .sidebar nav ul ul { display: none; } /* tag menu tree open */ .js .sidebar li span.active + ul { display: block; } .visible-if-js { display: none; } .js .visible-if-js { display: revert; } .no-js .sidebar:target { left: 0; height: 100vh; overflow: auto; } .no-js .sidebar nav ul span { display: none; } .no-js .versions { margin-top: 1em; } .no-js .sub-header { display: none; } .js .projects { display: none; } .no-js .select-projects { display: none; } .no-js .filter-input { display: none; } .no-js .icon-filter { display: none; } /* filter results */ .filter-results a { color: #fff; display: block; line-height: 1.2; } .filter-results a strong { color: #ffa264; } /* error */ .lxrerror { padding: 1.5em; } #error-details { margin-bottom: 1em; } #error-details-links * { display: block; } #error-details-links button { border: 0; background-color: transparent; padding: 0; margin: 0; } #report-error-details-header { color: gray; } /* ident */ .lxrident { padding: 1.5em; } .lxrident ul { list-style: none; padding: 0; padding-bottom: 0em; } .lxrident a { font-size: 0.8em; line-height: 1.55; color: #666; } .lxrident a:hover { border-bottom: 1px dotted #999999; } .lxrident a strong { font-size: 1.3em; font-weight: 400; color: #000; } .lxrident li li::before { color: #444; } .lxrident > pre { background-color: #edf0ff; color: #5764a8; padding: 1em; margin-top: 0; border-radius: 0.3em; } .lxrident li li::before { font-family: 'Ubuntu Mono', monospace; font-size: 1.1em; content: '├╴'; } .lxrident li li:last-of-type::before { content: '└╴'; } /* tree */ .lxrtree table { width: 100%; max-width: 100%; margin-top: 0.5em; margin-bottom: 2em; } .lxrtree table td:first-child { width: 1%; } .lxrtree table td a { white-space: nowrap; text-overflow: ellipsis; } .lxrtree { padding: 1em 0; } .lxrtree td a { display: block; padding: 0.15em 1.5em; } .lxrtree tr:hover { background: #ddd; } .tree-icon:before { display: inline-block; width: 1.5em; } .tree-icon.icon-tree:before { color: #ffa264; } .tree-icon.icon-blob:before { color: #a0a7ce; } .tree-icon.icon-back { color: #000; font-style: italic; opacity: 0.5; } .tree-icon.icon-back:hover { opacity: 1; } .size { opacity: 0.6; min-height: 1.5em; /* line_height + 2 * padding = 1.2em + 2 * 0.15em */ text-align: right; } /* source code */ .lxrcode { position: relative; } .lxrcode pre { margin: 0; padding-top: 1em; padding-bottom: 4em; min-height: 100%; } .lxrcode pre * { vertical-align: top; } .lxrcode, .highlighttable, .linenodiv, .highlight { height: 100%; } span[id^='codeline-'] { display: block; padding-left: 1em; } span[id^='codeline-'].line-highlight { width: 100%; height: 100%; background: #f8edc3; } .linenodiv { background: #e9e9e9; } .linenodiv pre a { color: #999; display: inline-block; padding: 0 1em; width: 100%; scroll-margin: 15vh; scroll-margin-top: 15vh; } .line-highlight { background: #ccc; } .linenodiv pre a.line-highlight, .linenodiv pre span { display: inline-block; } .linenodiv pre span.line-highlight > a, .linenodiv pre a:target { color: #444; } .linenodiv pre a:target { background: #ccc; pointer-events: none; } .linenodiv pre a:focus, .linenodiv pre a:hover { color: #000; } .linenodiv pre a:hover { background: #ddd; } .linenodiv pre a:focus { outline: 1px; outline-style: dotted; outline-offset: -1px; } .highlight .code a { color: inherit; font-weight: 700; background: linear-gradient(to bottom, #0000 10%, #f4f6ff 10%, #f4f6ff 90%, #0000 90%); border-radius: 0.2em; } .highlight .code a:hover { border-bottom: 1px dotted #000; } .code > div { height: 100%; } /* icons */ @font-face { font-family: "lxr"; src: url("/static/fonts/lxr.svg") format("svg"), url("/static/fonts/lxr.ttf") format("truetype"); font-weight: normal; font-style: normal; } [class^="icon-"]:before, [class*=" icon-"]:before { font-family: "lxr" !important; font-style: normal !important; font-weight: normal !important; font-variant: normal !important; text-transform: none !important; speak: none; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-blob:before { content: "\62"; } .icon-tree:before { content: "\64"; } .icon-back:before { content: "\68"; } .icon-search:before { content: "\6d"; } .icon-rss:before { content: "\67"; } .icon-github:before { content: "\61"; } .icon-facebook:before { content: "\65"; } .icon-mastodon:before { content: "\66"; } .icon-twitter:before { content: "\69"; } .icon-linkedin:before { content: "\6a"; } .icon-cross:before { content: "\6c"; } .icon-filter:before { content: "\6e"; } .icon-menu:before { content: "\6f"; } .icon-up:before { content: "\70"; } .icon-tag:before { content: "\63"; } /* highlight */ .highlight .code pre { color: #000; -moz-tab-size: 8; -o-tab-size: 8; tab-size: 8; white-space: pre; word-spacing: normal; word-break: normal; word-wrap: normal; -webkit-hyphens: none; -ms-hyphens: none; hyphens: none; } .highlight .code .hll { background-color: #ffffcc } .highlight .code { background: #ffffff } .highlight .code .c { color: slategray; font-style: italic; } /* Comment */ .highlight .code .err { color: #FF0000; background-color: #FFAAAA } /* Error */ .highlight .code .k { color: #008800 } /* Keyword */ .highlight .code .o { color: #666 } /* Operator */ .highlight .code .ch { color: #888888 } /* Comment.Hashbang */ .highlight .code .cm { color: slategray; font-style: italic; } /* Comment.Multiline */ .highlight .code .cp { color: #557799 } /* Comment.Preproc */ .highlight .code .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .code .c1 { color: slategray; font-style: italic; } /* Comment.Single */ .highlight .code .cs { color: #cc0000 } /* Comment.Special */ .highlight .code .gd { color: #A00000 } /* Generic.Deleted */ .highlight .code .ge { font-style: italic } /* Generic.Emph */ .highlight .code .gr { color: #FF0000 } /* Generic.Error */ .highlight .code .gh { color: #000080 } /* Generic.Heading */ .highlight .code .gi { color: #00A000 } /* Generic.Inserted */ .highlight .code .go { color: #888888 } /* Generic.Output */ .highlight .code .gp { color: #c65d09 } /* Generic.Prompt */ .highlight .code .gs { font-weight: bold } /* Generic.Strong */ .highlight .code .gu { color: #800080 } /* Generic.Subheading */ .highlight .code .gt { color: #0044DD } /* Generic.Traceback */ .highlight .code .kc { color: #008800 } /* Keyword.Constant */ .highlight .code .kd { color: #008800 } /* Keyword.Declaration */ .highlight .code .kn { color: #008800 } /* Keyword.Namespace */ .highlight .code .kp { color: #003388 } /* Keyword.Pseudo */ .highlight .code .kr { color: #008800 } /* Keyword.Reserved */ .highlight .code .kt { color: #333399 } /* Keyword.Type */ .highlight .code .m { color: #6600EE } /* Literal.Number */ .highlight .code .s { color: #de7f00 } /* Literal.String */ .highlight .code .na { color: #0000CC } /* Name.Attribute */ .highlight .code .nb { color: #007020 } /* Name.Builtin */ .highlight .code .nc { color: #BB0066 } /* Name.Class */ .highlight .code .no { color: #003366 } /* Name.Constant */ .highlight .code .nd { color: #555555 } /* Name.Decorator */ .highlight .code .ni { color: #880000 } /* Name.Entity */ .highlight .code .ne { color: #FF0000 } /* Name.Exception */ .highlight .code .nf { color: #0066BB } /* Name.Function */ .highlight .code .nl { color: #997700 } /* Name.Label */ .highlight .code .nn { color: #0e84b5 } /* Name.Namespace */ .highlight .code .nt { color: #007700 } /* Name.Tag */ .highlight .code .nv { color: #996633 } /* Name.Variable */ .highlight .code .ow { color: #000000 } /* Operator.Word */ .highlight .code .p { color: #666 } /* Text.Punctuation */ .highlight .code .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .code .mb { color: #6600EE } /* Literal.Number.Bin */ .highlight .code .mf { color: #6600EE } /* Literal.Number.Float */ .highlight .code .mh { color: #005588 } /* Literal.Number.Hex */ .highlight .code .mi { color: #0000DD } /* Literal.Number.Integer */ .highlight .code .mo { color: #4400EE } /* Literal.Number.Oct */ .highlight .code .sa { color: #de7f00 } /* Literal.String.Affix */ .highlight .code .sb { color: #de7f00 } /* Literal.String.Backtick */ .highlight .code .sc { color: #de7f00 } /* Literal.String.Char */ .highlight .code .dl { color: #de7f00 } /* Literal.String.Delimiter */ .highlight .code .sd { color: #a29900 } /* Literal.String.Doc */ .highlight .code .s2 { color: #de7f00 } /* Literal.String.Double */ .highlight .code .se { color: #a29900 } /* Literal.String.Escape */ .highlight .code .sh { color: #de7f00 } /* Literal.String.Heredoc */ .highlight .code .si { color: #de7f00 } /* Literal.String.Interpol */ .highlight .code .sx { color: #de7f00 } /* Literal.String.Other */ .highlight .code .sr { color: #a29900 } /* Literal.String.Regex */ .highlight .code .s1 { color: #de7f00 } /* Literal.String.Single */ .highlight .code .ss { color: #a29900 } /* Literal.String.Symbol */ .highlight .code .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .code .fm { color: #0066BB } /* Name.Function.Magic */ .highlight .code .vc { color: #336699 } /* Name.Variable.Class */ .highlight .code .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .code .vi { color: #3333BB } /* Name.Variable.Instance */ .highlight .code .vm { color: #996633 } /* Name.Variable.Magic */ .highlight .code .il { color: #0000DD } /* Literal.Number.Integer.Long */ /* layout */ main { min-height: calc(100vh - 45px - 25px); display: -webkit-box; display: -ms-flexbox; display: flex; position: relative; width: max-content; } .js .wrapper { position: absolute; top: 0; bottom: 0; left: 0; right: 0; width: auto; height: auto; overflow-x: auto; overflow-y: scroll; } .footer { position: fixed; bottom: 0; left: 0; right: 0; z-index: 2; } /* responsive */ .open-menu { margin-right: 1.2rem; margin-left: -0.5rem; } .open-menu::before { font-size: 1.5rem; line-height: 0; vertical-align: -0.31rem; } .close-menu { background: #222; color: #fff; position: absolute; text-align: center; padding-top: 0.5rem; font-size: 1.7rem; width: 2.8rem; height: 2.8rem; } .close-menu { margin-left: 0.3em; margin-top: -0.3em; } .sidebar { position: fixed; z-index: 30; top: 0; bottom: 0; left: -100%; width: 100%; height: auto; background: rgba(3, 8, 35, 0); } .sidebar .filter, .sidebar nav { width: 60%; max-width: 330px; } /* tablet and above */ @media screen and (min-width: 600px) { .filter input, .search input { padding: 0.9rem 1rem; } .search, .filter { padding: 0; } .filter { background: #222; } .close-menu { margin-left: 0; margin-top: 0; } .topbar { display: -webkit-box; display: -ms-flexbox; display: flex; } .breadcrumb { -webkit-box-flex: 1; -ms-flex: 1; flex: 1; } .search { width: 40%; } .sub-header { text-align: left; } } /* tablet only */ @media screen and (max-width: 1000px) { .sidebar { -webkit-transition: left 0.2s ease 0.2s, background 0.2s ease; transition: left 0.2s ease 0.2s, background 0.2s ease; } .show-menu-mobile .sidebar { position: fixed; left: 0; background: rgba(3, 8, 35, 0.5); } } /* desktop */ @media screen and (min-width: 1000px) { :root { --is-widescreen: true; } .close-menu { display: none; } .sidebar { position: absolute; top: -45px; bottom: -25px; -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; width: 235px; -webkit-box-flex: 0; -ms-flex: 0 0 235px; flex: 0 0 235px; } .sidebar .filter, .sidebar nav { width: 100%; } .sidebar nav { height: 100%; } .workspace { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; padding-left: 235px; } .show-menu .topbar, .no-js .topbar { padding-left: 235px; } .show-menu .footer, .no-js .footer { padding-left: calc(2.5rem + 235px); } .show-menu .sidebar { position: -webkit-sticky; position: sticky; left: 0; } .linenos { left: 235px; } .no-js .sidebar { left: 0; height: 100%; } } /* if browser support position: sticky */ @supports ((position: -webkit-sticky) or (position: sticky)) { .js .header { position: -webkit-sticky; position: sticky; left: 0; } .js .footer { position: -webkit-sticky; position: sticky; right: unset; } .js .topbar { position: -webkit-sticky; position: sticky; top: 0; left: 0; z-index: 2; } .js .sub-header { position: -webkit-sticky; position: sticky; left: 0; } .js .linenos { position: -webkit-sticky; position: sticky; left: 0; z-index: 1; } .js main { position: unset; } /* desktop */ @media screen and (min-width: 1000px) { .show-menu .js .linenos { left: 235px; } .js .workspace { padding-left: unset; } .js .sidebar { top: 0; height: 100vh; margin-top: -45px; margin-bottom: -25px; /* screen height minus potential horizontal scrollbar height */ height: calc(100vh - 18px); /* little hack to hide potential scrollbar gap */ -webkit-box-shadow: 0 18px 0 0 #111; box-shadow: 0 18px 0 0 #111; } } } /* === Banner =============================================================== */ /* Message banner in desktop mode */ @media screen and (min-width: 748px) { .message-banner { padding: 5px; -webkit-box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); -moz-box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); font-size: 14; font-family: Arial; font-weight: bold; width: 215px; height: 120px; text-align: center; border-radius: 20px; background: rgba(255,255,255,1); background: -moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(255,255,255,1)), color-stop(47%, rgba(246,246,246,1)), color-stop(100%, rgba(237,237,237,1))); background: -webkit-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); background: -o-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); background: -ms-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); background: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed', GradientType=0 ); } .message-banner p.title { font-size: 14px; margin: 0; } .message-banner .subtitle { font-size: 12px; position: relative; } .message-banner .container { height: 100%; display: flex; flex-direction: column; justify-content: space-evenly; } .message-banner .action { font-size: 14px; justify-content: center; display: none; } .message-banner .action-inner { border: 1px solid black; border-radius: 5px; padding: 2px 10px; } .message-banner .action-visible { display: flex; } } .icon-googleplus:before, .icon-mastodon:before { position: relative; left: 3px; } .select-projects { background-position: bottom 0.15em right 0.3em; width: 100%; } /* Message banner in mobile mode */ @media screen and (max-width: 748px) { .message-banner { width: 100%; padding: 3px; text-align: center; background: #757575; color: #fff; font-family: Arial; } .message-banner .title { margin: 0; font-weight: 900; text-wrap: pretty; } .message-banner .subtitle { padding-top: 2px; margin: 0; word-break: break-word; } .message-banner .action { display: none; } .message-banner .action-visible { display: none; } .message-link { width: 100%; } } /* === Fonts ================================================================ */ /* ubuntu-regular - cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext */ @font-face { font-family: 'Ubuntu'; font-style: normal; font-weight: 400; src: url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot'); /* IE9 Compat Modes */ src: local('Ubuntu Regular'), local('Ubuntu-Regular'), url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2') format('woff2'), /* Super Modern Browsers */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff') format('woff'), /* Modern Browsers */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf') format('truetype'), /* Safari, Android, iOS */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg#Ubuntu') format('svg'); /* Legacy iOS */ } /* ubuntu-italic - cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext */ @font-face { font-family: 'Ubuntu'; font-style: italic; font-weight: 400; src: url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot'); /* IE9 Compat Modes */ src: local('Ubuntu Italic'), local('Ubuntu-Italic'), url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2') format('woff2'), /* Super Modern Browsers */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff') format('woff'), /* Modern Browsers */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf') format('truetype'), /* Safari, Android, iOS */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg#Ubuntu') format('svg'); /* Legacy iOS */ } /* ubuntu-700 - cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext */ @font-face { font-family: 'Ubuntu'; font-style: normal; font-weight: 700; src: url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot'); /* IE9 Compat Modes */ src: local('Ubuntu Bold'), local('Ubuntu-Bold'), url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2') format('woff2'), /* Super Modern Browsers */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff') format('woff'), /* Modern Browsers */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf') format('truetype'), /* Safari, Android, iOS */ url('/static/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg#Ubuntu') format('svg'); /* Legacy iOS */ } /* === Header =============================================================== */ .header { min-height: 200px; background: #6D7DD2; background-image: url('/static/img/2penguins.svg'); background-repeat: no-repeat; background-size: 300px; background-position: bottom -82px right; } .header { display: -webkit-box; display: -ms-flexbox; display: flex; flex-direction: column; } .header h1 { font-family: 'Ubuntu Mono', monospace; font-style: italic; text-align: center; font-size: 3.2em; word-spacing: -0.4em; color: #fff; margin-bottom: 0; margin-top: 0; } .header h1 a { color: #fff; } .header h2 { font-family: 'Ubuntu Mono', monospace; text-align: center; font-size: 1em; font-weight: 400; margin-top: -0.5em; color: #fff; color: #ffa264; } .header em { font-style: normal; font-size: 0.9em; line-height: 1; text-align: center; float: right; padding: 0.5em 1em; position: absolute; bottom: 0; left: 0; right: 0; color: rgba(255,255,255,.7); background-color: rgba(255,255,255,.1); } .header em a { color: rgba(255,255,255,.9); } .header nav { text-align: center; width: 100%; } .header nav ul { list-style: none; } .header-main { display: grid; grid-template-columns: 235px 1fr 235px; } .banners { display: flex; justify-content: center; align-items: center; position: relative; } .under-topbar { height: 48px; } .nav-links { padding: 0.5em 1em; margin: 0; background-color: rgba(0,0,0,.5); width: 100%; font-size: 0.8em; line-height: 1.7; } .nav-links li { display: inline-block; margin: 0 0.5em; } .nav-links a { text-transform: uppercase; color: #9da9ea; } .nav-links a:hover { color: #fff; } .social-icons { position: absolute; top: 0.05rem; right: 0.3rem; padding: 0; margin: 0; display: inline-block; line-height: 1.5rem; /* little hack for firefox vertical align */ } .social-icons li { display: inline-block; font-size: 0; } .social-icons a::before { font-size: 1.65rem; color: #717fc5; } .social-icons a:focus::before, .social-icons a:hover::before { color: #fff; } /* responsive */ @media (max-width: 748px) { .banners { justify-content: flex-start; } .header .social-icons { position: static; background-color: rgba(0,0,0,.5); width: 100%; } .social-icons a::before { font-size: 1.5rem; color: #9da9ea; } .header-main { display: flex; flex-direction: column; } } @media (max-width: 520px) { .header h1 { font-size: 2.5em; font-size: 8vw; } .header h2 { font-size: 1em; font-size: 3vw; } } @media (max-width: 330px) { .header h1 { margin-top: 1.8em; } } /* === Autocomplete ========================================================= */ /* * @license MIT * * Autocomplete.js v2.7.1 * Developed by Baptiste Donaux * http://autocomplete-js.com * * (c) 2017, Baptiste Donaux * * Modified by Maxime Chretien * for the needs of Elixir Cross Referencer * https://github.com/bootlin/elixir * */ input[data-autocomplete] { border-color: #808080; border-style: none none solid none; border-width: 0px 0px 1px 0px; margin: 0px; padding: 5px; width: 100%; } .autocomplete { position: absolute; transition: all 0.5s ease 0s; max-height: 0; overflow-y: hidden; transition-duration: 0.3s; transition-property: all; transition-timing-function: cubic-bezier(0, 1, 0.5, 1); } .autocomplete:active, .autocomplete:focus, .autocomplete:hover { background-color: #EEEEEE; transition: all 0.5s ease 0s; } .autocomplete:empty { display: none; } .autocomplete > ul { list-style-type: none; margin: 0; padding: 0; } .autocomplete > ul > li { cursor: pointer; padding: 5px 0 5px 10px; white-space: nowrap; } .autocomplete > ul > li.active, .autocomplete > ul > li:active, .autocomplete > ul > li:focus, .autocomplete > ul > li:hover { background-color: #DDDDDD; transition: all 0.5s ease 0s; } .autocomplete > ul > li.active a:active, .autocomplete > ul > li:active a:active, .autocomplete > ul > li:focus a:active, .autocomplete > ul > li:hover a:active, .autocomplete > ul > li.active a:focus, .autocomplete > ul > li:active a:focus, .autocomplete > ul > li:focus a:focus, .autocomplete > ul > li:hover a:focus, .autocomplete > ul > li.active a:hover, .autocomplete > ul > li:active a:hover, .autocomplete > ul > li:focus a:hover, .autocomplete > ul > li:hover a:hover { text-decoration: none; } .autocomplete > ul > li.locked { cursor: inherit; } .autocomplete.open { display: block; transition: all 0.5s ease 0s; background-color: #EEEEEE; max-height: 500px; overflow-y: auto; transition-duration: 0.3s; transition-property: all; transition-timing-function: cubic-bezier(0, 1, 0.5, 1); z-index: 100; } .autocomplete.open:empty { display: none; } ================================================ FILE: t/050-testhelpers.t ================================================ #!/usr/bin/env perl # t/50-testhelpers.t: test TestHelpers.pm # # Copyright (c) 2020 Christopher White, . # Copyright (c) 2020 D3 Engineering, LLC. # # Elixir is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. use FindBin '$Bin'; use lib $Bin; use Test::More; use TestEnvironment; use TestHelpers qw(:all); # === line_mark_string ======================================================= our ($fn, $refln, $ln); sub level1 { eval line_mark_string 1, '$fn = __FILE__; $ln = __LINE__'; ok !$@, 'level1 no errors'; } $refln = __LINE__; level1; is $fn, __FILE__, 'level1 file'; cmp_ok $ln, '==', $refln, 'level1 line'; sub level2 { level2_inner(); } sub level2_inner { eval line_mark_string 2, '$fn = __FILE__; $ln = __LINE__'; ok !$@, 'level2_inner no errors'; } $refln = __LINE__; level2; is $fn, __FILE__, 'level2 file'; cmp_ok $ln, '==', $refln, 'level2 line'; done_testing; ================================================ FILE: t/100-basic.t ================================================ #!/usr/bin/env perl # 100-basic.t: Test basic elixir functions against the files in tree/ . # # Copyright (c) 2020 D3 Engineering, LLC. # By Christopher White, . # # Elixir is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. use autodie; # note: still need to check system() calls manually use FindBin '$Bin'; use lib $Bin; use File::Spec; use Test::More; use TestEnvironment; use TestHelpers; # =========================================================================== # Main # Set up my $tree_src_dir = sibling_abs_path('tree'); my $tenv = TestEnvironment->new; # Check programs my $script_sh = $tenv->script_sh; my $update_py = $tenv->update_py; my $query_py = $tenv->query_py; ok_or_die( (-f $script_sh && -r _ && -x _), 'script.sh executable', "Could not find executable script.sh at $script_sh"); ok_or_die( (-f $update_py && -r _ && -x _), 'update.py executable', "Could not find executable update.py at $update_py"); ok_or_die( (-f $query_py && -r _ && -x _), 'query.py executable', "Could not find executable query.py at $query_py"); $tenv->build_repo($tree_src_dir); $tenv->update_env; # Set LXR_REPO_DIR diag $tenv->report; # Check for tags in `script.sh list-tags`, as a sanity check before # building the test DB my @tags = `$script_sh list-tags`; die("Could not list tags: $! ($?)") if $?; ok_or_die( @tags == 1, 'One tag present', "Not one tag (@{[scalar @tags]})"); ok_or_die( $tags[0] =~ /^v5.4$/, 'Found the correct tag', 'Not the tag we expected'); $tenv->build_db; $tenv->update_env; # Set LXR_DATA_DIR my $db_dir = $tenv->lxr_data_dir; ok_or_die( -d $db_dir, 'database dir exists', "Database dir $db_dir not present"); # Make sure the database has the files we expect ok( (-r File::Spec->catfile($db_dir, $_)), "$_ exists" ) foreach qw(blobs.db definitions.db filenames.db hashes.db references.db variables.db versions.db); # Spot-check some identifiers run_produces_ok('ident query (nonexistent)', [$query_py, qw(v5.4 ident SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH C)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, qr{^\s*$}], MUST_SUCCEED); run_produces_ok('ident query (existent)', [$query_py, qw(v5.4 ident i2c_acpi_notify C)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => qr{drivers/i2c/i2c-core-acpi\.c.+\b402\b.+\bfunction\b} }, { ref => qr{drivers/i2c/i2c-core-acpi\.c.+\b439} }, ], MUST_SUCCEED); run_produces_ok('ident query (existent, #131)', [$query_py, qw(v5.4 ident class C)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => qr{issue131\.h.+\b9\b.+\bstruct\b} }, { ref => qr{issue131\.h.+\b13} }, ], MUST_SUCCEED); run_produces_ok('ident query (existent, #150)', [$query_py, qw(v5.4 ident memset C)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => qr{issue150\.S.+\b7\b.+\bfunction\b} }, { ref => qr{i2c-core-acpi\.c.+\b121\b} } ], MUST_SUCCEED); run_produces_ok('ident query (ENTRY that should not be detected, #150)', [$query_py, qw(v5.4 ident), 'HYPERVISOR_##hypercall', 'C'], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => { not => qr{hypercall\.S} } }, ], MUST_SUCCEED); run_produces_ok('ident query (ENTRY that should not be detected, #150)', [$query_py, qw(v5.4 ident), '0xfffffffe', 'C'], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => { not => qr{bcm74xx_sprom\.c} } }, ], MUST_SUCCEED); run_produces_ok('ident query (existent, #228)', [$query_py, qw(v5.4 ident sys_init_module C)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => qr{syscall_define\.c.+\b1\b.+\bfunction\b} } ], MUST_SUCCEED); # Spot-check some files run_produces_ok('file query (nonexistent)', [$query_py, qw(v5.4 file /SOME_NONEXISTENT_FILENAME_XYZZY_PLUGH)], [{not => qr{\S}}]); run_produces_ok('file query (existent), .h', [$query_py, qw(v5.4 file /drivers/i2c/i2c-dev.c)], [qr{\S}], MUST_SUCCEED); run_produces_ok('file query (existent), .c', [$query_py, qw(v5.4 file /drivers/i2c/i2c-dev.c)], [qr{i2c-dev\.c}, qr{\bVogl\b}], MUST_SUCCEED); run_produces_ok('file query (existent), .h', [$query_py, qw(v5.4 file /drivers/i2c/i2c-core.h)], [qr{i2c-core\.h}, qr{\bWe\b}], MUST_SUCCEED); done_testing; ================================================ FILE: t/200-api.t ================================================ #!/usr/bin/env perl # 200-api.t: Test elixir's REST api against the files in tree/ . # # Copyright (c) 2020 Tamir Carmeli, . # # Elixir is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. use FindBin '$Bin'; use lib $Bin; use Test::More; use TestEnvironment; use TestHelpers; my $tenv = TestEnvironment->new; $tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; # Test the api using pytest. Prints the test results run_produces_ok('api pytest suite', ["pytest", "-v", "t"], [], MUST_SUCCEED, 1); done_testing; ================================================ FILE: t/300-doc-comments.t ================================================ #!/usr/bin/env perl # 300-doc-comments.t: Test elixir doc-comment extraction against the files in tree/ . # # Copyright (c) 2020 Christopher White. # Copyright (c) 2020 D3 Engineering, LLC. # By Christopher White, . # # Elixir is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. use autodie; # note: still need to check system() calls manually use FindBin '$Bin'; use lib $Bin; use File::Spec; use Test::More; use TestEnvironment; use TestHelpers; # =========================================================================== # Main # Set up my $tenv = TestEnvironment->new; $tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; ok_or_die( -d $tenv->lxr_data_dir, 'database dir exists', "Database dir @{[$tenv->lxr_data_dir]} not present"); # Spot-check some identifiers run_produces_ok('doc-comment query (nonexistent)', [$tenv->query_py, qw(v5.4 ident SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH C)], [ qr{^Documented in:}, {doc => { not => qr{/} }}, # No file paths in the doc section ], MUST_SUCCEED); run_produces_ok('doc-comment query (existent but not documented)', [$tenv->query_py, qw(v5.4 ident gsb_buffer C)], # in drivers/i2c/i2c-core-acpi.c [ qr{^Documented in:}, {doc => { not => qr{/} }} ], MUST_SUCCEED); run_produces_ok('ident query (existent, function, documented in C file)', [$tenv->query_py, qw(v5.4 ident i2c_acpi_get_i2c_resource C)], [ qr{^Documented in:}, {doc => qr{drivers/i2c/i2c-core-acpi\.c.+\b45\b}}, ], MUST_SUCCEED); run_produces_ok('ident query (existent, function, documented in C file, #102)', [$tenv->query_py, qw(v5.4 ident documented_function_XYZZY C)], [ qr{^Documented in:}, {doc => qr{issue102\.c.+\b6\b}}, ], MUST_SUCCEED); # Non-functions run_produces_ok('ident query (existent, enum, documented in H file)', [$tenv->query_py, qw(v5.4 ident memblock_flags C)], [ qr{^Documented in:}, {doc => qr{\bmemblock\.h.+\b28\b}}, ], MUST_SUCCEED); run_produces_ok('ident query (existent, enum, not documented)', [$tenv->query_py, qw(v5.4 ident rseq_cpu_id_state C)], # uapi/linux/rseq.h:16 [ qr{^Documented in:}, {doc => { not => qr{/} }} ], MUST_SUCCEED); run_produces_ok('ident query (existent, struct, documented in H file)', [$tenv->query_py, qw(v5.4 ident memblock_region C)], [ qr{^Documented in:}, {doc => qr{\bmemblock\.h.+\b42\b}}, ], MUST_SUCCEED); run_produces_ok('ident query (existent, struct, not documented)', [$tenv->query_py, qw(v5.4 ident epoll_event C)], # eventpoll.h:77 [ qr{^Documented in:}, {doc => { not => qr{/} }} ], MUST_SUCCEED); run_produces_ok('ident query (existent, macro, documented in H file)', [$tenv->query_py, qw(v5.4 ident for_each_mem_range C)], [ qr{^Documented in:}, {doc => qr{\bmemblock\.h.+\b148\b}}, ], MUST_SUCCEED); run_produces_ok('ident query (existent, macro, not documented)', [$tenv->query_py, qw(v5.4 ident MEMBLOCK_LOW_LIMIT C)], # memblock.h:343 [ qr{^Documented in:}, {doc => { not => qr{/} }} ], MUST_SUCCEED); # Specific cases from #134 # Like regmap_update_bits_base() run_produces_ok('ident query (existent, function, documented in C file, nonstandard doc comment, #134)', [$tenv->query_py, qw(v5.4 ident issue134_function1 C)], [ qr{^Documented in:}, {doc => qr{\bissue134\.c.+\b9\b}}, ], MUST_SUCCEED); # Like wait_for_completion() run_produces_ok('ident query (existent, function, documented in C file, nonstandard doc comment, #134)', [$tenv->query_py, qw(v5.4 ident issue134_function2 C)], [ qr{^Documented in:}, {doc => qr{\bissue134\.c.+\b25\b}}, ], MUST_SUCCEED); # Like v4l2_fwnode_endpoint_parse() run_produces_ok('ident query (existent, prototype, documented in C file), #134', [$tenv->query_py, qw(v5.4 ident issue134_function3 C)], [ qr{^Documented in:}, {doc => qr{\bissue134\.c.+\b38\b}}, ], MUST_SUCCEED); # #186 run_produces_ok('No warnings on macro, #186', [$tenv->find_doc, File::Spec->catfile($tenv->lxr_repo_dir, 'issue186.c')], [ ], MUST_SUCCEED); # warnings appear on stderr, failing the MUST_SUCCEED checks # #186 counterexamples run_produces_ok('ident query (existent, documented as function), #186 counterexample', [$tenv->query_py, qw(v5.4 ident i186c_fn1 C)], [ qr{^Documented in:}, {doc => qr{\bissue186-counterexamples\.c.+\b5\b}}, ], MUST_SUCCEED); run_produces_ok('ident query (existent, documented as macro), #186 counterexample', [$tenv->query_py, qw(v5.4 ident i186c_fn2 C)], [ qr{^Documented in:}, {doc => qr{\bissue186-counterexamples\.c.+\b20\b}}, ], MUST_SUCCEED); # #188 run_produces_ok('No warnings on indented #define, #188', [$tenv->find_doc, File::Spec->catfile($tenv->lxr_repo_dir, 'issue188.c')], [ ], MUST_SUCCEED); # warnings appear on stderr, failing the MUST_SUCCEED checks # #192 # Like request_firmware() run_produces_ok('ident query (existent, function, documented in C file, type on preceding line, #192)', [$tenv->query_py, qw(v5.4 ident issue192a C)], [ qr{^Documented in:}, {doc => qr{\bissue192\.c.+\b5\b}}, ], MUST_SUCCEED); run_produces_ok('ident query (existent, function, documented in C file, uppercase return type on preceding line, #192)', [$tenv->query_py, qw(v5.4 ident issue192b C)], [ qr{^Documented in:}, {doc => qr{\bissue192\.c.+\b15\b}}, ], MUST_SUCCEED); ######################################################################### done_testing; ================================================ FILE: t/400-web.t ================================================ #!/usr/bin/env perl # t/400-web.pl: Test web.py # # Copyright (c) 2020 Christopher White, . # # Elixir is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. use FindBin '$Bin'; use lib $Bin; use Test::More; use TestEnvironment; use TestHelpers; # =========================================================================== # Main # Set up for the tests my $tenv = TestEnvironment->new; $tenv->build_repo(sibling_abs_path('tree')); # dies on error $tenv->build_db; $tenv->update_env; diag $tenv->report; http_request_ok 'index query', $tenv, '/testproj/latest/source', [ qr{^Content-Type:\s*text/html}, qr{href="latest/source/issue102.c"}, qr{href="latest/source/arch"} ], 1; http_request_ok 'identifier query', $tenv, '/testproj/v5.4/ident/gsb_buffer', [ qr{^Content-Type:\s*text/html}, qr{\bgsb_buffer\b}, qr{"v5.4/source/drivers/i2c/i2c-core-acpi.c\#L23".+? drivers/i2c/i2c-core-acpi.c.+? line[ ]23.+? \bstruct\b}x ], 1; # Doc comments: testcases pulled from t/300 http_request_ok 'doc-comment query (nonexistent)', $tenv, '/testproj/v5.4/ident/SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH', [ qr{^Content-Type:\s*text/html}, qr{Identifier not used}i], 1; http_request_ok 'doc-comment query (existent but not documented)', $tenv, '/testproj/v5.4/ident/gsb_buffer', # in drivers/i2c/i2c-core-acpi.c [ qr{^Content-Type:\s*text/html}, { not => qr{\bDocumented in\b} }, ], 1; http_request_ok 'ident query (existent, function, documented in C file)', $tenv, '/testproj/v5.4/ident/i2c_acpi_get_i2c_resource', [ qr{^Content-Type:\s*text/html}, qr{\bDocumented in \d}, {doc => qr{drivers/i2c/i2c-core-acpi\.c.+\b45\b}}, ], 1; http_request_ok 'ident query (existent, function, documented in C file, #102)', $tenv, '/testproj/v5.4/ident/documented_function_XYZZY', [ qr{^Content-Type:\s*text/html}, qr{\bDocumented in \d}, {doc => qr{issue102\.c.+\b6\b}}, ], 1; done_testing; ================================================ FILE: t/TestClass.pm ================================================ # A minified OOP library - https://metacpan.org/pod/distribution/Mo/ReadMe.pod package TestClass; # use Mo qw(build default is required import); # The following line of code was produced from the previous line by # Mo::Inline version 0.40 no warnings;my$M=__PACKAGE__.'::';*{$M.Object::new}=sub{my$c=shift;my$s=bless{@_},$c;my%n=%{$c.'::'.':E'};map{$s->{$_}=$n{$_}->()if!exists$s->{$_}}keys%n;$s};*{$M.import}=sub{import warnings;$^H|=1538;my($P,%e,%o)=caller.'::';shift;eval"no Mo::$_",&{$M.$_.::e}($P,\%e,\%o,\@_)for@_;return if$e{M};%e=(extends,sub{eval"no $_[0]()";@{$P.ISA}=$_[0]},has,sub{my$n=shift;my$m=sub{$#_?$_[0]{$n}=$_[1]:$_[0]{$n}};@_=(default,@_)if!($#_%2);$m=$o{$_}->($m,$n,@_)for sort keys%o;*{$P.$n}=$m},%e,);*{$P.$_}=$e{$_}for keys%e;@{$P.ISA}=$M.Object};*{$M.'build::e'}=sub{my($P,$e)=@_;$e->{new}=sub{$c=shift;my$s=&{$M.Object::new}($c,@_);my@B;do{@B=($c.::BUILD,@B)}while($c)=@{$c.::ISA};exists&$_&&&$_($s)for@B;$s}};*{$M.'default::e'}=sub{my($P,$e,$o)=@_;$o->{default}=sub{my($m,$n,%a)=@_;exists$a{default}or return$m;my($d,$r)=$a{default};my$g='HASH'eq($r=ref$d)?sub{+{%$d}}:'ARRAY'eq$r?sub{[@$d]}:'CODE'eq$r?$d:sub{$d};my$i=exists$a{lazy}?$a{lazy}:!${$P.':N'};$i or ${$P.':E'}{$n}=$g and return$m;sub{$#_?$m->(@_):!exists$_[0]{$n}?$_[0]{$n}=$g->(@_):$m->(@_)}}};*{$M.'is::e'}=sub{my($P,$e,$o)=@_;$o->{is}=sub{my($m,$n,%a)=@_;$a{is}or return$m;sub{$#_&&$a{is}eq'ro'&&caller ne'Mo::coerce'?die$n.' is ro':$m->(@_)}}};*{$M.'required::e'}=sub{my($P,$e,$o)=@_;$o->{required}=sub{my($m,$n,%a)=@_;if($a{required}){my$C=*{$P."new"}{CODE}||*{$M.Object::new}{CODE};no warnings 'redefine';*{$P."new"}=sub{my$s=$C->(@_);my%a=@_[1..$#_];die$n." required"if!exists$a{$n};$s}}$m}};my$i=\&import;*{$M.import}=sub{(@_==2 and not$_[1])?pop@_:@_==1?push@_,grep!/import/,@f:();goto&$i};@f=qw[build default is required import];use strict;use warnings; 1; ================================================ FILE: t/TestEnvironment.pm ================================================ #!/usr/bin/env perl # TestEnvironment.pm: A class representing an Elixir test environment. # See license information at end of file. # # For a cleaner view of the documentation, run # perldoc TestEnvironment.pm # (on Ubuntu, you may need to install the perl-doc package first.) # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses only core Perl modules, and modules bundled with # the Elixir distribution. =head1 NAME TestEnvironment - Class representing an Elixir test environment =head1 SYNOPSIS use TestEnvironment; my $tenv = TestEnvironment->new; $tenv->build_repo($source_path); # Make a git repo $tenv->build_db(); # Run update.py $tenv->export_env; # Set $LXR_* environment vars # Now run tests against the database in $db_path This module creates a temporary project dir and populates it with repo and data subdirs in a single project, named "testproj". =cut package TestEnvironment; use TestClass; # Now we are a class use autodie; # note: still need to check system() calls manually use Cwd qw(abs_path); use File::Path qw(remove_tree); use File::Spec; use File::Temp 0.14 qw(tempdir); use FindBin; use IO::Select; use IPC::Open3; use Symbol; use Test::More; use TestHelpers; use constant PROJECT => 'testproj'; =head1 ATTRIBUTES =head2 lxr_proj_dir C<$lxr_proj_dir> is the value to use in the C environment variable. =head2 lxr_data_dir C<$lxr_data_dir> is the value to use in the C environment variable. =head2 lxr_repo_dir C<$lxr_repo_dir> is the value to use in the C environment variable. =head2 script_sh The path to C. Assigned by default using C if not specified. =head2 query_py As L, but for C. =head2 update_py As L, but for C. =head2 web_py As L, but for C. =head2 find_doc As L, but for C. =cut has lxr_proj_dir => (); has lxr_data_dir => (); has lxr_repo_dir => (); has script_sh => ( default => sub { find_program('script.sh') } ); has query_py => ( default => sub { find_program('query.py') } ); has update_py => ( default => sub { find_program('update.py') } ); has web_py => ( default => sub { find_program(qw(http web.py)) } ); has find_doc => ( default => sub { find_program('find-file-doc-comments.pl') } ); # Internal attributes # a variable representing the temporary project directory. # When this goes out of scope, the directory will be removed. has _proj_dir_token => (); =head1 MEMBER FUNCTIONS =head2 build_repo Create a Git repo and tag it. Usage: $tenv->build_repo($source_tree_dir); C<$source_tree_dir> is the directory holding the tree of source files you want to index. Dies on error. On success, returns the instance, for chaining. =cut sub build_repo { my ($self, $tree_src_dir) = @_; die "Incorrect parameters" unless @_==2 && ref $self && $tree_src_dir; die "Need a source dir" unless $tree_src_dir; die "No repo dir" unless $self->lxr_repo_dir; my $tempdir_path = $self->lxr_repo_dir; my @gitopts = ( '-C', $tempdir_path, '-c', 'init.defaultBranch=main', '-c', 'user.name=test@example.com', '-c', 'user.email=test'); diag "Using temporary directory $tempdir_path"; run_program('git', @gitopts, 'init', $tempdir_path) or die("git init failed"); run_program('sh', '-c', "tar cf - -C \"$tree_src_dir\" . | tar xf - -C \"$tempdir_path\"") or die("Could not copy files into $tempdir_path"); run_program('sh', '-c', "chown -R `whoami`:`id -ng` \"$tempdir_path\""); run_program('sh', '-c', "chmod -R 775 \"$tempdir_path\""); run_program('git', @gitopts, 'add', '.') or die("git add failed"); run_program('git', @gitopts, 'commit', '-am', 'Initial commit') or die("git commit failed"); run_program('git', @gitopts, 'tag', 'v5.4') or die("git tag failed"); return $self; } #build_repo() =head2 build_db Build a test database for the repository. L must be set before calling this. Usage: $tenv->build_db() Dies on error. On success, returns the instance, for chaining. B: This function will remove the contents of C<< $tenv->lxr_data_dir >> unconditionally. =cut sub build_db { my $self = shift; die "No parameters allowed" if @_; die "No repo dir" unless $self->lxr_repo_dir; die "No data dir" unless $self->lxr_data_dir; my $db_dir = $self->lxr_data_dir; if(-e $db_dir) { # Remove any existing DB dir remove_tree($db_dir); mkdir($db_dir) or die "Could not create fresh $db_dir"; } local $ENV{LXR_REPO_DIR} = $self->lxr_repo_dir; local $ENV{LXR_DATA_DIR} = $db_dir; run_program($self->update_py) or die "Could not create database from $ENV{LXR_REPO_DIR} in $ENV{LXR_DATA_DIR}"; return $self; } #build_db() =head2 update_env Set the C, C, and C environment variables. Will not set a variable if the corresponding member does not have a value. Returns the instance, for chaining. =cut sub update_env { my $self = shift; $ENV{LXR_PROJ_DIR} = $self->lxr_proj_dir if $self->lxr_proj_dir; $ENV{LXR_REPO_DIR} = $self->lxr_repo_dir if $self->lxr_repo_dir; $ENV{LXR_DATA_DIR} = $self->lxr_data_dir if $self->lxr_data_dir; return $self; } #update_env() =head2 report Returns a human-readable report of the current environment's state. =cut sub report { my $self = shift; return <lxr_proj_dir || '']} Repository: @{[$self->lxr_repo_dir || '']} Database: @{[$self->lxr_data_dir || '']} script.sh: @{[$self->script_sh || '']} update.py: @{[$self->update_py || '']} query.py: @{[$self->query_py || '']} web.py: @{[$self->web_py || '']} find-file-doc-comments.pl: @{[$self->find_doc || '']} EOT } #report() =head2 make_web_request Request a URL from L. Usage: my $html = $tenv->make_web_request($url); # Returns the HTML from stdout, or dies my ($exit_status, $lrStdout, $lrStderr) = $tenv->make_web_request($url); # Returns the shell exit status, stdout text, and stderr text. See L for the details of the return values in the second case. =cut sub make_web_request { my ($self, $url) = @_; $self->update_env; # just in case local $ENV{REQUEST_URI} = $url; diag "Requesting `$url'"; my ($exit_status, $lrStdout, $lrStderr) = run_program($self->web_py); if(!wantarray) { return $lrStdout; } else { return ($exit_status, $lrStdout, $lrStderr); } } #make_web_request() =head2 BUILD Constructor. Creates the temporary project dir. =cut sub BUILD { my $self = shift; my $temp_proj_dir = tempdir(CLEANUP => 1); my $proj_dir = abs_path($temp_proj_dir); # Make the directory structure mkdir File::Spec->catdir($proj_dir, PROJECT); my $data_dir = File::Spec->catdir($proj_dir, PROJECT, 'data'); my $repo_dir = File::Spec->catdir($proj_dir, PROJECT, 'repo'); mkdir $data_dir; mkdir $repo_dir; # Save the paths $self->_proj_dir_token($temp_proj_dir); $self->lxr_proj_dir($proj_dir); $self->lxr_data_dir($data_dir); $self->lxr_repo_dir($repo_dir); } #BUILD() =head2 DESTROY Destructor. Called automatically. =cut sub DESTROY { local($., $@, $!, $^E, $?); my $self = shift; # Release the temporary directories $self->_proj_dir_token(undef); } #DESTROY() 1; __END__ =head1 AUTHOR Christopher White, C<< >> =head1 COPYRIGHT Copyright (c) 2020 D3 Engineering, LLC. Elixir is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Elixir 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Elixir. If not, see . =cut ================================================ FILE: t/TestHelpers.pm ================================================ #!/usr/bin/env perl # TestHelpers.pm: Common routines for use in tests. # See license information at end of file. # # For a cleaner view of the documentation, run # perldoc TestHelpers.pm # (on Ubuntu, you may need to install the perl-doc package first.) # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. =head1 NAME TestHelpers - Common routines for use in tests =head1 SYNOPSIS C, and all the functions below will be exported. TestHelpers also turns on L and L. =cut package TestHelpers; use strict; use warnings; use autodie; # note: still need to check system() calls manually use Cwd qw(abs_path); use File::Spec; use File::Temp 0.14 qw(tempdir); use FindBin; use IO::Select; use IPC::Open3; use Symbol; use Test::More; # Automatically export all the functions listed in @EXPORT. The functions # in @EXPORT_OK can be exported on request. use parent 'Exporter'; our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); BEGIN { @EXPORT = qw(sibling_abs_path find_program run_program ok_or_die run_produces_ok http_request_ok MUST_SUCCEED); @EXPORT_OK = qw(line_mark_string); %EXPORT_TAGS = ( all => [@EXPORT, @EXPORT_OK], ); } #BEGIN # Forwards for internal functions sub line_mark_string; # =========================================================================== =head1 CONSTANTS =head2 MUST_SUCCEED True. So that calls to L will be more self-explanatory. =cut use constant MUST_SUCCEED => !!1; =head1 FUNCTIONS These are helper routines that generally perform specific tasks. =head2 sibling_abs_path Return the absolute path of a file or directory in the same directory as this file. Usage: $path = sibling_abs_path('name'); =cut sub sibling_abs_path { return File::Spec->rel2abs(File::Spec->catfile($FindBin::Bin, @_)); } #sibling_abs_path() =head2 find_program Looks for a program in the parent directory of this script. Usage: $path = find_program(['subdir', ]'program name') =cut sub find_program { my $pgm_file = pop; # Last arg my @pgm_dirs = @_; # Any args before the last are additional dirs. my ($my_vol, $my_dirs, undef) = File::Spec->splitpath($FindBin::Bin, 1); # 1 => is a dir # Go up to the parent of the directory holding this file my @my_dirs = File::Spec->splitdir($my_dirs); die "Cannot run from the root directory" unless @my_dirs >= 2; pop @my_dirs; my $dest_dirs = File::Spec->catdir(@my_dirs, @pgm_dirs); return File::Spec->catpath($my_vol, $dest_dirs, $pgm_file); } #find_program() =head2 run_program Print a command, then run it. Can be used three ways: =over =item In void context Returns if system() and the command succeed, dies otherwise. Usage: run_program('program', 'arg1', ...); =item In scalar context Returns true if system() and the command succeed, false otherwise. Usage: my $ok = run_program('program', 'arg1', ...); =item In list context Returns the exit status, stdout, and stderr. Usage: my ($exit_status, $lrStdout, $lrStderr) = run_program('program', 'arg1', ...); # Returns the shell exit status, stdout text, and stderr text. C<$lrStdout> and C<$lrStderr> are references to the lists of output lines on the respective handles. C<$exit_status> is C<128+signal> if the process was killed by C, for consistency with bash (L). =back =cut sub _run_and_capture; # forward sub _check_queries; # forward sub run_program { if(wantarray) { goto &_run_and_capture; } my $errmsg; diag "Running @_"; my $status = system(@_); if ($status == -1) { $errmsg = "failed to execute $_[0]: $!"; } elsif ($status & 127) { $errmsg = sprintf "$_[0] died with signal %d, %s coredump\n", ($status & 127), ($status & 128) ? 'with' : 'without'; } elsif($status != 0) { $errmsg = sprintf "$_[0] exited with value %d\n", $status >> 8; } else { diag "$_[0] reported success"; } if($errmsg) { die $errmsg unless defined wantarray; diag $errmsg; } return($status == 0); } #run_program() =head2 ok_or_die Run a test, but die if it fails. Usage: ok_or_die( , 'description', 'what to print if it dies' ) =cut sub ok_or_die { my ($cond, $msg, $err_msg) = @_; my (undef, $filename, $line) = caller; my $retval = eval line_mark_string 1, < $mustSucceed, $printOutput) The test passes if each condition in C<@conditions> is true. If C<$mustSucceed> is true, also tests for exit status 0 and empty stderr. If C<$printOutput> is true, prints the output of C<@program_and_args>. =head3 Conditions that can be used any time =over =item * A regex: true if the regex matches at least one line in the output of C<@program_and_args> =item * C<< { not => regex } >>: true if the regex is NOT found in any line of the output of C<@program_and_args>. =back =head3 Conditions for the output of C =over =item * C<< { def => regex } >>: true if the regex matches at least one line in the "Symbol Definitions" section of the output of C<@program_and_args>. =item * C<< { ref => regex } >>: true if the regex matches at least one line in the "Symbol References" section of the output of C<@program_and_args>. =item * C<< { doc => regex } >>: true if the regex matches at least one line in the "Documented in" section of the output of C<@program_and_args>. =back =cut # _run_and_capture: run a program and return its exit status and output. # Usage: # my ($exit_status, \@stdout, \@stderr) = run_program('program', 'arg1', ...); sub _run_and_capture { my ($in , $out, $err); # Filehandles $err = Symbol::gensym; diag "Running @_"; my $pid = open3($in, $out, $err, @_); my (@outlines, @errlines); # Captured output my $s = IO::Select->new; $s->add($out); $s->add($err); while(my @ready = $s->can_read) { for my $fh (@ready) { if(eof($fh)) { $s->remove($fh); next; } if($fh == $out) { push @outlines, scalar readline $fh; } else { push @errlines, scalar readline $fh; } } } waitpid $pid, 0; my $exit_status = $?; $exit_status = ($exit_status & 127) + 128 if $exit_status & 127; # Killed by signal return ($exit_status, \@outlines, \@errlines); } #_run_and_capture() sub run_produces_ok { my ($desc, $lrProgram, $lrConditions, $mustSucceed, $printOutput) = @_; my ($exit_status, $outlines, $errlines) = _run_and_capture(@$lrProgram); _check_queries($desc, $lrConditions, $mustSucceed, $printOutput, $exit_status, $outlines, $errlines); } #run_produces_ok() sub _check_queries { my ($desc, $lrConditions, $mustSucceed, $printOutput, $exit_status, $lrStdout, $lrStderr) = @_; my @outlines = @$lrStdout; my @errlines = @$lrStderr; if ($printOutput) { diag "@outlines"; } # Check for and report Python errors foreach(@outlines, @errlines) { if (/^.*?(\S+)\s+contains the description of this error/) { my $logfn = $1; no autodie; open my $logfh, '<', $logfn or warn("Could not open Python log file $logfn: $!"), last; my $logtext = do { local $/; <$logfh> }; close $logfh; diag "Python error log $logfn:\n$logtext"; last; } } # Basic checks if($mustSucceed) { eval line_mark_string 2, <<'EOT'; cmp_ok($exit_status, '==', 0, "$desc: exit status 0"); cmp_ok(@errlines, '==', 0, "$desc: stderr empty"); EOT die $@ if $@; } # Check regexes my %query_py_output; # filled in only if we see a def/ref/doc for my $entry (@$lrConditions) { my ($re, $negated, $source) = _parse_condition($entry); # Parse query.py output if we need it and haven't done so %query_py_output = _parseq(@outlines) if $source ne 'output' && !%query_py_output; # Build a line of test code to run my $test = 'ok( '; $test .= '!' if $negated; $test .= '(grep { m{$re} } '; if($source eq 'output') { $test .= '@outlines'; } else { $test .= '@{$query_py_output{' . $source . '}}'; } $test .= '), "$desc: ' . $source; $test .= ($negated ? ' excludes ' : ' includes ') . "\Q$re\E" . '");'; # Run it eval line_mark_string 2, $test; die $@ if $@; } #foreach $entry } #run_produces_ok() =head2 http_request_ok Run C against a given path and check whether it produces expected output. Usage: http_request_ok($desc, $tenv, $path, \@conditions, $printOutput) The test passes if the HTTP request succeeds, and if each condition in C<@conditions> is true of the result (headers and body). C<$tenv> is a L. C<$path> is the path part of the URL, e.g., C. See L for C<@conditions>. If C<$printOutput> is true, prints the output of C<@program_and_args>. =cut sub http_request_ok { my ($desc, $tenv, $path, $lrConditions, $printOutput) = @_; die "Invalid args" unless $desc && ref $tenv && eval { @$lrConditions }; my ($exit_status, $lrStdout, $lrStderr) = $tenv->make_web_request($path); _check_queries($desc, $lrConditions, MUST_SUCCEED, $printOutput, $exit_status, $lrStdout, $lrStderr); } #http_request_ok() =head1 INTERNAL FUNCTIONS These are ones you probably won't need to call. =head2 _parseq Parse the output of query.py. Usage: %parsed = _parseq(@lines_of_output); =cut sub _parseq { my %retval = ( def => [], ref => [], doc => [] ); my $list; foreach(@_) { chomp; if(/(?:^Symbol Definitions:$)|\bDefined in \d+/) { $list = 'def'; next; } elsif(/(?:^Symbol References:$)|\bReferenced in \d+/) { $list = 'ref'; next; } elsif(/(?:^Documented in:$)|\bDocumented in \d+/) { $list = 'doc'; next; } next unless $list; #diag "Adding `$_' to list $list"; push @{$retval{$list}}, $_; } return %retval; } #_parseq() =head2 _parse_condition Parse a condition for L. Usage: ($regex, $negated, $source) = _parse_condition($entry[, $source]); =cut sub _parse_condition { my ($entry, $source_in) = @_; my ($regex, $negated, $source); # Return values # Basic cases if(ref $entry eq 'Regexp') { return ($entry, 0, $source_in || 'output'); } elsif(ref $entry eq 'HASH' && ref $entry->{not} eq 'Regexp') { return ($entry->{not}, 1, $source_in || 'output'); } # Sub-keys: chain if(ref $entry eq 'HASH' && scalar keys %{$entry} == 1) { return _parse_condition((values %{$entry})[0], (keys %{$entry})[0]); } # If we get here, we don't know how to handle it die "Invalid entry $entry"; } #_parse_condition() =head2 _croak Lazy invoker for L. =cut sub _croak { require Carp; goto &Carp::croak; } #_croak() =head2 line_mark_string Add a C<#line> directive to a string. Usage: To use the caller's filename and line number: my $str = line_mark_string < will be used for the filename and line number. In the first and third forms, the C<#line> directive will point to the line after the C invocation, i.e., the first line of . Generally, C<$contents> will be source code, although this is not required. C<$contents> must be defined, but can be empty. =cut sub line_mark_string { my ($contents, $filename, $line); if(@_ == 1) { $contents = $_[0]; (undef, $filename, $line) = caller; ++$line; } elsif(@_ == 2) { (undef, $filename, $line) = caller($_[0]); $contents = $_[1]; } elsif(@_ == 3) { ($filename, $line, $contents) = @_; ++$line; } else { _croak "Invalid invocation"; } _croak "Need text" unless defined $contents; die "Couldn't get location information" unless $filename && $line; $filename =~ s/"/-/g; return < and L in the caller. =cut sub import { __PACKAGE__->export_to_level(1, @_); strict->import; warnings->import; } #import() 1; __END__ =head1 AUTHOR Christopher White, C<< >> =head1 COPYRIGHT Copyright (c) 2020 D3 Engineering, LLC. Elixir is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Elixir 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Elixir. If not, see . =cut ================================================ FILE: t/api_test.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2020 Carmeli Tamir and contributors # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . import os import sys import falcon from falcon import testing api_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..','api')) sys.path.insert(0, api_dir) from api import create_ident_getter class APITest(testing.TestCase): def setUp(self): super(APITest, self).setUp() self.app = create_ident_getter() def test_identifier_not_found(self): result = self.simulate_get('/ident/testproj/SOME_NONEXISTENT_IDENTIFIER', query_string="version=latest&family=C") self.assertEqual(result.status_code, 200) self.assertEqual(result.json, {'definitions': [], 'references':[], 'documentations': []}) def test_missing_version(self): # A get request without a version query string result = self.simulate_get('/ident/testproj/of_i2c_get_board_info', query_string="") self.assertEqual(result.status_code, 400) required_response = falcon.HTTPMissingParam('version') self.assertEqual(result.json["title"], required_response.title) self.assertEqual(result.json["description"], required_response.description) def test_existing_identifier(self): result_for_specific_version = self.simulate_get('/ident/testproj/of_i2c_get_board_info', query_string="version=v5.4&family=C") result_for_latest_version = self.simulate_get('/ident/testproj/of_i2c_get_board_info', query_string="version=latest&family=C") expected_json = { 'definitions': [ {'path': 'include/linux/i2c.h', 'line': 941, 'type': 'prototype'}, {'path': 'drivers/i2c/i2c-core-of.c', 'line': 22, 'type': 'function'}, {'path': 'include/linux/i2c.h', 'line': 968, 'type': 'function'} ], 'references': [ {'path': 'drivers/i2c/i2c-core-of.c', 'line': '62,73', 'type': None} ], 'documentations': [] } self.assertEqual(result_for_specific_version.status_code, 200) self.assertEqual(result_for_latest_version.status_code, 200) self.assertEqual(result_for_specific_version.json, expected_json) self.assertEqual(result_for_latest_version.json, expected_json) ================================================ FILE: t/interact.pl ================================================ #!/usr/bin/env perl # t/interact.pl: Open a shell on a DB of the files in tree/ . # # Copyright (c) 2020 Christopher White, . # # Elixir is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # # SPDX-License-Identifier: AGPL-3.0-or-later # # This file uses core Perl modules only. use FindBin '$Bin'; use lib $Bin; use Cwd; use TestEnvironment; use TestHelpers; # =========================================================================== # Main my $pwd = getcwd; # This block is all that's required to set up for a test. my $tenv = TestEnvironment->new; $tenv->build_repo(sibling_abs_path('tree')); # dies on error eval { $tenv->build_db; }; warn "Could not update database: $@" if $@; $tenv->update_env; print($tenv->report); # Make some convenient symlinks chdir($tenv->lxr_repo_dir); system(qw(ln -s), $tenv->script_sh, 'script.sh') == 0 or warn "error creating ./script.sh: $? $!"; system(qw(ln -s), $tenv->update_py, 'update.py') == 0 or warn "error creating ./update.py: $? $!"; system(qw(ln -s), $tenv->query_py, 'query.py') == 0 or warn "error creating ./query.py: $? $!"; system(qw(ln -s), $tenv->web_py, 'web.py') == 0 or warn "error creating ./web.py: $? $!"; system(qw(ln -s), $tenv->find_doc, 'find-file-doc-comments.pl') == 0 or warn "error creating ./find-file-doc-comments.pl: $? $!"; print("Exit when done, and the repository and database will be removed.\n"); my $retval = system($ENV{SHELL} || 'sh'); # Don't stay in the temp dir --- the dir can't be removed if we are there. chdir $pwd; exit $retval>>8; ================================================ FILE: t/tree/arch/arm/xen/hypercall.S ================================================ /****************************************************************************** * hypercall.S * * Xen hypercall wrappers * * Stefano Stabellini , Citrix, 2012 * * 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; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* * The Xen hypercall calling convention is very similar to the ARM * procedure calling convention: the first paramter is passed in r0, the * second in r1, the third in r2 and the fourth in r3. Considering that * Xen hypercalls have 5 arguments at most, the fifth paramter is passed * in r4, differently from the procedure calling convention of using the * stack for that case. * * The hypercall number is passed in r12. * * The return value is in r0. * * The hvc ISS is required to be 0xEA1, that is the Xen specific ARM * hypercall tag. */ #include #include #include #include #define XEN_IMM 0xEA1 #define HYPERCALL_SIMPLE(hypercall) \ ENTRY(HYPERVISOR_##hypercall) \ mov r12, #__HYPERVISOR_##hypercall; \ __HVC(XEN_IMM); \ ret lr; \ ENDPROC(HYPERVISOR_##hypercall) #define HYPERCALL0 HYPERCALL_SIMPLE #define HYPERCALL1 HYPERCALL_SIMPLE #define HYPERCALL2 HYPERCALL_SIMPLE #define HYPERCALL3 HYPERCALL_SIMPLE #define HYPERCALL4 HYPERCALL_SIMPLE #define HYPERCALL5(hypercall) \ ENTRY(HYPERVISOR_##hypercall) \ stmdb sp!, {r4} \ ldr r4, [sp, #4] \ mov r12, #__HYPERVISOR_##hypercall; \ __HVC(XEN_IMM); \ ldm sp!, {r4} \ ret lr \ ENDPROC(HYPERVISOR_##hypercall) .text HYPERCALL2(xen_version); HYPERCALL3(console_io); HYPERCALL3(grant_table_op); HYPERCALL2(sched_op); HYPERCALL2(event_channel_op); HYPERCALL2(hvm_op); HYPERCALL2(memory_op); HYPERCALL2(physdev_op); HYPERCALL3(vcpu_op); HYPERCALL1(tmem_op); HYPERCALL1(platform_op_raw); HYPERCALL2(multicall); HYPERCALL2(vm_assist); HYPERCALL3(dm_op); ENTRY(privcmd_call) stmdb sp!, {r4} mov r12, r0 mov r0, r1 mov r1, r2 mov r2, r3 ldr r3, [sp, #8] /* * Privcmd calls are issued by the userspace. We need to allow the * kernel to access the userspace memory before issuing the hypercall. */ uaccess_enable r4 /* r4 is loaded now as we use it as scratch register before */ ldr r4, [sp, #4] __HVC(XEN_IMM) /* * Disable userspace access from kernel. This is fine to do it * unconditionally as no set_fs(KERNEL_DS) is called before. */ uaccess_disable r4 ldm sp!, {r4} ret lr ENDPROC(privcmd_call); ================================================ FILE: t/tree/arch/x86/include/asm/acpi.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _ASM_X86_ACPI_H #define _ASM_X86_ACPI_H /* * Copyright (C) 2001 Paul Diefenbaugh * Copyright (C) 2001 Patrick Mochel */ #include #include #include #include #include #include #include #include #ifdef CONFIG_ACPI_APEI # include #endif #ifdef CONFIG_ACPI extern int acpi_lapic; extern int acpi_ioapic; extern int acpi_noirq; extern int acpi_strict; extern int acpi_disabled; extern int acpi_pci_disabled; extern int acpi_skip_timer_override; extern int acpi_use_timer_override; extern int acpi_fix_pin2_polarity; extern int acpi_disable_cmcff; extern u8 acpi_sci_flags; extern u32 acpi_sci_override_gsi; void acpi_pic_sci_set_trigger(unsigned int, u16); struct device; extern int (*__acpi_register_gsi)(struct device *dev, u32 gsi, int trigger, int polarity); extern void (*__acpi_unregister_gsi)(u32 gsi); static inline void disable_acpi(void) { acpi_disabled = 1; acpi_pci_disabled = 1; acpi_noirq = 1; } extern int acpi_gsi_to_irq(u32 gsi, unsigned int *irq); static inline void acpi_noirq_set(void) { acpi_noirq = 1; } static inline void acpi_disable_pci(void) { acpi_pci_disabled = 1; acpi_noirq_set(); } /* Low-level suspend routine. */ extern int (*acpi_suspend_lowlevel)(void); /* Physical address to resume after wakeup */ #define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start)) /* * Check if the CPU can handle C2 and deeper */ static inline unsigned int acpi_processor_cstate_check(unsigned int max_cstate) { /* * Early models (<=5) of AMD Opterons are not supposed to go into * C2 state. * * Steppings 0x0A and later are good */ if (boot_cpu_data.x86 == 0x0F && boot_cpu_data.x86_vendor == X86_VENDOR_AMD && boot_cpu_data.x86_model <= 0x05 && boot_cpu_data.x86_stepping < 0x0A) return 1; else if (boot_cpu_has(X86_BUG_AMD_APIC_C1E)) return 1; else return max_cstate; } static inline bool arch_has_acpi_pdc(void) { struct cpuinfo_x86 *c = &cpu_data(0); return (c->x86_vendor == X86_VENDOR_INTEL || c->x86_vendor == X86_VENDOR_CENTAUR); } static inline void arch_acpi_set_pdc_bits(u32 *buf) { struct cpuinfo_x86 *c = &cpu_data(0); buf[2] |= ACPI_PDC_C_CAPABILITY_SMP; if (cpu_has(c, X86_FEATURE_EST)) buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP; if (cpu_has(c, X86_FEATURE_ACPI)) buf[2] |= ACPI_PDC_T_FFH; /* * If mwait/monitor is unsupported, C2/C3_FFH will be disabled */ if (!cpu_has(c, X86_FEATURE_MWAIT)) buf[2] &= ~(ACPI_PDC_C_C2C3_FFH); } static inline bool acpi_has_cpu_in_madt(void) { return !!acpi_lapic; } #define ACPI_HAVE_ARCH_SET_ROOT_POINTER static inline void acpi_arch_set_root_pointer(u64 addr) { x86_init.acpi.set_root_pointer(addr); } #define ACPI_HAVE_ARCH_GET_ROOT_POINTER static inline u64 acpi_arch_get_root_pointer(void) { return x86_init.acpi.get_root_pointer(); } void acpi_generic_reduced_hw_init(void); void x86_default_set_root_pointer(u64 addr); u64 x86_default_get_root_pointer(void); #else /* !CONFIG_ACPI */ #define acpi_lapic 0 #define acpi_ioapic 0 #define acpi_disable_cmcff 0 static inline void acpi_noirq_set(void) { } static inline void acpi_disable_pci(void) { } static inline void disable_acpi(void) { } static inline void acpi_generic_reduced_hw_init(void) { } static inline void x86_default_set_root_pointer(u64 addr) { } static inline u64 x86_default_get_root_pointer(void) { return 0; } #endif /* !CONFIG_ACPI */ #define ARCH_HAS_POWER_INIT 1 #ifdef CONFIG_ACPI_NUMA extern int x86_acpi_numa_init(void); #endif /* CONFIG_ACPI_NUMA */ #define acpi_unlazy_tlb(x) leave_mm(x) #ifdef CONFIG_ACPI_APEI static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr) { /* * We currently have no way to look up the EFI memory map * attributes for a region in a consistent way, because the * memmap is discarded after efi_free_boot_services(). So if * you call efi_mem_attributes() during boot and at runtime, * you could theoretically see different attributes. * * We are yet to see any x86 platforms that require anything * other than PAGE_KERNEL (some ARM64 platforms require the * equivalent of PAGE_KERNEL_NOCACHE). Additionally, if SME * is active, the ACPI information will not be encrypted, * so return PAGE_KERNEL_NOENC until we know differently. */ return PAGE_KERNEL_NOENC; } #endif #define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn_mapped << PAGE_SHIFT) #endif /* _ASM_X86_ACPI_H */ ================================================ FILE: t/tree/arch/x86/include/asm/ist.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Include file for the interface to IST BIOS * Copyright 2002 Andy Grover */ #ifndef _ASM_X86_IST_H #define _ASM_X86_IST_H #include extern struct ist_info ist_info; #endif /* _ASM_X86_IST_H */ ================================================ FILE: t/tree/arch/x86/include/asm/orc_types.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2017 Josh Poimboeuf */ #ifndef _ORC_TYPES_H #define _ORC_TYPES_H #include #include /* * The ORC_REG_* registers are base registers which are used to find other * registers on the stack. * * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the * address of the previous frame: the caller's SP before it called the current * function. * * ORC_REG_UNDEFINED means the corresponding register's value didn't change in * the current frame. * * The most commonly used base registers are SP and BP -- which the previous SP * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is * usually based on. * * The rest of the base registers are needed for special cases like entry code * and GCC realigned stacks. */ #define ORC_REG_UNDEFINED 0 #define ORC_REG_PREV_SP 1 #define ORC_REG_DX 2 #define ORC_REG_DI 3 #define ORC_REG_BP 4 #define ORC_REG_SP 5 #define ORC_REG_R10 6 #define ORC_REG_R13 7 #define ORC_REG_BP_INDIRECT 8 #define ORC_REG_SP_INDIRECT 9 #define ORC_REG_MAX 15 /* * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the * caller's SP right before it made the call). Used for all callable * functions, i.e. all C code and all callable asm functions. * * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points * to a fully populated pt_regs from a syscall, interrupt, or exception. * * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset * points to the iret return frame. * * The UNWIND_HINT macros are used only for the unwind_hint struct. They * aren't used in struct orc_entry due to size and complexity constraints. * Objtool converts them to real types when it converts the hints to orc * entries. */ #define ORC_TYPE_CALL 0 #define ORC_TYPE_REGS 1 #define ORC_TYPE_REGS_IRET 2 #define UNWIND_HINT_TYPE_SAVE 3 #define UNWIND_HINT_TYPE_RESTORE 4 #ifndef __ASSEMBLY__ /* * This struct is more or less a vastly simplified version of the DWARF Call * Frame Information standard. It contains only the necessary parts of DWARF * CFI, simplified for ease of access by the in-kernel unwinder. It tells the * unwinder how to find the previous SP and BP (and sometimes entry regs) on * the stack for a given code address. Each instance of the struct corresponds * to one or more code locations. */ struct orc_entry { s16 sp_offset; s16 bp_offset; unsigned sp_reg:4; unsigned bp_reg:4; unsigned type:2; unsigned end:1; } __packed; /* * This struct is used by asm and inline asm code to manually annotate the * location of registers on the stack for the ORC unwinder. * * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*. */ struct unwind_hint { u32 ip; s16 sp_offset; u8 sp_reg; u8 type; u8 end; }; #endif /* __ASSEMBLY__ */ #endif /* _ORC_TYPES_H */ ================================================ FILE: t/tree/arch/x86/include/asm/uprobes.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _ASM_UPROBES_H #define _ASM_UPROBES_H /* * User-space Probes (UProbes) for x86 * * Copyright (C) IBM Corporation, 2008-2011 * Authors: * Srikar Dronamraju * Jim Keniston */ #include typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 #define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ #define UPROBE_SWBP_INSN 0xcc #define UPROBE_SWBP_INSN_SIZE 1 struct uprobe_xol_ops; struct arch_uprobe { union { u8 insn[MAX_UINSN_BYTES]; u8 ixol[MAX_UINSN_BYTES]; }; const struct uprobe_xol_ops *ops; union { struct { s32 offs; u8 ilen; u8 opc1; } branch; struct { u8 fixups; u8 ilen; } defparam; struct { u8 reg_offset; /* to the start of pt_regs */ u8 ilen; } push; }; }; struct arch_uprobe_task { #ifdef CONFIG_X86_64 unsigned long saved_scratch_register; #endif unsigned int saved_trap_nr; unsigned int saved_tf; }; #endif /* _ASM_UPROBES_H */ ================================================ FILE: t/tree/arch/x86/include/uapi/asm/ist.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * Include file for the interface to IST BIOS * Copyright 2002 Andy Grover * * 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, 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. */ #ifndef _UAPI_ASM_X86_IST_H #define _UAPI_ASM_X86_IST_H #include struct ist_info { __u32 signature; __u32 command; __u32 event; __u32 perf_level; }; #endif /* _UAPI_ASM_X86_IST_H */ ================================================ FILE: t/tree/drivers/firmware/broadcom/bcm74xx_sprom.c ================================================ /* * Copyright (C) 2004 Florian Schirmer * Copyright (C) 2006 Felix Fietkau * Copyright (C) 2006 Michael Buesch * Copyright (C) 2010 Waldemar Brodkorb * Copyright (C) 2010-2012 Hauke Mehrtens * * 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 SOFTWARE IS PROVIDED ``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. * * 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., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include static void create_key(const char *prefix, const char *postfix, const char *name, char *buf, int len) { if (prefix && postfix) snprintf(buf, len, "%s%s%s", prefix, name, postfix); else if (prefix) snprintf(buf, len, "%s%s", prefix, name); else if (postfix) snprintf(buf, len, "%s%s", name, postfix); else snprintf(buf, len, "%s", name); } static int get_nvram_var(const char *prefix, const char *postfix, const char *name, char *buf, int len, bool fallback) { char key[40]; int err; create_key(prefix, postfix, name, key, sizeof(key)); err = bcm47xx_nvram_getenv(key, buf, len); if (fallback && err == -ENOENT && prefix) { create_key(NULL, postfix, name, key, sizeof(key)); err = bcm47xx_nvram_getenv(key, buf, len); } return err; } #define NVRAM_READ_VAL(type) \ static void nvram_read_ ## type(const char *prefix, \ const char *postfix, const char *name, \ type *val, type allset, bool fallback) \ { \ char buf[100]; \ int err; \ type var; \ \ err = get_nvram_var(prefix, postfix, name, buf, sizeof(buf), \ fallback); \ if (err < 0) \ return; \ err = kstrto ## type(strim(buf), 0, &var); \ if (err) { \ pr_warn("can not parse nvram name %s%s%s with value %s got %i\n", \ prefix, name, postfix, buf, err); \ return; \ } \ if (allset && var == allset) \ return; \ *val = var; \ } NVRAM_READ_VAL(u8) NVRAM_READ_VAL(s8) NVRAM_READ_VAL(u16) NVRAM_READ_VAL(u32) #undef NVRAM_READ_VAL static void nvram_read_u32_2(const char *prefix, const char *name, u16 *val_lo, u16 *val_hi, bool fallback) { char buf[100]; int err; u32 val; err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); if (err < 0) return; err = kstrtou32(strim(buf), 0, &val); if (err) { pr_warn("can not parse nvram name %s%s with value %s got %i\n", prefix, name, buf, err); return; } *val_lo = (val & 0x0000FFFFU); *val_hi = (val & 0xFFFF0000U) >> 16; } static void nvram_read_leddc(const char *prefix, const char *name, u8 *leddc_on_time, u8 *leddc_off_time, bool fallback) { char buf[100]; int err; u32 val; err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); if (err < 0) return; err = kstrtou32(strim(buf), 0, &val); if (err) { pr_warn("can not parse nvram name %s%s with value %s got %i\n", prefix, name, buf, err); return; } if (val == 0xffff || val == 0xffffffff) return; *leddc_on_time = val & 0xff; *leddc_off_time = (val >> 16) & 0xff; } static void nvram_read_macaddr(const char *prefix, const char *name, u8 val[6], bool fallback) { char buf[100]; int err; err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); if (err < 0) return; strreplace(buf, '-', ':'); if (!mac_pton(buf, val)) pr_warn("Can not parse mac address: %s\n", buf); } static void nvram_read_alpha2(const char *prefix, const char *name, char val[2], bool fallback) { char buf[10]; int err; err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); if (err < 0) return; if (buf[0] == '0') return; if (strlen(buf) > 2) { pr_warn("alpha2 is too long %s\n", buf); return; } memcpy(val, buf, 2); } /* This is one-function-only macro, it uses local "sprom" variable! */ #define ENTRY(_revmask, _type, _prefix, _name, _val, _allset, _fallback) \ if (_revmask & BIT(sprom->revision)) \ nvram_read_ ## _type(_prefix, NULL, _name, &sprom->_val, \ _allset, _fallback) /* * Special version of filling function that can be safely called for any SPROM * revision. For every NVRAM to SPROM mapping it contains bitmask of revisions * for which the mapping is valid. * It obviously requires some hexadecimal/bitmasks knowledge, but allows * writing cleaner code (easy revisions handling). * Note that while SPROM revision 0 was never used, we still keep BIT(0) * reserved for it, just to keep numbering sane. */ static void bcm47xx_sprom_fill_auto(struct ssb_sprom *sprom, const char *prefix, bool fallback) { const char *pre = prefix; bool fb = fallback; /* Broadcom extracts it for rev 8+ but it was found on 2 and 4 too */ ENTRY(0xfffffffe, u16, pre, "devid", dev_id, 0, fallback); ENTRY(0xfffffffe, u16, pre, "boardrev", board_rev, 0, true); ENTRY(0xfffffffe, u32, pre, "boardflags", boardflags, 0, fb); ENTRY(0xfffffff0, u32, pre, "boardflags2", boardflags2, 0, fb); ENTRY(0xfffff800, u32, pre, "boardflags3", boardflags3, 0, fb); ENTRY(0x00000002, u16, pre, "boardflags", boardflags_lo, 0, fb); ENTRY(0xfffffffc, u16, pre, "boardtype", board_type, 0, true); ENTRY(0xfffffffe, u16, pre, "boardnum", board_num, 0, fb); ENTRY(0x00000002, u8, pre, "cc", country_code, 0, fb); ENTRY(0xfffffff8, u8, pre, "regrev", regrev, 0, fb); ENTRY(0xfffffffe, u8, pre, "ledbh0", gpio0, 0xff, fb); ENTRY(0xfffffffe, u8, pre, "ledbh1", gpio1, 0xff, fb); ENTRY(0xfffffffe, u8, pre, "ledbh2", gpio2, 0xff, fb); ENTRY(0xfffffffe, u8, pre, "ledbh3", gpio3, 0xff, fb); ENTRY(0x0000070e, u16, pre, "pa0b0", pa0b0, 0, fb); ENTRY(0x0000070e, u16, pre, "pa0b1", pa0b1, 0, fb); ENTRY(0x0000070e, u16, pre, "pa0b2", pa0b2, 0, fb); ENTRY(0x0000070e, u8, pre, "pa0itssit", itssi_bg, 0, fb); ENTRY(0x0000070e, u8, pre, "pa0maxpwr", maxpwr_bg, 0, fb); ENTRY(0x0000070c, u8, pre, "opo", opo, 0, fb); ENTRY(0xfffffffe, u8, pre, "aa2g", ant_available_bg, 0, fb); ENTRY(0xfffffffe, u8, pre, "aa5g", ant_available_a, 0, fb); ENTRY(0x000007fe, s8, pre, "ag0", antenna_gain.a0, 0, fb); ENTRY(0x000007fe, s8, pre, "ag1", antenna_gain.a1, 0, fb); ENTRY(0x000007f0, s8, pre, "ag2", antenna_gain.a2, 0, fb); ENTRY(0x000007f0, s8, pre, "ag3", antenna_gain.a3, 0, fb); ENTRY(0x0000070e, u16, pre, "pa1b0", pa1b0, 0, fb); ENTRY(0x0000070e, u16, pre, "pa1b1", pa1b1, 0, fb); ENTRY(0x0000070e, u16, pre, "pa1b2", pa1b2, 0, fb); ENTRY(0x0000070c, u16, pre, "pa1lob0", pa1lob0, 0, fb); ENTRY(0x0000070c, u16, pre, "pa1lob1", pa1lob1, 0, fb); ENTRY(0x0000070c, u16, pre, "pa1lob2", pa1lob2, 0, fb); ENTRY(0x0000070c, u16, pre, "pa1hib0", pa1hib0, 0, fb); ENTRY(0x0000070c, u16, pre, "pa1hib1", pa1hib1, 0, fb); ENTRY(0x0000070c, u16, pre, "pa1hib2", pa1hib2, 0, fb); ENTRY(0x0000070e, u8, pre, "pa1itssit", itssi_a, 0, fb); ENTRY(0x0000070e, u8, pre, "pa1maxpwr", maxpwr_a, 0, fb); ENTRY(0x0000070c, u8, pre, "pa1lomaxpwr", maxpwr_al, 0, fb); ENTRY(0x0000070c, u8, pre, "pa1himaxpwr", maxpwr_ah, 0, fb); ENTRY(0x00000708, u8, pre, "bxa2g", bxa2g, 0, fb); ENTRY(0x00000708, u8, pre, "rssisav2g", rssisav2g, 0, fb); ENTRY(0x00000708, u8, pre, "rssismc2g", rssismc2g, 0, fb); ENTRY(0x00000708, u8, pre, "rssismf2g", rssismf2g, 0, fb); ENTRY(0x00000708, u8, pre, "bxa5g", bxa5g, 0, fb); ENTRY(0x00000708, u8, pre, "rssisav5g", rssisav5g, 0, fb); ENTRY(0x00000708, u8, pre, "rssismc5g", rssismc5g, 0, fb); ENTRY(0x00000708, u8, pre, "rssismf5g", rssismf5g, 0, fb); ENTRY(0x00000708, u8, pre, "tri2g", tri2g, 0, fb); ENTRY(0x00000708, u8, pre, "tri5g", tri5g, 0, fb); ENTRY(0x00000708, u8, pre, "tri5gl", tri5gl, 0, fb); ENTRY(0x00000708, u8, pre, "tri5gh", tri5gh, 0, fb); ENTRY(0x00000708, s8, pre, "rxpo2g", rxpo2g, 0, fb); ENTRY(0x00000708, s8, pre, "rxpo5g", rxpo5g, 0, fb); ENTRY(0xfffffff0, u8, pre, "txchain", txchain, 0xf, fb); ENTRY(0xfffffff0, u8, pre, "rxchain", rxchain, 0xf, fb); ENTRY(0xfffffff0, u8, pre, "antswitch", antswitch, 0xff, fb); ENTRY(0x00000700, u8, pre, "tssipos2g", fem.ghz2.tssipos, 0, fb); ENTRY(0x00000700, u8, pre, "extpagain2g", fem.ghz2.extpa_gain, 0, fb); ENTRY(0x00000700, u8, pre, "pdetrange2g", fem.ghz2.pdet_range, 0, fb); ENTRY(0x00000700, u8, pre, "triso2g", fem.ghz2.tr_iso, 0, fb); ENTRY(0x00000700, u8, pre, "antswctl2g", fem.ghz2.antswlut, 0, fb); ENTRY(0x00000700, u8, pre, "tssipos5g", fem.ghz5.tssipos, 0, fb); ENTRY(0x00000700, u8, pre, "extpagain5g", fem.ghz5.extpa_gain, 0, fb); ENTRY(0x00000700, u8, pre, "pdetrange5g", fem.ghz5.pdet_range, 0, fb); ENTRY(0x00000700, u8, pre, "triso5g", fem.ghz5.tr_iso, 0, fb); ENTRY(0x00000700, u8, pre, "antswctl5g", fem.ghz5.antswlut, 0, fb); ENTRY(0x000000f0, u8, pre, "txpid2ga0", txpid2g[0], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid2ga1", txpid2g[1], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid2ga2", txpid2g[2], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid2ga3", txpid2g[3], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5ga0", txpid5g[0], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5ga1", txpid5g[1], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5ga2", txpid5g[2], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5ga3", txpid5g[3], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gla0", txpid5gl[0], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gla1", txpid5gl[1], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gla2", txpid5gl[2], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gla3", txpid5gl[3], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gha0", txpid5gh[0], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gha1", txpid5gh[1], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gha2", txpid5gh[2], 0, fb); ENTRY(0x000000f0, u8, pre, "txpid5gha3", txpid5gh[3], 0, fb); ENTRY(0xffffff00, u8, pre, "tempthresh", tempthresh, 0, fb); ENTRY(0xffffff00, u8, pre, "tempoffset", tempoffset, 0, fb); ENTRY(0xffffff00, u16, pre, "rawtempsense", rawtempsense, 0, fb); ENTRY(0xffffff00, u8, pre, "measpower", measpower, 0, fb); ENTRY(0xffffff00, u8, pre, "tempsense_slope", tempsense_slope, 0, fb); ENTRY(0xffffff00, u8, pre, "tempcorrx", tempcorrx, 0, fb); ENTRY(0xffffff00, u8, pre, "tempsense_option", tempsense_option, 0, fb); ENTRY(0x00000700, u8, pre, "freqoffset_corr", freqoffset_corr, 0, fb); ENTRY(0x00000700, u8, pre, "iqcal_swp_dis", iqcal_swp_dis, 0, fb); ENTRY(0x00000700, u8, pre, "hw_iqcal_en", hw_iqcal_en, 0, fb); ENTRY(0x00000700, u8, pre, "elna2g", elna2g, 0, fb); ENTRY(0x00000700, u8, pre, "elna5g", elna5g, 0, fb); ENTRY(0xffffff00, u8, pre, "phycal_tempdelta", phycal_tempdelta, 0, fb); ENTRY(0xffffff00, u8, pre, "temps_period", temps_period, 0, fb); ENTRY(0xffffff00, u8, pre, "temps_hysteresis", temps_hysteresis, 0, fb); ENTRY(0xffffff00, u8, pre, "measpower1", measpower1, 0, fb); ENTRY(0xffffff00, u8, pre, "measpower2", measpower2, 0, fb); ENTRY(0x000001f0, u16, pre, "cck2gpo", cck2gpo, 0, fb); ENTRY(0x000001f0, u32, pre, "ofdm2gpo", ofdm2gpo, 0, fb); ENTRY(0x000001f0, u32, pre, "ofdm5gpo", ofdm5gpo, 0, fb); ENTRY(0x000001f0, u32, pre, "ofdm5glpo", ofdm5glpo, 0, fb); ENTRY(0x000001f0, u32, pre, "ofdm5ghpo", ofdm5ghpo, 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo0", mcs2gpo[0], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo1", mcs2gpo[1], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo2", mcs2gpo[2], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo3", mcs2gpo[3], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo4", mcs2gpo[4], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo5", mcs2gpo[5], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo6", mcs2gpo[6], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs2gpo7", mcs2gpo[7], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo0", mcs5gpo[0], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo1", mcs5gpo[1], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo2", mcs5gpo[2], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo3", mcs5gpo[3], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo4", mcs5gpo[4], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo5", mcs5gpo[5], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo6", mcs5gpo[6], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5gpo7", mcs5gpo[7], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo0", mcs5glpo[0], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo1", mcs5glpo[1], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo2", mcs5glpo[2], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo3", mcs5glpo[3], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo4", mcs5glpo[4], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo5", mcs5glpo[5], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo6", mcs5glpo[6], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5glpo7", mcs5glpo[7], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo0", mcs5ghpo[0], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo1", mcs5ghpo[1], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo2", mcs5ghpo[2], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo3", mcs5ghpo[3], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo4", mcs5ghpo[4], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo5", mcs5ghpo[5], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo6", mcs5ghpo[6], 0, fb); ENTRY(0x000001f0, u16, pre, "mcs5ghpo7", mcs5ghpo[7], 0, fb); ENTRY(0x000001f0, u16, pre, "cddpo", cddpo, 0, fb); ENTRY(0x000001f0, u16, pre, "stbcpo", stbcpo, 0, fb); ENTRY(0x000001f0, u16, pre, "bw40po", bw40po, 0, fb); ENTRY(0x000001f0, u16, pre, "bwduppo", bwduppo, 0, fb); ENTRY(0xfffffe00, u16, pre, "cckbw202gpo", cckbw202gpo, 0, fb); ENTRY(0xfffffe00, u16, pre, "cckbw20ul2gpo", cckbw20ul2gpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw202gpo", legofdmbw202gpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw20ul2gpo", legofdmbw20ul2gpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw205glpo", legofdmbw205glpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw20ul5glpo", legofdmbw20ul5glpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw205gmpo", legofdmbw205gmpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw20ul5gmpo", legofdmbw20ul5gmpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw205ghpo", legofdmbw205ghpo, 0, fb); ENTRY(0x00000600, u32, pre, "legofdmbw20ul5ghpo", legofdmbw20ul5ghpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw202gpo", mcsbw202gpo, 0, fb); ENTRY(0x00000600, u32, pre, "mcsbw20ul2gpo", mcsbw20ul2gpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw402gpo", mcsbw402gpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw205glpo", mcsbw205glpo, 0, fb); ENTRY(0x00000600, u32, pre, "mcsbw20ul5glpo", mcsbw20ul5glpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw405glpo", mcsbw405glpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw205gmpo", mcsbw205gmpo, 0, fb); ENTRY(0x00000600, u32, pre, "mcsbw20ul5gmpo", mcsbw20ul5gmpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw405gmpo", mcsbw405gmpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw205ghpo", mcsbw205ghpo, 0, fb); ENTRY(0x00000600, u32, pre, "mcsbw20ul5ghpo", mcsbw20ul5ghpo, 0, fb); ENTRY(0xfffffe00, u32, pre, "mcsbw405ghpo", mcsbw405ghpo, 0, fb); ENTRY(0x00000600, u16, pre, "mcs32po", mcs32po, 0, fb); ENTRY(0x00000600, u16, pre, "legofdm40duppo", legofdm40duppo, 0, fb); ENTRY(0x00000700, u8, pre, "pcieingress_war", pcieingress_war, 0, fb); /* TODO: rev 11 support */ ENTRY(0x00000700, u8, pre, "rxgainerr2ga0", rxgainerr2ga[0], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr2ga1", rxgainerr2ga[1], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr2ga2", rxgainerr2ga[2], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gla0", rxgainerr5gla[0], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gla1", rxgainerr5gla[1], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gla2", rxgainerr5gla[2], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gma0", rxgainerr5gma[0], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gma1", rxgainerr5gma[1], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gma2", rxgainerr5gma[2], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gha0", rxgainerr5gha[0], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gha1", rxgainerr5gha[1], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gha2", rxgainerr5gha[2], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gua0", rxgainerr5gua[0], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gua1", rxgainerr5gua[1], 0, fb); ENTRY(0x00000700, u8, pre, "rxgainerr5gua2", rxgainerr5gua[2], 0, fb); ENTRY(0xfffffe00, u8, pre, "sar2g", sar2g, 0, fb); ENTRY(0xfffffe00, u8, pre, "sar5g", sar5g, 0, fb); /* TODO: rev 11 support */ ENTRY(0x00000700, u8, pre, "noiselvl2ga0", noiselvl2ga[0], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl2ga1", noiselvl2ga[1], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl2ga2", noiselvl2ga[2], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gla0", noiselvl5gla[0], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gla1", noiselvl5gla[1], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gla2", noiselvl5gla[2], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gma0", noiselvl5gma[0], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gma1", noiselvl5gma[1], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gma2", noiselvl5gma[2], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gha0", noiselvl5gha[0], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gha1", noiselvl5gha[1], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gha2", noiselvl5gha[2], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gua0", noiselvl5gua[0], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gua1", noiselvl5gua[1], 0, fb); ENTRY(0x00000700, u8, pre, "noiselvl5gua2", noiselvl5gua[2], 0, fb); } #undef ENTRY /* It's specififc, uses local variable, don't use it (again). */ static void bcm47xx_fill_sprom_path_r4589(struct ssb_sprom *sprom, const char *prefix, bool fallback) { char postfix[2]; int i; for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) { struct ssb_sprom_core_pwr_info *pwr_info; pwr_info = &sprom->core_pwr_info[i]; snprintf(postfix, sizeof(postfix), "%i", i); nvram_read_u8(prefix, postfix, "maxp2ga", &pwr_info->maxpwr_2g, 0, fallback); nvram_read_u8(prefix, postfix, "itt2ga", &pwr_info->itssi_2g, 0, fallback); nvram_read_u8(prefix, postfix, "itt5ga", &pwr_info->itssi_5g, 0, fallback); nvram_read_u16(prefix, postfix, "pa2gw0a", &pwr_info->pa_2g[0], 0, fallback); nvram_read_u16(prefix, postfix, "pa2gw1a", &pwr_info->pa_2g[1], 0, fallback); nvram_read_u16(prefix, postfix, "pa2gw2a", &pwr_info->pa_2g[2], 0, fallback); nvram_read_u8(prefix, postfix, "maxp5ga", &pwr_info->maxpwr_5g, 0, fallback); nvram_read_u8(prefix, postfix, "maxp5gha", &pwr_info->maxpwr_5gh, 0, fallback); nvram_read_u8(prefix, postfix, "maxp5gla", &pwr_info->maxpwr_5gl, 0, fallback); nvram_read_u16(prefix, postfix, "pa5gw0a", &pwr_info->pa_5g[0], 0, fallback); nvram_read_u16(prefix, postfix, "pa5gw1a", &pwr_info->pa_5g[1], 0, fallback); nvram_read_u16(prefix, postfix, "pa5gw2a", &pwr_info->pa_5g[2], 0, fallback); nvram_read_u16(prefix, postfix, "pa5glw0a", &pwr_info->pa_5gl[0], 0, fallback); nvram_read_u16(prefix, postfix, "pa5glw1a", &pwr_info->pa_5gl[1], 0, fallback); nvram_read_u16(prefix, postfix, "pa5glw2a", &pwr_info->pa_5gl[2], 0, fallback); nvram_read_u16(prefix, postfix, "pa5ghw0a", &pwr_info->pa_5gh[0], 0, fallback); nvram_read_u16(prefix, postfix, "pa5ghw1a", &pwr_info->pa_5gh[1], 0, fallback); nvram_read_u16(prefix, postfix, "pa5ghw2a", &pwr_info->pa_5gh[2], 0, fallback); } } static void bcm47xx_fill_sprom_path_r45(struct ssb_sprom *sprom, const char *prefix, bool fallback) { char postfix[2]; int i; for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) { struct ssb_sprom_core_pwr_info *pwr_info; pwr_info = &sprom->core_pwr_info[i]; snprintf(postfix, sizeof(postfix), "%i", i); nvram_read_u16(prefix, postfix, "pa2gw3a", &pwr_info->pa_2g[3], 0, fallback); nvram_read_u16(prefix, postfix, "pa5gw3a", &pwr_info->pa_5g[3], 0, fallback); nvram_read_u16(prefix, postfix, "pa5glw3a", &pwr_info->pa_5gl[3], 0, fallback); nvram_read_u16(prefix, postfix, "pa5ghw3a", &pwr_info->pa_5gh[3], 0, fallback); } } static bool bcm47xx_is_valid_mac(u8 *mac) { return mac && !(mac[0] == 0x00 && mac[1] == 0x90 && mac[2] == 0x4c); } static int bcm47xx_increase_mac_addr(u8 *mac, u8 num) { u8 *oui = mac + ETH_ALEN/2 - 1; u8 *p = mac + ETH_ALEN - 1; do { (*p) += num; if (*p > num) break; p--; num = 1; } while (p != oui); if (p == oui) { pr_err("unable to fetch mac address\n"); return -ENOENT; } return 0; } static int mac_addr_used = 2; static void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, const char *prefix, bool fallback) { bool fb = fallback; nvram_read_macaddr(prefix, "et0macaddr", sprom->et0mac, fallback); nvram_read_u8(prefix, NULL, "et0mdcport", &sprom->et0mdcport, 0, fallback); nvram_read_u8(prefix, NULL, "et0phyaddr", &sprom->et0phyaddr, 0, fallback); nvram_read_macaddr(prefix, "et1macaddr", sprom->et1mac, fallback); nvram_read_u8(prefix, NULL, "et1mdcport", &sprom->et1mdcport, 0, fallback); nvram_read_u8(prefix, NULL, "et1phyaddr", &sprom->et1phyaddr, 0, fallback); nvram_read_macaddr(prefix, "et2macaddr", sprom->et2mac, fb); nvram_read_u8(prefix, NULL, "et2mdcport", &sprom->et2mdcport, 0, fb); nvram_read_u8(prefix, NULL, "et2phyaddr", &sprom->et2phyaddr, 0, fb); nvram_read_macaddr(prefix, "macaddr", sprom->il0mac, fallback); nvram_read_macaddr(prefix, "il0macaddr", sprom->il0mac, fallback); /* The address prefix 00:90:4C is used by Broadcom in their initial * configuration. When a mac address with the prefix 00:90:4C is used * all devices from the same series are sharing the same mac address. * To prevent mac address collisions we replace them with a mac address * based on the base address. */ if (!bcm47xx_is_valid_mac(sprom->il0mac)) { u8 mac[6]; nvram_read_macaddr(NULL, "et0macaddr", mac, false); if (bcm47xx_is_valid_mac(mac)) { int err = bcm47xx_increase_mac_addr(mac, mac_addr_used); if (!err) { ether_addr_copy(sprom->il0mac, mac); mac_addr_used++; } } } } static void bcm47xx_fill_board_data(struct ssb_sprom *sprom, const char *prefix, bool fallback) { nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, &sprom->boardflags_hi, fallback); nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, &sprom->boardflags2_hi, fallback); } void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix, bool fallback) { bcm47xx_fill_sprom_ethernet(sprom, prefix, fallback); bcm47xx_fill_board_data(sprom, prefix, fallback); nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0, fallback); /* Entries requiring custom functions */ nvram_read_alpha2(prefix, "ccode", sprom->alpha2, fallback); if (sprom->revision >= 3) nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time, &sprom->leddc_off_time, fallback); switch (sprom->revision) { case 4: case 5: bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback); bcm47xx_fill_sprom_path_r45(sprom, prefix, fallback); break; case 8: case 9: bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback); break; } bcm47xx_sprom_fill_auto(sprom, prefix, fallback); } #if IS_BUILTIN(CONFIG_SSB) && IS_ENABLED(CONFIG_SSB_SPROM) static int bcm47xx_get_sprom_ssb(struct ssb_bus *bus, struct ssb_sprom *out) { char prefix[10]; switch (bus->bustype) { case SSB_BUSTYPE_SSB: bcm47xx_fill_sprom(out, NULL, false); return 0; case SSB_BUSTYPE_PCI: memset(out, 0, sizeof(struct ssb_sprom)); snprintf(prefix, sizeof(prefix), "pci/%u/%u/", bus->host_pci->bus->number + 1, PCI_SLOT(bus->host_pci->devfn)); bcm47xx_fill_sprom(out, prefix, false); return 0; default: pr_warn("Unable to fill SPROM for given bustype.\n"); return -EINVAL; } } #endif #if IS_BUILTIN(CONFIG_BCMA) /* * Having many NVRAM entries for PCI devices led to repeating prefixes like * pci/1/1/ all the time and wasting flash space. So at some point Broadcom * decided to introduce prefixes like 0: 1: 2: etc. * If we find e.g. devpath0=pci/2/1 or devpath0=pci/2/1/ we should use 0: * instead of pci/2/1/. */ static void bcm47xx_sprom_apply_prefix_alias(char *prefix, size_t prefix_size) { size_t prefix_len = strlen(prefix); size_t short_len = prefix_len - 1; char nvram_var[10]; char buf[20]; int i; /* Passed prefix has to end with a slash */ if (prefix_len <= 0 || prefix[prefix_len - 1] != '/') return; for (i = 0; i < 3; i++) { if (snprintf(nvram_var, sizeof(nvram_var), "devpath%d", i) <= 0) continue; if (bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf)) < 0) continue; if (!strcmp(buf, prefix) || (short_len && strlen(buf) == short_len && !strncmp(buf, prefix, short_len))) { snprintf(prefix, prefix_size, "%d:", i); return; } } } static int bcm47xx_get_sprom_bcma(struct bcma_bus *bus, struct ssb_sprom *out) { struct bcma_boardinfo *binfo = &bus->boardinfo; struct bcma_device *core; char buf[10]; char *prefix; bool fallback = false; switch (bus->hosttype) { case BCMA_HOSTTYPE_PCI: memset(out, 0, sizeof(struct ssb_sprom)); /* On BCM47XX all PCI buses share the same domain */ if (IS_ENABLED(CONFIG_BCM47XX)) snprintf(buf, sizeof(buf), "pci/%u/%u/", bus->host_pci->bus->number + 1, PCI_SLOT(bus->host_pci->devfn)); else snprintf(buf, sizeof(buf), "pci/%u/%u/", pci_domain_nr(bus->host_pci->bus) + 1, bus->host_pci->bus->number); bcm47xx_sprom_apply_prefix_alias(buf, sizeof(buf)); prefix = buf; break; case BCMA_HOSTTYPE_SOC: memset(out, 0, sizeof(struct ssb_sprom)); core = bcma_find_core(bus, BCMA_CORE_80211); if (core) { snprintf(buf, sizeof(buf), "sb/%u/", core->core_index); prefix = buf; fallback = true; } else { prefix = NULL; } break; default: pr_warn("Unable to fill SPROM for given bustype.\n"); return -EINVAL; } nvram_read_u16(prefix, NULL, "boardvendor", &binfo->vendor, 0, true); if (!binfo->vendor) binfo->vendor = SSB_BOARDVENDOR_BCM; nvram_read_u16(prefix, NULL, "boardtype", &binfo->type, 0, true); bcm47xx_fill_sprom(out, prefix, fallback); return 0; } #endif static unsigned int bcm47xx_sprom_registered; /* * On bcm47xx we need to register SPROM fallback handler very early, so we can't * use anything like platform device / driver for this. */ int bcm47xx_sprom_register_fallbacks(void) { if (bcm47xx_sprom_registered) return 0; #if IS_BUILTIN(CONFIG_SSB) && IS_ENABLED(CONFIG_SSB_SPROM) if (ssb_arch_register_fallback_sprom(&bcm47xx_get_sprom_ssb)) pr_warn("Failed to register ssb SPROM handler\n"); #endif #if IS_BUILTIN(CONFIG_BCMA) if (bcma_arch_register_fallback_sprom(&bcm47xx_get_sprom_bcma)) pr_warn("Failed to register bcma SPROM handler\n"); #endif bcm47xx_sprom_registered = 1; return 0; } fs_initcall(bcm47xx_sprom_register_fallbacks); ================================================ FILE: t/tree/drivers/i2c/i2c-boardinfo.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * i2c-boardinfo.c - collect pre-declarations of I2C devices */ #include #include #include #include #include #include #include "i2c-core.h" /* These symbols are exported ONLY FOR the i2c core. * No other users will be supported. */ DECLARE_RWSEM(__i2c_board_lock); EXPORT_SYMBOL_GPL(__i2c_board_lock); LIST_HEAD(__i2c_board_list); EXPORT_SYMBOL_GPL(__i2c_board_list); int __i2c_first_dynamic_bus_num; EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num); /** * i2c_register_board_info - statically declare I2C devices * @busnum: identifies the bus to which these devices belong * @info: vector of i2c device descriptors * @len: how many descriptors in the vector; may be zero to reserve * the specified bus number. * * Systems using the Linux I2C driver stack can declare tables of board info * while they initialize. This should be done in board-specific init code * near arch_initcall() time, or equivalent, before any I2C adapter driver is * registered. For example, mainboard init code could define several devices, * as could the init code for each daughtercard in a board stack. * * The I2C devices will be created later, after the adapter for the relevant * bus has been registered. After that moment, standard driver model tools * are used to bind "new style" I2C drivers to the devices. The bus number * for any device declared using this routine is not available for dynamic * allocation. * * The board info passed can safely be __initdata, but be careful of embedded * pointers (for platform_data, functions, etc) since that won't be copied. * Device properties are deep-copied though. */ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { int status; down_write(&__i2c_board_lock); /* dynamic bus numbers will be assigned after the last static one */ if (busnum >= __i2c_first_dynamic_bus_num) __i2c_first_dynamic_bus_num = busnum + 1; for (status = 0; len; len--, info++) { struct i2c_devinfo *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) { pr_debug("i2c-core: can't register boardinfo!\n"); status = -ENOMEM; break; } devinfo->busnum = busnum; devinfo->board_info = *info; if (info->properties) { devinfo->board_info.properties = property_entries_dup(info->properties); if (IS_ERR(devinfo->board_info.properties)) { status = PTR_ERR(devinfo->board_info.properties); kfree(devinfo); break; } } if (info->resources) { devinfo->board_info.resources = kmemdup(info->resources, info->num_resources * sizeof(*info->resources), GFP_KERNEL); if (!devinfo->board_info.resources) { status = -ENOMEM; kfree(devinfo); break; } } list_add_tail(&devinfo->list, &__i2c_board_list); } up_write(&__i2c_board_lock); return status; } ================================================ FILE: t/tree/drivers/i2c/i2c-core-acpi.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * Linux I2C core ACPI support code * * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu */ #include #include #include #include #include #include #include #include "i2c-core.h" struct i2c_acpi_handler_data { struct acpi_connection_info info; struct i2c_adapter *adapter; }; struct gsb_buffer { u8 status; u8 len; union { u16 wdata; u8 bdata; u8 data[0]; }; } __packed; struct i2c_acpi_lookup { struct i2c_board_info *info; acpi_handle adapter_handle; acpi_handle device_handle; acpi_handle search_handle; int n; int index; u32 speed; u32 min_speed; u32 force_speed; }; /** * i2c_acpi_get_i2c_resource - Gets I2cSerialBus resource if type matches * @ares: ACPI resource * @i2c: Pointer to I2cSerialBus resource will be returned here * * Checks if the given ACPI resource is of type I2cSerialBus. * In this case, returns a pointer to it to the caller. * * Returns true if resource type is of I2cSerialBus, otherwise false. */ bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, struct acpi_resource_i2c_serialbus **i2c) { struct acpi_resource_i2c_serialbus *sb; if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) return false; sb = &ares->data.i2c_serial_bus; if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) return false; *i2c = sb; return true; } EXPORT_SYMBOL_GPL(i2c_acpi_get_i2c_resource); static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) { struct i2c_acpi_lookup *lookup = data; struct i2c_board_info *info = lookup->info; struct acpi_resource_i2c_serialbus *sb; acpi_status status; if (info->addr || !i2c_acpi_get_i2c_resource(ares, &sb)) return 1; if (lookup->index != -1 && lookup->n++ != lookup->index) return 1; status = acpi_get_handle(lookup->device_handle, sb->resource_source.string_ptr, &lookup->adapter_handle); if (ACPI_FAILURE(status)) return 1; info->addr = sb->slave_address; lookup->speed = sb->connection_speed; if (sb->access_mode == ACPI_I2C_10BIT_MODE) info->flags |= I2C_CLIENT_TEN; return 1; } static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = { /* * ACPI video acpi_devices, which are handled by the acpi-video driver * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. */ { ACPI_VIDEO_HID, 0 }, {} }; static int i2c_acpi_do_lookup(struct acpi_device *adev, struct i2c_acpi_lookup *lookup) { struct i2c_board_info *info = lookup->info; struct list_head resource_list; int ret; if (acpi_bus_get_status(adev) || !adev->status.present) return -EINVAL; if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) return -ENODEV; memset(info, 0, sizeof(*info)); lookup->device_handle = acpi_device_handle(adev); /* Look up for I2cSerialBus resource */ INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, i2c_acpi_fill_info, lookup); acpi_dev_free_resource_list(&resource_list); if (ret < 0 || !info->addr) return -EINVAL; return 0; } static int i2c_acpi_add_resource(struct acpi_resource *ares, void *data) { int *irq = data; struct resource r; if (*irq <= 0 && acpi_dev_resource_interrupt(ares, 0, &r)) *irq = i2c_dev_irq_from_resources(&r, 1); return 1; /* No need to add resource to the list */ } /** * i2c_acpi_get_irq - get device IRQ number from ACPI * @client: Pointer to the I2C client device * * Find the IRQ number used by a specific client device. * * Return: The IRQ number or an error code. */ int i2c_acpi_get_irq(struct i2c_client *client) { struct acpi_device *adev = ACPI_COMPANION(&client->dev); struct list_head resource_list; int irq = -ENOENT; int ret; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, i2c_acpi_add_resource, &irq); if (ret < 0) return ret; acpi_dev_free_resource_list(&resource_list); if (irq == -ENOENT) irq = acpi_dev_gpio_irq_get(adev, 0); return irq; } static int i2c_acpi_get_info(struct acpi_device *adev, struct i2c_board_info *info, struct i2c_adapter *adapter, acpi_handle *adapter_handle) { struct i2c_acpi_lookup lookup; int ret; memset(&lookup, 0, sizeof(lookup)); lookup.info = info; lookup.index = -1; if (acpi_device_enumerated(adev)) return -EINVAL; ret = i2c_acpi_do_lookup(adev, &lookup); if (ret) return ret; if (adapter) { /* The adapter must match the one in I2cSerialBus() connector */ if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle) return -ENODEV; } else { struct acpi_device *adapter_adev; /* The adapter must be present */ if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev)) return -ENODEV; if (acpi_bus_get_status(adapter_adev) || !adapter_adev->status.present) return -ENODEV; } info->fwnode = acpi_fwnode_handle(adev); if (adapter_handle) *adapter_handle = lookup.adapter_handle; acpi_set_modalias(adev, dev_name(&adev->dev), info->type, sizeof(info->type)); return 0; } static void i2c_acpi_register_device(struct i2c_adapter *adapter, struct acpi_device *adev, struct i2c_board_info *info) { adev->power.flags.ignore_parent = true; acpi_device_set_enumerated(adev); if (!i2c_new_device(adapter, info)) { adev->power.flags.ignore_parent = false; dev_err(&adapter->dev, "failed to add I2C device %s from ACPI\n", dev_name(&adev->dev)); } } static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, void *data, void **return_value) { struct i2c_adapter *adapter = data; struct acpi_device *adev; struct i2c_board_info info; if (acpi_bus_get_device(handle, &adev)) return AE_OK; if (i2c_acpi_get_info(adev, &info, adapter, NULL)) return AE_OK; i2c_acpi_register_device(adapter, adev, &info); return AE_OK; } #define I2C_ACPI_MAX_SCAN_DEPTH 32 /** * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter * @adap: pointer to adapter * * Enumerate all I2C slave devices behind this adapter by walking the ACPI * namespace. When a device is found it will be added to the Linux device * model and bound to the corresponding ACPI handle. */ void i2c_acpi_register_devices(struct i2c_adapter *adap) { acpi_status status; if (!has_acpi_companion(&adap->dev)) return; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, I2C_ACPI_MAX_SCAN_DEPTH, i2c_acpi_add_device, NULL, adap, NULL); if (ACPI_FAILURE(status)) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } const struct acpi_device_id * i2c_acpi_match_device(const struct acpi_device_id *matches, struct i2c_client *client) { if (!(client && matches)) return NULL; return acpi_match_device(matches, &client->dev); } static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { /* * These Silead touchscreen controllers only work at 400KHz, for * some reason they do not work at 100KHz. On some devices the ACPI * tables list another device at their bus as only being capable * of 100KHz, testing has shown that these other devices work fine * at 400KHz (as can be expected of any recent i2c hw) so we force * the speed of the bus to 400 KHz if a Silead device is present. */ { "MSSL1680", 0 }, {} }; static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, void *data, void **return_value) { struct i2c_acpi_lookup *lookup = data; struct acpi_device *adev; if (acpi_bus_get_device(handle, &adev)) return AE_OK; if (i2c_acpi_do_lookup(adev, lookup)) return AE_OK; if (lookup->search_handle != lookup->adapter_handle) return AE_OK; if (lookup->speed <= lookup->min_speed) lookup->min_speed = lookup->speed; if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0) lookup->force_speed = 400000; return AE_OK; } /** * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI * @dev: The device owning the bus * * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves * devices connected to this bus and use the speed of slowest device. * * Returns the speed in Hz or zero */ u32 i2c_acpi_find_bus_speed(struct device *dev) { struct i2c_acpi_lookup lookup; struct i2c_board_info dummy; acpi_status status; if (!has_acpi_companion(dev)) return 0; memset(&lookup, 0, sizeof(lookup)); lookup.search_handle = ACPI_HANDLE(dev); lookup.min_speed = UINT_MAX; lookup.info = &dummy; lookup.index = -1; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, I2C_ACPI_MAX_SCAN_DEPTH, i2c_acpi_lookup_speed, NULL, &lookup, NULL); if (ACPI_FAILURE(status)) { dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); return 0; } if (lookup.force_speed) { if (lookup.force_speed != lookup.min_speed) dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n", lookup.min_speed, lookup.force_speed); return lookup.force_speed; } else if (lookup.min_speed != UINT_MAX) { return lookup.min_speed; } else { return 0; } } EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); static int i2c_acpi_find_match_adapter(struct device *dev, const void *data) { struct i2c_adapter *adapter = i2c_verify_adapter(dev); if (!adapter) return 0; return ACPI_HANDLE(dev) == (acpi_handle)data; } struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) { struct device *dev; dev = bus_find_device(&i2c_bus_type, NULL, handle, i2c_acpi_find_match_adapter); return dev ? i2c_verify_adapter(dev) : NULL; } EXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle); static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) { struct device *dev; dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); return dev ? i2c_verify_client(dev) : NULL; } static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, void *arg) { struct acpi_device *adev = arg; struct i2c_board_info info; acpi_handle adapter_handle; struct i2c_adapter *adapter; struct i2c_client *client; switch (value) { case ACPI_RECONFIG_DEVICE_ADD: if (i2c_acpi_get_info(adev, &info, NULL, &adapter_handle)) break; adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); if (!adapter) break; i2c_acpi_register_device(adapter, adev, &info); break; case ACPI_RECONFIG_DEVICE_REMOVE: if (!acpi_device_enumerated(adev)) break; client = i2c_acpi_find_client_by_adev(adev); if (!client) break; i2c_unregister_device(client); put_device(&client->dev); break; } return NOTIFY_OK; } struct notifier_block i2c_acpi_notifier = { .notifier_call = i2c_acpi_notify, }; /** * i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource * @dev: Device owning the ACPI resources to get the client from * @index: Index of ACPI resource to get * @info: describes the I2C device; note this is modified (addr gets set) * Context: can sleep * * By default the i2c subsys creates an i2c-client for the first I2cSerialBus * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus * resources, in that case this function can be used to create an i2c-client * for other I2cSerialBus resources in the Current Resource Settings table. * * Also see i2c_new_device, which this function calls to create the i2c-client. * * Returns a pointer to the new i2c-client, or error pointer in case of failure. * Specifically, -EPROBE_DEFER is returned if the adapter is not found. */ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, struct i2c_board_info *info) { struct i2c_acpi_lookup lookup; struct i2c_adapter *adapter; struct i2c_client *client; struct acpi_device *adev; LIST_HEAD(resource_list); int ret; adev = ACPI_COMPANION(dev); if (!adev) return ERR_PTR(-EINVAL); memset(&lookup, 0, sizeof(lookup)); lookup.info = info; lookup.device_handle = acpi_device_handle(adev); lookup.index = index; ret = acpi_dev_get_resources(adev, &resource_list, i2c_acpi_fill_info, &lookup); if (ret < 0) return ERR_PTR(ret); acpi_dev_free_resource_list(&resource_list); if (!info->addr) return ERR_PTR(-EADDRNOTAVAIL); adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle); if (!adapter) return ERR_PTR(-EPROBE_DEFER); client = i2c_new_device(adapter, info); if (!client) return ERR_PTR(-ENODEV); return client; } EXPORT_SYMBOL_GPL(i2c_acpi_new_device); #ifdef CONFIG_ACPI_I2C_OPREGION static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { struct i2c_msg msgs[2]; int ret; u8 *buffer; buffer = kzalloc(data_len, GFP_KERNEL); if (!buffer) return AE_NO_MEMORY; msgs[0].addr = client->addr; msgs[0].flags = client->flags; msgs[0].len = 1; msgs[0].buf = &cmd; msgs[1].addr = client->addr; msgs[1].flags = client->flags | I2C_M_RD; msgs[1].len = data_len; msgs[1].buf = buffer; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) { /* Getting a NACK is unfortunately normal with some DSTDs */ if (ret == -EREMOTEIO) dev_dbg(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", data_len, client->addr, cmd, ret); else dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", data_len, client->addr, cmd, ret); /* 2 transfers must have completed successfully */ } else if (ret == 2) { memcpy(data, buffer, data_len); ret = 0; } else { ret = -EIO; } kfree(buffer); return ret; } static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { struct i2c_msg msgs[1]; u8 *buffer; int ret = AE_OK; buffer = kzalloc(data_len + 1, GFP_KERNEL); if (!buffer) return AE_NO_MEMORY; buffer[0] = cmd; memcpy(buffer + 1, data, data_len); msgs[0].addr = client->addr; msgs[0].flags = client->flags; msgs[0].len = data_len + 1; msgs[0].buf = buffer; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); kfree(buffer); if (ret < 0) { dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); return ret; } /* 1 transfer must have completed successfully */ return (ret == 1) ? 0 : -EIO; } static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, void *handler_context, void *region_context) { struct gsb_buffer *gsb = (struct gsb_buffer *)value64; struct i2c_acpi_handler_data *data = handler_context; struct acpi_connection_info *info = &data->info; struct acpi_resource_i2c_serialbus *sb; struct i2c_adapter *adapter = data->adapter; struct i2c_client *client; struct acpi_resource *ares; u32 accessor_type = function >> 16; u8 action = function & ACPI_IO_MASK; acpi_status ret; int status; ret = acpi_buffer_to_resource(info->connection, info->length, &ares); if (ACPI_FAILURE(ret)) return ret; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { ret = AE_NO_MEMORY; goto err; } if (!value64 || !i2c_acpi_get_i2c_resource(ares, &sb)) { ret = AE_BAD_PARAMETER; goto err; } client->adapter = adapter; client->addr = sb->slave_address; if (sb->access_mode == ACPI_I2C_10BIT_MODE) client->flags |= I2C_CLIENT_TEN; switch (accessor_type) { case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: if (action == ACPI_READ) { status = i2c_smbus_read_byte(client); if (status >= 0) { gsb->bdata = status; status = 0; } } else { status = i2c_smbus_write_byte(client, gsb->bdata); } break; case ACPI_GSB_ACCESS_ATTRIB_BYTE: if (action == ACPI_READ) { status = i2c_smbus_read_byte_data(client, command); if (status >= 0) { gsb->bdata = status; status = 0; } } else { status = i2c_smbus_write_byte_data(client, command, gsb->bdata); } break; case ACPI_GSB_ACCESS_ATTRIB_WORD: if (action == ACPI_READ) { status = i2c_smbus_read_word_data(client, command); if (status >= 0) { gsb->wdata = status; status = 0; } } else { status = i2c_smbus_write_word_data(client, command, gsb->wdata); } break; case ACPI_GSB_ACCESS_ATTRIB_BLOCK: if (action == ACPI_READ) { status = i2c_smbus_read_block_data(client, command, gsb->data); if (status >= 0) { gsb->len = status; status = 0; } } else { status = i2c_smbus_write_block_data(client, command, gsb->len, gsb->data); } break; case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: if (action == ACPI_READ) { status = acpi_gsb_i2c_read_bytes(client, command, gsb->data, info->access_length); } else { status = acpi_gsb_i2c_write_bytes(client, command, gsb->data, info->access_length); } break; default: dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", accessor_type, client->addr); ret = AE_BAD_PARAMETER; goto err; } gsb->status = status; err: kfree(client); ACPI_FREE(ares); return ret; } int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { acpi_handle handle; struct i2c_acpi_handler_data *data; acpi_status status; if (!adapter->dev.parent) return -ENODEV; handle = ACPI_HANDLE(adapter->dev.parent); if (!handle) return -ENODEV; data = kzalloc(sizeof(struct i2c_acpi_handler_data), GFP_KERNEL); if (!data) return -ENOMEM; data->adapter = adapter; status = acpi_bus_attach_private_data(handle, (void *)data); if (ACPI_FAILURE(status)) { kfree(data); return -ENOMEM; } status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GSBUS, &i2c_acpi_space_handler, NULL, data); if (ACPI_FAILURE(status)) { dev_err(&adapter->dev, "Error installing i2c space handler\n"); acpi_bus_detach_private_data(handle); kfree(data); return -ENOMEM; } acpi_walk_dep_device_list(handle); return 0; } void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { acpi_handle handle; struct i2c_acpi_handler_data *data; acpi_status status; if (!adapter->dev.parent) return; handle = ACPI_HANDLE(adapter->dev.parent); if (!handle) return; acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GSBUS, &i2c_acpi_space_handler); status = acpi_bus_get_private_data(handle, (void **)&data); if (ACPI_SUCCESS(status)) kfree(data); acpi_bus_detach_private_data(handle); } #endif /* CONFIG_ACPI_I2C_OPREGION */ ================================================ FILE: t/tree/drivers/i2c/i2c-core-base.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * Linux I2C core * * Copyright (C) 1995-99 Simon G. Vogl * With some changes from Kyösti Mälkki * Mux support by Rodolfo Giometti and * Michael Lawnick * * Copyright (C) 2013-2017 Wolfram Sang */ #define pr_fmt(fmt) "i2c-core: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "i2c-core.h" #define CREATE_TRACE_POINTS #include #define I2C_ADDR_OFFSET_TEN_BIT 0xa000 #define I2C_ADDR_OFFSET_SLAVE 0x1000 #define I2C_ADDR_7BITS_MAX 0x77 #define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1) #define I2C_ADDR_DEVICE_ID 0x7c /* * core_lock protects i2c_adapter_idr, and guarantees that device detection, * deletion of detected devices are serialized */ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); static bool is_registered; int i2c_transfer_trace_reg(void) { static_branch_inc(&i2c_trace_msg_key); return 0; } void i2c_transfer_trace_unreg(void) { static_branch_dec(&i2c_trace_msg_key); } const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) { if (!(id && client)) return NULL; while (id->name[0]) { if (strcmp(client->name, id->name) == 0) return id; id++; } return NULL; } EXPORT_SYMBOL_GPL(i2c_match_id); static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; /* Attempt an OF style match */ if (i2c_of_match_device(drv->of_match_table, client)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* Finally an I2C match */ if (i2c_match_id(driver->id_table, client)) return 1; return 0; } static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct i2c_client *client = to_i2c_client(dev); int rc; rc = of_device_uevent_modalias(dev, env); if (rc != -ENODEV) return rc; rc = acpi_device_uevent_modalias(dev, env); if (rc != -ENODEV) return rc; return add_uevent_var(env, "MODALIAS=%s%s", I2C_MODULE_PREFIX, client->name); } /* i2c bus recovery routines */ static int get_scl_gpio_value(struct i2c_adapter *adap) { return gpiod_get_value_cansleep(adap->bus_recovery_info->scl_gpiod); } static void set_scl_gpio_value(struct i2c_adapter *adap, int val) { gpiod_set_value_cansleep(adap->bus_recovery_info->scl_gpiod, val); } static int get_sda_gpio_value(struct i2c_adapter *adap) { return gpiod_get_value_cansleep(adap->bus_recovery_info->sda_gpiod); } static void set_sda_gpio_value(struct i2c_adapter *adap, int val) { gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val); } static int i2c_generic_bus_free(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; int ret = -EOPNOTSUPP; if (bri->get_bus_free) ret = bri->get_bus_free(adap); else if (bri->get_sda) ret = bri->get_sda(adap); if (ret < 0) return ret; return ret ? 0 : -EBUSY; } /* * We are generating clock pulses. ndelay() determines durating of clk pulses. * We will generate clock with rate 100 KHz and so duration of both clock levels * is: delay in ns = (10^6 / 100) / 2 */ #define RECOVERY_NDELAY 5000 #define RECOVERY_CLK_CNT 9 int i2c_generic_scl_recovery(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; int i = 0, scl = 1, ret = 0; if (bri->prepare_recovery) bri->prepare_recovery(adap); /* * If we can set SDA, we will always create a STOP to ensure additional * pulses will do no harm. This is achieved by letting SDA follow SCL * half a cycle later. Check the 'incomplete_write_byte' fault injector * for details. */ bri->set_scl(adap, scl); ndelay(RECOVERY_NDELAY / 2); if (bri->set_sda) bri->set_sda(adap, scl); ndelay(RECOVERY_NDELAY / 2); /* * By this time SCL is high, as we need to give 9 falling-rising edges */ while (i++ < RECOVERY_CLK_CNT * 2) { if (scl) { /* SCL shouldn't be low here */ if (!bri->get_scl(adap)) { dev_err(&adap->dev, "SCL is stuck low, exit recovery\n"); ret = -EBUSY; break; } } scl = !scl; bri->set_scl(adap, scl); /* Creating STOP again, see above */ ndelay(RECOVERY_NDELAY / 2); if (bri->set_sda) bri->set_sda(adap, scl); ndelay(RECOVERY_NDELAY / 2); if (scl) { ret = i2c_generic_bus_free(adap); if (ret == 0) break; } } /* If we can't check bus status, assume recovery worked */ if (ret == -EOPNOTSUPP) ret = 0; if (bri->unprepare_recovery) bri->unprepare_recovery(adap); return ret; } EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery); int i2c_recover_bus(struct i2c_adapter *adap) { if (!adap->bus_recovery_info) return -EOPNOTSUPP; dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); return adap->bus_recovery_info->recover_bus(adap); } EXPORT_SYMBOL_GPL(i2c_recover_bus); static void i2c_init_recovery(struct i2c_adapter *adap) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; char *err_str; if (!bri) return; if (!bri->recover_bus) { err_str = "no recover_bus() found"; goto err; } if (bri->scl_gpiod && bri->recover_bus == i2c_generic_scl_recovery) { bri->get_scl = get_scl_gpio_value; bri->set_scl = set_scl_gpio_value; if (bri->sda_gpiod) { bri->get_sda = get_sda_gpio_value; /* FIXME: add proper flag instead of '0' once available */ if (gpiod_get_direction(bri->sda_gpiod) == 0) bri->set_sda = set_sda_gpio_value; } return; } if (bri->recover_bus == i2c_generic_scl_recovery) { /* Generic SCL recovery */ if (!bri->set_scl || !bri->get_scl) { err_str = "no {get|set}_scl() found"; goto err; } if (!bri->set_sda && !bri->get_sda) { err_str = "either get_sda() or set_sda() needed"; goto err; } } return; err: dev_err(&adap->dev, "Not using recovery: %s\n", err_str); adap->bus_recovery_info = NULL; } static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client) { struct i2c_adapter *adap = client->adapter; unsigned int irq; if (!adap->host_notify_domain) return -ENXIO; if (client->flags & I2C_CLIENT_TEN) return -EINVAL; irq = irq_create_mapping(adap->host_notify_domain, client->addr); return irq > 0 ? irq : -ENXIO; } static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); client->irq = client->init_irq; if (!client->irq && !driver->disable_i2c_core_irq_mapping) { int irq = -ENOENT; if (client->flags & I2C_CLIENT_HOST_NOTIFY) { dev_dbg(dev, "Using Host Notify IRQ\n"); /* Keep adapter active when Host Notify is required */ pm_runtime_get_sync(&client->adapter->dev); irq = i2c_smbus_host_notify_to_irq(client); } else if (dev->of_node) { irq = of_irq_get_byname(dev->of_node, "irq"); if (irq == -EINVAL || irq == -ENODATA) irq = of_irq_get(dev->of_node, 0); } else if (ACPI_COMPANION(dev)) { irq = i2c_acpi_get_irq(client); } if (irq == -EPROBE_DEFER) return irq; if (irq < 0) irq = 0; client->irq = irq; } /* * An I2C ID table is not mandatory, if and only if, a suitable OF * or ACPI ID table is supplied for the probing device. */ if (!driver->id_table && !i2c_acpi_match_device(dev->driver->acpi_match_table, client) && !i2c_of_match_device(dev->driver->of_match_table, client)) return -ENODEV; if (client->flags & I2C_CLIENT_WAKE) { int wakeirq; wakeirq = of_irq_get_byname(dev->of_node, "wakeup"); if (wakeirq == -EPROBE_DEFER) return wakeirq; device_init_wakeup(&client->dev, true); if (wakeirq > 0 && wakeirq != client->irq) status = dev_pm_set_dedicated_wake_irq(dev, wakeirq); else if (client->irq > 0) status = dev_pm_set_wake_irq(dev, client->irq); else status = 0; if (status) dev_warn(&client->dev, "failed to set up wakeup irq\n"); } dev_dbg(dev, "probe\n"); status = of_clk_set_defaults(dev->of_node, false); if (status < 0) goto err_clear_wakeup_irq; status = dev_pm_domain_attach(&client->dev, true); if (status) goto err_clear_wakeup_irq; /* * When there are no more users of probe(), * rename probe_new to probe. */ if (driver->probe_new) status = driver->probe_new(client); else if (driver->probe) status = driver->probe(client, i2c_match_id(driver->id_table, client)); else status = -EINVAL; if (status) goto err_detach_pm_domain; return 0; err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); err_clear_wakeup_irq: dev_pm_clear_wake_irq(&client->dev); device_init_wakeup(&client->dev, false); return status; } static int i2c_device_remove(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status = 0; if (!client || !dev->driver) return 0; driver = to_i2c_driver(dev->driver); if (driver->remove) { dev_dbg(dev, "remove\n"); status = driver->remove(client); } dev_pm_domain_detach(&client->dev, true); dev_pm_clear_wake_irq(&client->dev); device_init_wakeup(&client->dev, false); client->irq = 0; if (client->flags & I2C_CLIENT_HOST_NOTIFY) pm_runtime_put(&client->adapter->dev); return status; } static void i2c_device_shutdown(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; if (!client || !dev->driver) return; driver = to_i2c_driver(dev->driver); if (driver->shutdown) driver->shutdown(client); } static void i2c_client_dev_release(struct device *dev) { kfree(to_i2c_client(dev)); } static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", dev->type == &i2c_client_type ? to_i2c_client(dev)->name : to_i2c_adapter(dev)->name); } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); int len; len = of_device_modalias(dev, buf, PAGE_SIZE); if (len != -ENODEV) return len; len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); if (len != -ENODEV) return len; return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name); } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); static struct attribute *i2c_dev_attrs[] = { &dev_attr_name.attr, /* modalias helps coldplug: modprobe $(cat .../modalias) */ &dev_attr_modalias.attr, NULL }; ATTRIBUTE_GROUPS(i2c_dev); struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, }; EXPORT_SYMBOL_GPL(i2c_bus_type); struct device_type i2c_client_type = { .groups = i2c_dev_groups, .uevent = i2c_device_uevent, .release = i2c_client_dev_release, }; EXPORT_SYMBOL_GPL(i2c_client_type); /** * i2c_verify_client - return parameter as i2c_client, or NULL * @dev: device, probably from some driver model iterator * * When traversing the driver model tree, perhaps using driver model * iterators like @device_for_each_child(), you can't assume very much * about the nodes you find. Use this function to avoid oopses caused * by wrongly treating some non-I2C device as an i2c_client. */ struct i2c_client *i2c_verify_client(struct device *dev) { return (dev->type == &i2c_client_type) ? to_i2c_client(dev) : NULL; } EXPORT_SYMBOL(i2c_verify_client); /* Return a unique address which takes the flags of the client into account */ static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client) { unsigned short addr = client->addr; /* For some client flags, add an arbitrary offset to avoid collisions */ if (client->flags & I2C_CLIENT_TEN) addr |= I2C_ADDR_OFFSET_TEN_BIT; if (client->flags & I2C_CLIENT_SLAVE) addr |= I2C_ADDR_OFFSET_SLAVE; return addr; } /* This is a permissive address validity check, I2C address map constraints * are purposely not enforced, except for the general call address. */ static int i2c_check_addr_validity(unsigned int addr, unsigned short flags) { if (flags & I2C_CLIENT_TEN) { /* 10-bit address, all values are valid */ if (addr > 0x3ff) return -EINVAL; } else { /* 7-bit address, reject the general call address */ if (addr == 0x00 || addr > 0x7f) return -EINVAL; } return 0; } /* And this is a strict address validity check, used when probing. If a * device uses a reserved address, then it shouldn't be probed. 7-bit * addressing is assumed, 10-bit address devices are rare and should be * explicitly enumerated. */ int i2c_check_7bit_addr_validity_strict(unsigned short addr) { /* * Reserved addresses per I2C specification: * 0x00 General call address / START byte * 0x01 CBUS address * 0x02 Reserved for different bus format * 0x03 Reserved for future purposes * 0x04-0x07 Hs-mode master code * 0x78-0x7b 10-bit slave addressing * 0x7c-0x7f Reserved for future purposes */ if (addr < 0x08 || addr > 0x77) return -EINVAL; return 0; } static int __i2c_check_addr_busy(struct device *dev, void *addrp) { struct i2c_client *client = i2c_verify_client(dev); int addr = *(int *)addrp; if (client && i2c_encode_flags_to_addr(client) == addr) return -EBUSY; return 0; } /* walk up mux tree */ static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result; result = device_for_each_child(&adapter->dev, &addr, __i2c_check_addr_busy); if (!result && parent) result = i2c_check_mux_parents(parent, addr); return result; } /* recurse down mux tree */ static int i2c_check_mux_children(struct device *dev, void *addrp) { int result; if (dev->type == &i2c_adapter_type) result = device_for_each_child(dev, addrp, i2c_check_mux_children); else result = __i2c_check_addr_busy(dev, addrp); return result; } static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result = 0; if (parent) result = i2c_check_mux_parents(parent, addr); if (!result) result = device_for_each_child(&adapter->dev, &addr, i2c_check_mux_children); return result; } /** * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT * locks only this branch in the adapter tree */ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter)); } /** * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT * trylocks only this branch in the adapter tree */ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) { return rt_mutex_trylock(&adapter->bus_lock); } /** * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT * unlocks only this branch in the adapter tree */ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) { rt_mutex_unlock(&adapter->bus_lock); } static void i2c_dev_set_name(struct i2c_adapter *adap, struct i2c_client *client, struct i2c_board_info const *info) { struct acpi_device *adev = ACPI_COMPANION(&client->dev); if (info && info->dev_name) { dev_set_name(&client->dev, "i2c-%s", info->dev_name); return; } if (adev) { dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev)); return; } dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), i2c_encode_flags_to_addr(client)); } int i2c_dev_irq_from_resources(const struct resource *resources, unsigned int num_resources) { struct irq_data *irqd; int i; for (i = 0; i < num_resources; i++) { const struct resource *r = &resources[i]; if (resource_type(r) != IORESOURCE_IRQ) continue; if (r->flags & IORESOURCE_BITS) { irqd = irq_get_irq_data(r->start); if (!irqd) break; irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); } return r->start; } return 0; } /** * i2c_new_client_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * Create an i2c device. Binding is handled through driver model * probe()/remove() methods. A driver may be bound to this device when we * return from this function, or any later moment (e.g. maybe hotplugging will * load the driver module). This call is not appropriate for use by mainboard * initialization logic, which usually runs during an arch_initcall() long * before any i2c_adapter could exist. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or an ERR_PTR to describe the error. */ struct i2c_client * i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); if (!client) return ERR_PTR(-ENOMEM); client->adapter = adap; client->dev.platform_data = info->platform_data; client->flags = info->flags; client->addr = info->addr; client->init_irq = info->irq; if (!client->init_irq) client->init_irq = i2c_dev_irq_from_resources(info->resources, info->num_resources); strlcpy(client->name, info->type, sizeof(client->name)); status = i2c_check_addr_validity(client->addr, client->flags); if (status) { dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); goto out_err_silent; } /* Check for address business */ status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); if (status) goto out_err; client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; client->dev.of_node = of_node_get(info->of_node); client->dev.fwnode = info->fwnode; i2c_dev_set_name(adap, client, info); if (info->properties) { status = device_add_properties(&client->dev, info->properties); if (status) { dev_err(&adap->dev, "Failed to add properties to client %s: %d\n", client->name, status); goto out_err_put_of_node; } } status = device_register(&client->dev); if (status) goto out_free_props; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; out_free_props: if (info->properties) device_remove_properties(&client->dev); out_err_put_of_node: of_node_put(info->of_node); out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x (%d)\n", client->name, client->addr, status); out_err_silent: kfree(client); return ERR_PTR(status); } EXPORT_SYMBOL_GPL(i2c_new_client_device); /** * i2c_new_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * This deprecated function has the same functionality as * @i2c_new_client_device, it just returns NULL instead of an ERR_PTR in case of * an error for compatibility with current I2C API. It will be removed once all * users are converted. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or NULL to indicate an error. */ struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *ret; ret = i2c_new_client_device(adap, info); return IS_ERR(ret) ? NULL : ret; } EXPORT_SYMBOL_GPL(i2c_new_device); /** * i2c_unregister_device - reverse effect of i2c_new_device() * @client: value returned from i2c_new_device() * Context: can sleep */ void i2c_unregister_device(struct i2c_client *client) { if (IS_ERR_OR_NULL(client)) return; if (client->dev.of_node) { of_node_clear_flag(client->dev.of_node, OF_POPULATED); of_node_put(client->dev.of_node); } if (ACPI_COMPANION(&client->dev)) acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev)); device_unregister(&client->dev); } EXPORT_SYMBOL_GPL(i2c_unregister_device); static const struct i2c_device_id dummy_id[] = { { "dummy", 0 }, { }, }; static int dummy_probe(struct i2c_client *client, const struct i2c_device_id *id) { return 0; } static int dummy_remove(struct i2c_client *client) { return 0; } static struct i2c_driver dummy_driver = { .driver.name = "dummy", .probe = dummy_probe, .remove = dummy_remove, .id_table = dummy_id, }; /** * i2c_new_dummy_device - return a new i2c device bound to a dummy driver * @adapter: the adapter managing the device * @address: seven bit address to be used * Context: can sleep * * This returns an I2C client bound to the "dummy" driver, intended for use * with devices that consume multiple addresses. Examples of such chips * include various EEPROMS (like 24c04 and 24c08 models). * * These dummy devices have two main uses. First, most I2C and SMBus calls * except i2c_transfer() need a client handle; the dummy will be that handle. * And second, this prevents the specified address from being bound to a * different driver. * * This returns the new i2c client, which should be saved for later use with * i2c_unregister_device(); or an ERR_PTR to describe the error. */ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address) { struct i2c_board_info info = { I2C_BOARD_INFO("dummy", address), }; return i2c_new_client_device(adapter, &info); } EXPORT_SYMBOL_GPL(i2c_new_dummy_device); /** * i2c_new_dummy - return a new i2c device bound to a dummy driver * @adapter: the adapter managing the device * @address: seven bit address to be used * Context: can sleep * * This deprecated function has the same functionality as @i2c_new_dummy_device, * it just returns NULL instead of an ERR_PTR in case of an error for * compatibility with current I2C API. It will be removed once all users are * converted. * * This returns the new i2c client, which should be saved for later use with * i2c_unregister_device(); or NULL to indicate an error. */ struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) { struct i2c_client *ret; ret = i2c_new_dummy_device(adapter, address); return IS_ERR(ret) ? NULL : ret; } EXPORT_SYMBOL_GPL(i2c_new_dummy); struct i2c_dummy_devres { struct i2c_client *client; }; static void devm_i2c_release_dummy(struct device *dev, void *res) { struct i2c_dummy_devres *this = res; i2c_unregister_device(this->client); } /** * devm_i2c_new_dummy_device - return a new i2c device bound to a dummy driver * @dev: device the managed resource is bound to * @adapter: the adapter managing the device * @address: seven bit address to be used * Context: can sleep * * This is the device-managed version of @i2c_new_dummy_device. It returns the * new i2c client or an ERR_PTR in case of an error. */ struct i2c_client *devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adapter, u16 address) { struct i2c_dummy_devres *dr; struct i2c_client *client; dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL); if (!dr) return ERR_PTR(-ENOMEM); client = i2c_new_dummy_device(adapter, address); if (IS_ERR(client)) { devres_free(dr); } else { dr->client = client; devres_add(dev, dr); } return client; } EXPORT_SYMBOL_GPL(devm_i2c_new_dummy_device); /** * i2c_new_ancillary_device - Helper to get the instantiated secondary address * and create the associated device * @client: Handle to the primary client * @name: Handle to specify which secondary address to get * @default_addr: Used as a fallback if no secondary address was specified * Context: can sleep * * I2C clients can be composed of multiple I2C slaves bound together in a single * component. The I2C client driver then binds to the master I2C slave and needs * to create I2C dummy clients to communicate with all the other slaves. * * This function creates and returns an I2C dummy client whose I2C address is * retrieved from the platform firmware based on the given slave name. If no * address is specified by the firmware default_addr is used. * * On DT-based platforms the address is retrieved from the "reg" property entry * cell whose "reg-names" value matches the slave name. * * This returns the new i2c client, which should be saved for later use with * i2c_unregister_device(); or an ERR_PTR to describe the error. */ struct i2c_client *i2c_new_ancillary_device(struct i2c_client *client, const char *name, u16 default_addr) { struct device_node *np = client->dev.of_node; u32 addr = default_addr; int i; if (np) { i = of_property_match_string(np, "reg-names", name); if (i >= 0) of_property_read_u32_index(np, "reg", i, &addr); } dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr); return i2c_new_dummy_device(client->adapter, addr); } EXPORT_SYMBOL_GPL(i2c_new_ancillary_device); /* ------------------------------------------------------------------------- */ /* I2C bus adapters -- one roots each I2C or SMBUS segment */ static void i2c_adapter_dev_release(struct device *dev) { struct i2c_adapter *adap = to_i2c_adapter(dev); complete(&adap->dev_released); } unsigned int i2c_adapter_depth(struct i2c_adapter *adapter) { unsigned int depth = 0; while ((adapter = i2c_parent_is_i2c_adapter(adapter))) depth++; WARN_ONCE(depth >= MAX_LOCKDEP_SUBCLASSES, "adapter depth exceeds lockdep subclass limit\n"); return depth; } EXPORT_SYMBOL_GPL(i2c_adapter_depth); /* * Let users instantiate I2C devices through sysfs. This can be used when * platform initialization code doesn't contain the proper data for * whatever reason. Also useful for drivers that do device detection and * detection fails, either because the device uses an unexpected address, * or this is a compatible device with different ID register values. * * Parameter checking may look overzealous, but we really don't want * the user to provide incorrect parameters. */ static ssize_t i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_adapter *adap = to_i2c_adapter(dev); struct i2c_board_info info; struct i2c_client *client; char *blank, end; int res; memset(&info, 0, sizeof(struct i2c_board_info)); blank = strchr(buf, ' '); if (!blank) { dev_err(dev, "%s: Missing parameters\n", "new_device"); return -EINVAL; } if (blank - buf > I2C_NAME_SIZE - 1) { dev_err(dev, "%s: Invalid device name\n", "new_device"); return -EINVAL; } memcpy(info.type, buf, blank - buf); /* Parse remaining parameters, reject extra parameters */ res = sscanf(++blank, "%hi%c", &info.addr, &end); if (res < 1) { dev_err(dev, "%s: Can't parse I2C address\n", "new_device"); return -EINVAL; } if (res > 1 && end != '\n') { dev_err(dev, "%s: Extra parameters\n", "new_device"); return -EINVAL; } if ((info.addr & I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) { info.addr &= ~I2C_ADDR_OFFSET_TEN_BIT; info.flags |= I2C_CLIENT_TEN; } if (info.addr & I2C_ADDR_OFFSET_SLAVE) { info.addr &= ~I2C_ADDR_OFFSET_SLAVE; info.flags |= I2C_CLIENT_SLAVE; } client = i2c_new_client_device(adap, &info); if (IS_ERR(client)) return PTR_ERR(client); /* Keep track of the added device */ mutex_lock(&adap->userspace_clients_lock); list_add_tail(&client->detected, &adap->userspace_clients); mutex_unlock(&adap->userspace_clients_lock); dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", info.type, info.addr); return count; } static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); /* * And of course let the users delete the devices they instantiated, if * they got it wrong. This interface can only be used to delete devices * instantiated by i2c_sysfs_new_device above. This guarantees that we * don't delete devices to which some kernel code still has references. * * Parameter checking may look overzealous, but we really don't want * the user to delete the wrong device. */ static ssize_t i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_adapter *adap = to_i2c_adapter(dev); struct i2c_client *client, *next; unsigned short addr; char end; int res; /* Parse parameters, reject extra parameters */ res = sscanf(buf, "%hi%c", &addr, &end); if (res < 1) { dev_err(dev, "%s: Can't parse I2C address\n", "delete_device"); return -EINVAL; } if (res > 1 && end != '\n') { dev_err(dev, "%s: Extra parameters\n", "delete_device"); return -EINVAL; } /* Make sure the device was added through sysfs */ res = -ENOENT; mutex_lock_nested(&adap->userspace_clients_lock, i2c_adapter_depth(adap)); list_for_each_entry_safe(client, next, &adap->userspace_clients, detected) { if (i2c_encode_flags_to_addr(client) == addr) { dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", "delete_device", client->name, client->addr); list_del(&client->detected); i2c_unregister_device(client); res = count; break; } } mutex_unlock(&adap->userspace_clients_lock); if (res < 0) dev_err(dev, "%s: Can't find device in list\n", "delete_device"); return res; } static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device); static struct attribute *i2c_adapter_attrs[] = { &dev_attr_name.attr, &dev_attr_new_device.attr, &dev_attr_delete_device.attr, NULL }; ATTRIBUTE_GROUPS(i2c_adapter); struct device_type i2c_adapter_type = { .groups = i2c_adapter_groups, .release = i2c_adapter_dev_release, }; EXPORT_SYMBOL_GPL(i2c_adapter_type); /** * i2c_verify_adapter - return parameter as i2c_adapter or NULL * @dev: device, probably from some driver model iterator * * When traversing the driver model tree, perhaps using driver model * iterators like @device_for_each_child(), you can't assume very much * about the nodes you find. Use this function to avoid oopses caused * by wrongly treating some non-I2C device as an i2c_adapter. */ struct i2c_adapter *i2c_verify_adapter(struct device *dev) { return (dev->type == &i2c_adapter_type) ? to_i2c_adapter(dev) : NULL; } EXPORT_SYMBOL(i2c_verify_adapter); #ifdef CONFIG_I2C_COMPAT static struct class_compat *i2c_adapter_compat_class; #endif static void i2c_scan_static_board_info(struct i2c_adapter *adapter) { struct i2c_devinfo *devinfo; down_read(&__i2c_board_lock); list_for_each_entry(devinfo, &__i2c_board_list, list) { if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info)) dev_err(&adapter->dev, "Can't create device at 0x%02x\n", devinfo->board_info.addr); } up_read(&__i2c_board_lock); } static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap) { /* Detect supported devices on that bus, and instantiate them */ i2c_detect(adap, driver); return 0; } static int __process_new_adapter(struct device_driver *d, void *data) { return i2c_do_add_adapter(to_i2c_driver(d), data); } static const struct i2c_lock_operations i2c_adapter_lock_ops = { .lock_bus = i2c_adapter_lock_bus, .trylock_bus = i2c_adapter_trylock_bus, .unlock_bus = i2c_adapter_unlock_bus, }; static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap) { struct irq_domain *domain = adap->host_notify_domain; irq_hw_number_t hwirq; if (!domain) return; for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++) irq_dispose_mapping(irq_find_mapping(domain, hwirq)); irq_domain_remove(domain); adap->host_notify_domain = NULL; } static int i2c_host_notify_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw_irq_num) { irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq); return 0; } static const struct irq_domain_ops i2c_host_notify_irq_ops = { .map = i2c_host_notify_irq_map, }; static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap) { struct irq_domain *domain; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY)) return 0; domain = irq_domain_create_linear(adap->dev.fwnode, I2C_ADDR_7BITS_COUNT, &i2c_host_notify_irq_ops, adap); if (!domain) return -ENOMEM; adap->host_notify_domain = domain; return 0; } /** * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct * I2C client. * @adap: the adapter * @addr: the I2C address of the notifying device * Context: can't sleep * * Helper function to be called from an I2C bus driver's interrupt * handler. It will schedule the Host Notify IRQ. */ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) { int irq; if (!adap) return -EINVAL; irq = irq_find_mapping(adap->host_notify_domain, addr); if (irq <= 0) return -ENXIO; generic_handle_irq(irq); return 0; } EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); static int i2c_register_adapter(struct i2c_adapter *adap) { int res = -EINVAL; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) { res = -EAGAIN; goto out_list; } /* Sanity checks */ if (WARN(!adap->name[0], "i2c adapter has no name")) goto out_list; if (!adap->algo) { pr_err("adapter '%s': no algo supplied!\n", adap->name); goto out_list; } if (!adap->lock_ops) adap->lock_ops = &i2c_adapter_lock_ops; adap->locked_flags = 0; rt_mutex_init(&adap->bus_lock); rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) adap->timeout = HZ; /* register soft irqs for Host Notify */ res = i2c_setup_host_notify_irq_domain(adap); if (res) { pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", adap->name, res); goto out_list; } dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); goto out_list; } res = of_i2c_setup_smbus_alert(adap); if (res) goto out_reg; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); pm_runtime_no_callbacks(&adap->dev); pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); #ifdef CONFIG_I2C_COMPAT res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent); if (res) dev_warn(&adap->dev, "Failed to create compatibility class link\n"); #endif i2c_init_recovery(adap); /* create pre-declared device nodes */ of_i2c_register_devices(adap); i2c_acpi_register_devices(adap); i2c_acpi_install_space_handler(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); /* Notify drivers */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); mutex_unlock(&core_lock); return 0; out_reg: init_completion(&adap->dev_released); device_unregister(&adap->dev); wait_for_completion(&adap->dev_released); out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); return res; } /** * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1 * @adap: the adapter to register (with adap->nr initialized) * Context: can sleep * * See i2c_add_numbered_adapter() for details. */ static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; mutex_lock(&core_lock); id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) return id == -ENOSPC ? -EBUSY : id; return i2c_register_adapter(adap); } /** * i2c_add_adapter - declare i2c adapter, use dynamic bus number * @adapter: the adapter to add * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * doesn't matter or when its bus number is specified by an dt alias. * Examples of bases when the bus number doesn't matter: I2C adapters * dynamically added by USB links or PCI plugin cards. * * When this returns zero, a new bus number was allocated and stored * in adap->nr, and the specified adapter became available for clients. * Otherwise, a negative errno value is returned. */ int i2c_add_adapter(struct i2c_adapter *adapter) { struct device *dev = &adapter->dev; int id; if (dev->of_node) { id = of_alias_get_id(dev->of_node, "i2c"); if (id >= 0) { adapter->nr = id; return __i2c_add_numbered_adapter(adapter); } } mutex_lock(&core_lock); id = idr_alloc(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) return id; adapter->nr = id; return i2c_register_adapter(adapter); } EXPORT_SYMBOL(i2c_add_adapter); /** * i2c_add_numbered_adapter - declare i2c adapter, use static bus number * @adap: the adapter to register (with adap->nr initialized) * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * matters. For example, use it for I2C adapters from system-on-chip CPUs, * or otherwise built in to the system's mainboard, and where i2c_board_info * is used to properly configure I2C devices. * * If the requested bus number is set to -1, then this function will behave * identically to i2c_add_adapter, and will dynamically assign a bus number. * * If no devices have pre-been declared for this bus, then be sure to * register the adapter before any dynamically allocated ones. Otherwise * the required bus ID may not be available. * * When this returns zero, the specified adapter became available for * clients using the bus number provided in adap->nr. Also, the table * of I2C devices pre-declared using i2c_register_board_info() is scanned, * and the appropriate driver model device nodes are created. Otherwise, a * negative errno value is returned. */ int i2c_add_numbered_adapter(struct i2c_adapter *adap) { if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); return __i2c_add_numbered_adapter(adap); } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); static void i2c_do_del_adapter(struct i2c_driver *driver, struct i2c_adapter *adapter) { struct i2c_client *client, *_n; /* Remove the devices we created ourselves as the result of hardware * probing (using a driver's detect method) */ list_for_each_entry_safe(client, _n, &driver->clients, detected) { if (client->adapter == adapter) { dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", client->name, client->addr); list_del(&client->detected); i2c_unregister_device(client); } } } static int __unregister_client(struct device *dev, void *dummy) { struct i2c_client *client = i2c_verify_client(dev); if (client && strcmp(client->name, "dummy")) i2c_unregister_device(client); return 0; } static int __unregister_dummy(struct device *dev, void *dummy) { struct i2c_client *client = i2c_verify_client(dev); i2c_unregister_device(client); return 0; } static int __process_removed_adapter(struct device_driver *d, void *data) { i2c_do_del_adapter(to_i2c_driver(d), data); return 0; } /** * i2c_del_adapter - unregister I2C adapter * @adap: the adapter being unregistered * Context: can sleep * * This unregisters an I2C adapter which was previously registered * by @i2c_add_adapter or @i2c_add_numbered_adapter. */ void i2c_del_adapter(struct i2c_adapter *adap) { struct i2c_adapter *found; struct i2c_client *client, *next; /* First make sure that this adapter was ever added */ mutex_lock(&core_lock); found = idr_find(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); if (found != adap) { pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); return; } i2c_acpi_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_removed_adapter); mutex_unlock(&core_lock); /* Remove devices instantiated from sysfs */ mutex_lock_nested(&adap->userspace_clients_lock, i2c_adapter_depth(adap)); list_for_each_entry_safe(client, next, &adap->userspace_clients, detected) { dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, client->addr); list_del(&client->detected); i2c_unregister_device(client); } mutex_unlock(&adap->userspace_clients_lock); /* Detach any active clients. This can't fail, thus we do not * check the returned value. This is a two-pass process, because * we can't remove the dummy devices during the first pass: they * could have been instantiated by real devices wishing to clean * them up properly, so we give them a chance to do that first. */ device_for_each_child(&adap->dev, NULL, __unregister_client); device_for_each_child(&adap->dev, NULL, __unregister_dummy); #ifdef CONFIG_I2C_COMPAT class_compat_remove_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent); #endif /* device name is gone after device_unregister */ dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); pm_runtime_disable(&adap->dev); i2c_host_notify_irq_teardown(adap); /* wait until all references to the device are gone * * FIXME: This is old code and should ideally be replaced by an * alternative which results in decoupling the lifetime of the struct * device from the i2c_adapter, like spi or netdev do. Any solution * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled! */ init_completion(&adap->dev_released); device_unregister(&adap->dev); wait_for_completion(&adap->dev_released); /* free bus id */ mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); /* Clear the device structure in case this adapter is ever going to be added again */ memset(&adap->dev, 0, sizeof(adap->dev)); } EXPORT_SYMBOL(i2c_del_adapter); /** * i2c_parse_fw_timings - get I2C related timing parameters from firmware * @dev: The device to scan for I2C timing properties * @t: the i2c_timings struct to be filled with values * @use_defaults: bool to use sane defaults derived from the I2C specification * when properties are not found, otherwise use 0 * * Scan the device for the generic I2C properties describing timing parameters * for the signal and fill the given struct with the results. If a property was * not found and use_defaults was true, then maximum timings are assumed which * are derived from the I2C specification. If use_defaults is not used, the * results will be 0, so drivers can apply their own defaults later. The latter * is mainly intended for avoiding regressions of existing drivers which want * to switch to this function. New drivers almost always should use the defaults. */ void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults) { int ret; memset(t, 0, sizeof(*t)); ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz); if (ret && use_defaults) t->bus_freq_hz = 100000; ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns); if (ret && use_defaults) { if (t->bus_freq_hz <= 100000) t->scl_rise_ns = 1000; else if (t->bus_freq_hz <= 400000) t->scl_rise_ns = 300; else t->scl_rise_ns = 120; } ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns); if (ret && use_defaults) { if (t->bus_freq_hz <= 400000) t->scl_fall_ns = 300; else t->scl_fall_ns = 120; } device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns); ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns); if (ret && use_defaults) t->sda_fall_ns = t->scl_fall_ns; device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns); } EXPORT_SYMBOL_GPL(i2c_parse_fw_timings); /* ------------------------------------------------------------------------- */ int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data)) { int res; mutex_lock(&core_lock); res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); mutex_unlock(&core_lock); return res; } EXPORT_SYMBOL_GPL(i2c_for_each_dev); static int __process_new_driver(struct device *dev, void *data) { if (dev->type != &i2c_adapter_type) return 0; return i2c_do_add_adapter(data, to_i2c_adapter(dev)); } /* * An i2c_driver is used with one or more i2c_client (device) nodes to access * i2c slave chips, on a bus instance associated with some i2c_adapter. */ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { int res; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) return -EAGAIN; /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; INIT_LIST_HEAD(&driver->clients); /* When registration returns, the driver core * will have called probe() for all matching-but-unbound devices. */ res = driver_register(&driver->driver); if (res) return res; pr_debug("driver [%s] registered\n", driver->driver.name); /* Walk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver); return 0; } EXPORT_SYMBOL(i2c_register_driver); static int __process_removed_driver(struct device *dev, void *data) { if (dev->type == &i2c_adapter_type) i2c_do_del_adapter(data, to_i2c_adapter(dev)); return 0; } /** * i2c_del_driver - unregister I2C driver * @driver: the driver being unregistered * Context: can sleep */ void i2c_del_driver(struct i2c_driver *driver) { i2c_for_each_dev(driver, __process_removed_driver); driver_unregister(&driver->driver); pr_debug("driver [%s] unregistered\n", driver->driver.name); } EXPORT_SYMBOL(i2c_del_driver); /* ------------------------------------------------------------------------- */ /** * i2c_use_client - increments the reference count of the i2c client structure * @client: the client being referenced * * Each live reference to a client should be refcounted. The driver model does * that automatically as part of driver binding, so that most drivers don't * need to do this explicitly: they hold a reference until they're unbound * from the device. * * A pointer to the client with the incremented reference counter is returned. */ struct i2c_client *i2c_use_client(struct i2c_client *client) { if (client && get_device(&client->dev)) return client; return NULL; } EXPORT_SYMBOL(i2c_use_client); /** * i2c_release_client - release a use of the i2c client structure * @client: the client being no longer referenced * * Must be called when a user of a client is finished with it. */ void i2c_release_client(struct i2c_client *client) { if (client) put_device(&client->dev); } EXPORT_SYMBOL(i2c_release_client); struct i2c_cmd_arg { unsigned cmd; void *arg; }; static int i2c_cmd(struct device *dev, void *_arg) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_cmd_arg *arg = _arg; struct i2c_driver *driver; if (!client || !client->dev.driver) return 0; driver = to_i2c_driver(client->dev.driver); if (driver->command) driver->command(client, arg->cmd, arg->arg); return 0; } void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) { struct i2c_cmd_arg cmd_arg; cmd_arg.cmd = cmd; cmd_arg.arg = arg; device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd); } EXPORT_SYMBOL(i2c_clients_command); static int __init i2c_init(void) { int retval; retval = of_alias_get_highest_id("i2c"); down_write(&__i2c_board_lock); if (retval >= __i2c_first_dynamic_bus_num) __i2c_first_dynamic_bus_num = retval + 1; up_write(&__i2c_board_lock); retval = bus_register(&i2c_bus_type); if (retval) return retval; is_registered = true; #ifdef CONFIG_I2C_COMPAT i2c_adapter_compat_class = class_compat_register("i2c-adapter"); if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; } #endif retval = i2c_add_driver(&dummy_driver); if (retval) goto class_err; if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); return 0; class_err: #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); bus_err: #endif is_registered = false; bus_unregister(&i2c_bus_type); return retval; } static void __exit i2c_exit(void) { if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier)); if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier)); i2c_del_driver(&dummy_driver); #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); #endif bus_unregister(&i2c_bus_type); tracepoint_synchronize_unregister(); } /* We must initialize early, because some subsystems register i2c drivers * in subsys_initcall() code, but are linked (and initialized) before i2c. */ postcore_initcall(i2c_init); module_exit(i2c_exit); /* ---------------------------------------------------- * the functional interface to the i2c busses. * ---------------------------------------------------- */ /* Check if val is exceeding the quirk IFF quirk is non 0 */ #define i2c_quirk_exceeded(val, quirk) ((quirk) && ((val) > (quirk))) static int i2c_quirk_error(struct i2c_adapter *adap, struct i2c_msg *msg, char *err_msg) { dev_err_ratelimited(&adap->dev, "adapter quirk: %s (addr 0x%04x, size %u, %s)\n", err_msg, msg->addr, msg->len, msg->flags & I2C_M_RD ? "read" : "write"); return -EOPNOTSUPP; } static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { const struct i2c_adapter_quirks *q = adap->quirks; int max_num = q->max_num_msgs, i; bool do_len_check = true; if (q->flags & I2C_AQ_COMB) { max_num = 2; /* special checks for combined messages */ if (num == 2) { if (q->flags & I2C_AQ_COMB_WRITE_FIRST && msgs[0].flags & I2C_M_RD) return i2c_quirk_error(adap, &msgs[0], "1st comb msg must be write"); if (q->flags & I2C_AQ_COMB_READ_SECOND && !(msgs[1].flags & I2C_M_RD)) return i2c_quirk_error(adap, &msgs[1], "2nd comb msg must be read"); if (q->flags & I2C_AQ_COMB_SAME_ADDR && msgs[0].addr != msgs[1].addr) return i2c_quirk_error(adap, &msgs[0], "comb msg only to same addr"); if (i2c_quirk_exceeded(msgs[0].len, q->max_comb_1st_msg_len)) return i2c_quirk_error(adap, &msgs[0], "msg too long"); if (i2c_quirk_exceeded(msgs[1].len, q->max_comb_2nd_msg_len)) return i2c_quirk_error(adap, &msgs[1], "msg too long"); do_len_check = false; } } if (i2c_quirk_exceeded(num, max_num)) return i2c_quirk_error(adap, &msgs[0], "too many messages"); for (i = 0; i < num; i++) { u16 len = msgs[i].len; if (msgs[i].flags & I2C_M_RD) { if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len)) return i2c_quirk_error(adap, &msgs[i], "msg too long"); if (q->flags & I2C_AQ_NO_ZERO_LEN_READ && len == 0) return i2c_quirk_error(adap, &msgs[i], "no zero length"); } else { if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len)) return i2c_quirk_error(adap, &msgs[i], "msg too long"); if (q->flags & I2C_AQ_NO_ZERO_LEN_WRITE && len == 0) return i2c_quirk_error(adap, &msgs[i], "no zero length"); } } return 0; } /** * __i2c_transfer - unlocked flavor of i2c_transfer * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Adapter lock must be held when calling this function. No debug logging * takes place. adap->algo->master_xfer existence isn't checked. */ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { unsigned long orig_jiffies; int ret, try; if (WARN_ON(!msgs || num < 1)) return -EINVAL; ret = __i2c_check_suspended(adap); if (ret) return ret; if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) return -EOPNOTSUPP; /* * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets * enabled. This is an efficient way of keeping the for-loop from * being executed when not needed. */ if (static_branch_unlikely(&i2c_trace_msg_key)) { int i; for (i = 0; i < num; i++) if (msgs[i].flags & I2C_M_RD) trace_i2c_read(adap, &msgs[i], i); else trace_i2c_write(adap, &msgs[i], i); } /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (ret = 0, try = 0; try <= adap->retries; try++) { if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic) ret = adap->algo->master_xfer_atomic(adap, msgs, num); else ret = adap->algo->master_xfer(adap, msgs, num); if (ret != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } if (static_branch_unlikely(&i2c_trace_msg_key)) { int i; for (i = 0; i < ret; i++) if (msgs[i].flags & I2C_M_RD) trace_i2c_reply(adap, &msgs[i], i); trace_i2c_result(adap, num, ret); } return ret; } EXPORT_SYMBOL(__i2c_transfer); /** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Note that there is no requirement that each message be sent to * the same slave address, although that is the most common model. */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int ret; if (!adap->algo->master_xfer) { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); return -EOPNOTSUPP; } /* REVISIT the fault reporting model here is weak: * * - When we get an error after receiving N bytes from a slave, * there is no way to report "N". * * - When we get a NAK after transmitting N bytes to a slave, * there is no way to report "N" ... or to let the master * continue executing the rest of this combined message, if * that's the appropriate response. * * - When for example "num" is two and we successfully complete * the first message but get an error part way through the * second, it's unclear whether that should be reported as * one (discarding status on the second message) or errno * (discarding status on the first one). */ ret = __i2c_lock_bus_helper(adap); if (ret) return ret; ret = __i2c_transfer(adap, msgs, num); i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; } EXPORT_SYMBOL(i2c_transfer); /** * i2c_transfer_buffer_flags - issue a single I2C message transferring data * to/from a buffer * @client: Handle to slave device * @buf: Where the data is stored * @count: How many bytes to transfer, must be less than 64k since msg.len is u16 * @flags: The flags to be used for the message, e.g. I2C_M_RD for reads * * Returns negative errno, or else the number of bytes transferred. */ int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, int count, u16 flags) { int ret; struct i2c_msg msg = { .addr = client->addr, .flags = flags | (client->flags & I2C_M_TEN), .len = count, .buf = buf, }; ret = i2c_transfer(client->adapter, &msg, 1); /* * If everything went ok (i.e. 1 msg transferred), return #bytes * transferred, else error code. */ return (ret == 1) ? count : ret; } EXPORT_SYMBOL(i2c_transfer_buffer_flags); /** * i2c_get_device_id - get manufacturer, part id and die revision of a device * @client: The device to query * @id: The queried information * * Returns negative errno on error, zero on success. */ int i2c_get_device_id(const struct i2c_client *client, struct i2c_device_identity *id) { struct i2c_adapter *adap = client->adapter; union i2c_smbus_data raw_id; int ret; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) return -EOPNOTSUPP; raw_id.block[0] = 3; ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0, I2C_SMBUS_READ, client->addr << 1, I2C_SMBUS_I2C_BLOCK_DATA, &raw_id); if (ret) return ret; id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4); id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3); id->die_revision = raw_id.block[3] & 0x7; return 0; } EXPORT_SYMBOL_GPL(i2c_get_device_id); /* ---------------------------------------------------- * the i2c address scanning function * Will not work for 10-bit addresses! * ---------------------------------------------------- */ /* * Legacy default probe function, mostly relevant for SMBus. The default * probe method is a quick write, but it is known to corrupt the 24RF08 * EEPROMs due to a state machine bug, and could also irreversibly * write-protect some EEPROMs, so for address ranges 0x30-0x37 and 0x50-0x5f, * we use a short byte read instead. Also, some bus drivers don't implement * quick write, so we fallback to a byte read in that case too. * On x86, there is another special case for FSC hardware monitoring chips, * which want regular byte reads (address 0x73.) Fortunately, these are the * only known chips using this I2C address on PC hardware. * Returns 1 if probe succeeded, 0 if not. */ static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr) { int err; union i2c_smbus_data dummy; #ifdef CONFIG_X86 if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON) && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE_DATA, &dummy); else #endif if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, I2C_SMBUS_QUICK, NULL); else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy); else { dev_warn(&adap->dev, "No suitable probing method supported for address 0x%02X\n", addr); err = -EOPNOTSUPP; } return err >= 0; } static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver) { struct i2c_board_info info; struct i2c_adapter *adapter = temp_client->adapter; int addr = temp_client->addr; int err; /* Make sure the address is valid */ err = i2c_check_7bit_addr_validity_strict(addr); if (err) { dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", addr); return err; } /* Skip if already in use (7 bit, no need to encode flags) */ if (i2c_check_addr_busy(adapter, addr)) return 0; /* Make sure there is something at this address */ if (!i2c_default_probe(adapter, addr)) return 0; /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); info.addr = addr; err = driver->detect(temp_client, &info); if (err) { /* -ENODEV is returned if the detection fails. We catch it here as this isn't an error. */ return err == -ENODEV ? 0 : err; } /* Consistency check */ if (info.type[0] == '\0') { dev_err(&adapter->dev, "%s detection function provided no name for 0x%x\n", driver->driver.name, addr); } else { struct i2c_client *client; /* Detection succeeded, instantiate the device */ if (adapter->class & I2C_CLASS_DEPRECATED) dev_warn(&adapter->dev, "This adapter will soon drop class based instantiation of devices. " "Please make sure client 0x%02x gets instantiated by other means. " "Check 'Documentation/i2c/instantiating-devices.rst' for details.\n", info.addr); dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", info.type, info.addr); client = i2c_new_device(adapter, &info); if (client) list_add_tail(&client->detected, &driver->clients); else dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", info.type, info.addr); } return 0; } static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) { const unsigned short *address_list; struct i2c_client *temp_client; int i, err = 0; int adap_id = i2c_adapter_id(adapter); address_list = driver->address_list; if (!driver->detect || !address_list) return 0; /* Warn that the adapter lost class based instantiation */ if (adapter->class == I2C_CLASS_DEPRECATED) { dev_dbg(&adapter->dev, "This adapter dropped support for I2C classes and won't auto-detect %s devices anymore. " "If you need it, check 'Documentation/i2c/instantiating-devices.rst' for alternatives.\n", driver->driver.name); return 0; } /* Stop here if the classes do not match */ if (!(adapter->class & driver->class)) return 0; /* Set up a temporary client to help detect callback */ temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (!temp_client) return -ENOMEM; temp_client->adapter = adapter; for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { dev_dbg(&adapter->dev, "found normal entry for adapter %d, addr 0x%02x\n", adap_id, address_list[i]); temp_client->addr = address_list[i]; err = i2c_detect_address(temp_client, driver); if (unlikely(err)) break; } kfree(temp_client); return err; } int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) { return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_QUICK, NULL) >= 0; } EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap, struct i2c_board_info *info, unsigned short const *addr_list, int (*probe)(struct i2c_adapter *adap, unsigned short addr)) { int i; if (!probe) probe = i2c_default_probe; for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { /* Check address validity */ if (i2c_check_7bit_addr_validity_strict(addr_list[i]) < 0) { dev_warn(&adap->dev, "Invalid 7-bit address 0x%02x\n", addr_list[i]); continue; } /* Check address availability (7 bit, no need to encode flags) */ if (i2c_check_addr_busy(adap, addr_list[i])) { dev_dbg(&adap->dev, "Address 0x%02x already in use, not probing\n", addr_list[i]); continue; } /* Test address responsiveness */ if (probe(adap, addr_list[i])) break; } if (addr_list[i] == I2C_CLIENT_END) { dev_dbg(&adap->dev, "Probing failed, no device found\n"); return NULL; } info->addr = addr_list[i]; return i2c_new_device(adap, info); } EXPORT_SYMBOL_GPL(i2c_new_probed_device); struct i2c_adapter *i2c_get_adapter(int nr) { struct i2c_adapter *adapter; mutex_lock(&core_lock); adapter = idr_find(&i2c_adapter_idr, nr); if (!adapter) goto exit; if (try_module_get(adapter->owner)) get_device(&adapter->dev); else adapter = NULL; exit: mutex_unlock(&core_lock); return adapter; } EXPORT_SYMBOL(i2c_get_adapter); void i2c_put_adapter(struct i2c_adapter *adap) { if (!adap) return; put_device(&adap->dev); module_put(adap->owner); } EXPORT_SYMBOL(i2c_put_adapter); /** * i2c_get_dma_safe_msg_buf() - get a DMA safe buffer for the given i2c_msg * @msg: the message to be checked * @threshold: the minimum number of bytes for which using DMA makes sense. * Should at least be 1. * * Return: NULL if a DMA safe buffer was not obtained. Use msg->buf with PIO. * Or a valid pointer to be used with DMA. After use, release it by * calling i2c_put_dma_safe_msg_buf(). * * This function must only be called from process context! */ u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold) { /* also skip 0-length msgs for bogus thresholds of 0 */ if (!threshold) pr_debug("DMA buffer for addr=0x%02x with length 0 is bogus\n", msg->addr); if (msg->len < threshold || msg->len == 0) return NULL; if (msg->flags & I2C_M_DMA_SAFE) return msg->buf; pr_debug("using bounce buffer for addr=0x%02x, len=%d\n", msg->addr, msg->len); if (msg->flags & I2C_M_RD) return kzalloc(msg->len, GFP_KERNEL); else return kmemdup(msg->buf, msg->len, GFP_KERNEL); } EXPORT_SYMBOL_GPL(i2c_get_dma_safe_msg_buf); /** * i2c_put_dma_safe_msg_buf - release DMA safe buffer and sync with i2c_msg * @buf: the buffer obtained from i2c_get_dma_safe_msg_buf(). May be NULL. * @msg: the message which the buffer corresponds to * @xferred: bool saying if the message was transferred */ void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred) { if (!buf || buf == msg->buf) return; if (xferred && msg->flags & I2C_M_RD) memcpy(msg->buf, buf, msg->len); kfree(buf); } EXPORT_SYMBOL_GPL(i2c_put_dma_safe_msg_buf); MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus main module"); MODULE_LICENSE("GPL"); ================================================ FILE: t/tree/drivers/i2c/i2c-core-of.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * Linux I2C core OF support code * * Copyright (C) 2008 Jochen Friedrich * based on a previous patch from Jon Smirl * * Copyright (C) 2013, 2018 Wolfram Sang */ #include #include #include #include #include #include #include #include #include "i2c-core.h" int of_i2c_get_board_info(struct device *dev, struct device_node *node, struct i2c_board_info *info) { u32 addr; int ret; memset(info, 0, sizeof(*info)); if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) { dev_err(dev, "of_i2c: modalias failure on %pOF\n", node); return -EINVAL; } ret = of_property_read_u32(node, "reg", &addr); if (ret) { dev_err(dev, "of_i2c: invalid reg on %pOF\n", node); return ret; } if (addr & I2C_TEN_BIT_ADDRESS) { addr &= ~I2C_TEN_BIT_ADDRESS; info->flags |= I2C_CLIENT_TEN; } if (addr & I2C_OWN_SLAVE_ADDRESS) { addr &= ~I2C_OWN_SLAVE_ADDRESS; info->flags |= I2C_CLIENT_SLAVE; } info->addr = addr; info->of_node = node; if (of_property_read_bool(node, "host-notify")) info->flags |= I2C_CLIENT_HOST_NOTIFY; if (of_get_property(node, "wakeup-source", NULL)) info->flags |= I2C_CLIENT_WAKE; return 0; } EXPORT_SYMBOL_GPL(of_i2c_get_board_info); static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, struct device_node *node) { struct i2c_client *client; struct i2c_board_info info; int ret; dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node); ret = of_i2c_get_board_info(&adap->dev, node, &info); if (ret) return ERR_PTR(ret); client = i2c_new_device(adap, &info); if (!client) { dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node); return ERR_PTR(-EINVAL); } return client; } void of_i2c_register_devices(struct i2c_adapter *adap) { struct device_node *bus, *node; struct i2c_client *client; /* Only register child devices if the adapter has a node pointer set */ if (!adap->dev.of_node) return; dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus"); if (!bus) bus = of_node_get(adap->dev.of_node); for_each_available_child_of_node(bus, node) { if (of_node_test_and_set_flag(node, OF_POPULATED)) continue; client = of_i2c_register_device(adap, node); if (IS_ERR(client)) { dev_err(&adap->dev, "Failed to create I2C device for %pOF\n", node); of_node_clear_flag(node, OF_POPULATED); } } of_node_put(bus); } static int of_dev_or_parent_node_match(struct device *dev, const void *data) { if (dev->of_node == data) return 1; if (dev->parent) return dev->parent->of_node == data; return 0; } /* must call put_device() when done with returned i2c_client device */ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) { struct device *dev; struct i2c_client *client; dev = bus_find_device_by_of_node(&i2c_bus_type, node); if (!dev) return NULL; client = i2c_verify_client(dev); if (!client) put_device(dev); return client; } EXPORT_SYMBOL(of_find_i2c_device_by_node); /* must call put_device() when done with returned i2c_adapter device */ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) { struct device *dev; struct i2c_adapter *adapter; dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_or_parent_node_match); if (!dev) return NULL; adapter = i2c_verify_adapter(dev); if (!adapter) put_device(dev); return adapter; } EXPORT_SYMBOL(of_find_i2c_adapter_by_node); /* must call i2c_put_adapter() when done with returned i2c_adapter device */ struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) { struct i2c_adapter *adapter; adapter = of_find_i2c_adapter_by_node(node); if (!adapter) return NULL; if (!try_module_get(adapter->owner)) { put_device(&adapter->dev); adapter = NULL; } return adapter; } EXPORT_SYMBOL(of_get_i2c_adapter_by_node); static const struct of_device_id* i2c_of_match_device_sysfs(const struct of_device_id *matches, struct i2c_client *client) { const char *name; for (; matches->compatible[0]; matches++) { /* * Adding devices through the i2c sysfs interface provides us * a string to match which may be compatible with the device * tree compatible strings, however with no actual of_node the * of_match_device() will not match */ if (sysfs_streq(client->name, matches->compatible)) return matches; name = strchr(matches->compatible, ','); if (!name) name = matches->compatible; else name++; if (sysfs_streq(client->name, name)) return matches; } return NULL; } const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches, struct i2c_client *client) { const struct of_device_id *match; if (!(client && matches)) return NULL; match = of_match_device(matches, &client->dev); if (match) return match; return i2c_of_match_device_sysfs(matches, client); } EXPORT_SYMBOL_GPL(i2c_of_match_device); #if IS_ENABLED(CONFIG_OF_DYNAMIC) static int of_i2c_notify(struct notifier_block *nb, unsigned long action, void *arg) { struct of_reconfig_data *rd = arg; struct i2c_adapter *adap; struct i2c_client *client; switch (of_reconfig_get_state_change(action, rd)) { case OF_RECONFIG_CHANGE_ADD: adap = of_find_i2c_adapter_by_node(rd->dn->parent); if (adap == NULL) return NOTIFY_OK; /* not for us */ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) { put_device(&adap->dev); return NOTIFY_OK; } client = of_i2c_register_device(adap, rd->dn); if (IS_ERR(client)) { dev_err(&adap->dev, "failed to create client for '%pOF'\n", rd->dn); put_device(&adap->dev); of_node_clear_flag(rd->dn, OF_POPULATED); return notifier_from_errno(PTR_ERR(client)); } put_device(&adap->dev); break; case OF_RECONFIG_CHANGE_REMOVE: /* already depopulated? */ if (!of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; /* find our device by node */ client = of_find_i2c_device_by_node(rd->dn); if (client == NULL) return NOTIFY_OK; /* no? not meant for us */ /* unregister takes one ref away */ i2c_unregister_device(client); /* and put the reference of the find */ put_device(&client->dev); break; } return NOTIFY_OK; } struct notifier_block i2c_of_notifier = { .notifier_call = of_i2c_notify, }; #endif /* CONFIG_OF_DYNAMIC */ ================================================ FILE: t/tree/drivers/i2c/i2c-core-slave.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * Linux I2C core slave support code * * Copyright (C) 2014 by Wolfram Sang */ #include #include #include #include #include #include #include "i2c-core.h" int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) { int ret; if (!client || !slave_cb) { WARN(1, "insufficient data\n"); return -EINVAL; } if (!(client->flags & I2C_CLIENT_SLAVE)) dev_warn(&client->dev, "%s: client slave flag not set. You might see address collisions\n", __func__); if (!(client->flags & I2C_CLIENT_TEN)) { /* Enforce stricter address checking */ ret = i2c_check_7bit_addr_validity_strict(client->addr); if (ret) { dev_err(&client->dev, "%s: invalid address\n", __func__); return ret; } } if (!client->adapter->algo->reg_slave) { dev_err(&client->dev, "%s: not supported by adapter\n", __func__); return -EOPNOTSUPP; } client->slave_cb = slave_cb; i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); ret = client->adapter->algo->reg_slave(client); i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); if (ret) { client->slave_cb = NULL; dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); } return ret; } EXPORT_SYMBOL_GPL(i2c_slave_register); int i2c_slave_unregister(struct i2c_client *client) { int ret; if (!client->adapter->algo->unreg_slave) { dev_err(&client->dev, "%s: not supported by adapter\n", __func__); return -EOPNOTSUPP; } i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); ret = client->adapter->algo->unreg_slave(client); i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); if (ret == 0) client->slave_cb = NULL; else dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); return ret; } EXPORT_SYMBOL_GPL(i2c_slave_unregister); /** * i2c_detect_slave_mode - detect operation mode * @dev: The device owning the bus * * This checks the device nodes for an I2C slave by checking the address * used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS * flag this means the device is configured to act as a I2C slave and it will * be listening at that address. * * Returns true if an I2C own slave address is detected, otherwise returns * false. */ bool i2c_detect_slave_mode(struct device *dev) { if (IS_BUILTIN(CONFIG_OF) && dev->of_node) { struct device_node *child; u32 reg; for_each_child_of_node(dev->of_node, child) { of_property_read_u32(child, "reg", ®); if (reg & I2C_OWN_SLAVE_ADDRESS) { of_node_put(child); return true; } } } else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) { dev_dbg(dev, "ACPI slave is not supported yet\n"); } return false; } EXPORT_SYMBOL_GPL(i2c_detect_slave_mode); ================================================ FILE: t/tree/drivers/i2c/i2c-core-smbus.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * Linux I2C core SMBus and SMBus emulation code * * This file contains the SMBus functions which are always included in the I2C * core because they can be emulated via I2C. SMBus specific extensions * (e.g. smbalert) are handled in a seperate i2c-smbus module. * * All SMBus-related things are written by Frodo Looijaard * SMBus 2.0 support by Mark Studebaker and * Jean Delvare */ #include #include #include #include #include #include "i2c-core.h" #define CREATE_TRACE_POINTS #include /* The SMBus parts */ #define POLY (0x1070U << 3) static u8 crc8(u16 data) { int i; for (i = 0; i < 8; i++) { if (data & 0x8000) data = data ^ POLY; data = data << 1; } return (u8)(data >> 8); } /* Incremental CRC8 over count bytes in the array pointed to by p */ static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) { int i; for (i = 0; i < count; i++) crc = crc8((crc ^ p[i]) << 8); return crc; } /* Assume a 7-bit address, which is reasonable for SMBus */ static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) { /* The address will be sent first */ u8 addr = i2c_8bit_addr_from_msg(msg); pec = i2c_smbus_pec(pec, &addr, 1); /* The data buffer follows */ return i2c_smbus_pec(pec, msg->buf, msg->len); } /* Used for write only transactions */ static inline void i2c_smbus_add_pec(struct i2c_msg *msg) { msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); msg->len++; } /* Return <0 on CRC error If there was a write before this read (most cases) we need to take the partial CRC from the write part into account. Note that this function does modify the message (we need to decrease the message length to hide the CRC byte from the caller). */ static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) { u8 rpec = msg->buf[--msg->len]; cpec = i2c_smbus_msg_pec(cpec, msg); if (rpec != cpec) { pr_debug("Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); return -EBADMSG; } return 0; } /** * i2c_smbus_read_byte - SMBus "receive byte" protocol * @client: Handle to slave device * * This executes the SMBus "receive byte" protocol, returning negative errno * else the byte received from the device. */ s32 i2c_smbus_read_byte(const struct i2c_client *client) { union i2c_smbus_data data; int status; status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data); return (status < 0) ? status : data.byte; } EXPORT_SYMBOL(i2c_smbus_read_byte); /** * i2c_smbus_write_byte - SMBus "send byte" protocol * @client: Handle to slave device * @value: Byte to be sent * * This executes the SMBus "send byte" protocol, returning negative errno * else zero on success. */ s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) { return i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); } EXPORT_SYMBOL(i2c_smbus_write_byte); /** * i2c_smbus_read_byte_data - SMBus "read byte" protocol * @client: Handle to slave device * @command: Byte interpreted by slave * * This executes the SMBus "read byte" protocol, returning negative errno * else a data byte received from the device. */ s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) { union i2c_smbus_data data; int status; status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, command, I2C_SMBUS_BYTE_DATA, &data); return (status < 0) ? status : data.byte; } EXPORT_SYMBOL(i2c_smbus_read_byte_data); /** * i2c_smbus_write_byte_data - SMBus "write byte" protocol * @client: Handle to slave device * @command: Byte interpreted by slave * @value: Byte being written * * This executes the SMBus "write byte" protocol, returning negative errno * else zero on success. */ s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value) { union i2c_smbus_data data; data.byte = value; return i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, command, I2C_SMBUS_BYTE_DATA, &data); } EXPORT_SYMBOL(i2c_smbus_write_byte_data); /** * i2c_smbus_read_word_data - SMBus "read word" protocol * @client: Handle to slave device * @command: Byte interpreted by slave * * This executes the SMBus "read word" protocol, returning negative errno * else a 16-bit unsigned "word" received from the device. */ s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) { union i2c_smbus_data data; int status; status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, command, I2C_SMBUS_WORD_DATA, &data); return (status < 0) ? status : data.word; } EXPORT_SYMBOL(i2c_smbus_read_word_data); /** * i2c_smbus_write_word_data - SMBus "write word" protocol * @client: Handle to slave device * @command: Byte interpreted by slave * @value: 16-bit "word" being written * * This executes the SMBus "write word" protocol, returning negative errno * else zero on success. */ s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value) { union i2c_smbus_data data; data.word = value; return i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, command, I2C_SMBUS_WORD_DATA, &data); } EXPORT_SYMBOL(i2c_smbus_write_word_data); /** * i2c_smbus_read_block_data - SMBus "block read" protocol * @client: Handle to slave device * @command: Byte interpreted by slave * @values: Byte array into which data will be read; big enough to hold * the data returned by the slave. SMBus allows at most 32 bytes. * * This executes the SMBus "block read" protocol, returning negative errno * else the number of data bytes in the slave's response. * * Note that using this function requires that the client's adapter support * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers * support this; its emulation through I2C messaging relies on a specific * mechanism (I2C_M_RECV_LEN) which may not be implemented. */ s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values) { union i2c_smbus_data data; int status; status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, command, I2C_SMBUS_BLOCK_DATA, &data); if (status) return status; memcpy(values, &data.block[1], data.block[0]); return data.block[0]; } EXPORT_SYMBOL(i2c_smbus_read_block_data); /** * i2c_smbus_write_block_data - SMBus "block write" protocol * @client: Handle to slave device * @command: Byte interpreted by slave * @length: Size of data block; SMBus allows at most 32 bytes * @values: Byte array which will be written. * * This executes the SMBus "block write" protocol, returning negative errno * else zero on success. */ s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values) { union i2c_smbus_data data; if (length > I2C_SMBUS_BLOCK_MAX) length = I2C_SMBUS_BLOCK_MAX; data.block[0] = length; memcpy(&data.block[1], values, length); return i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, command, I2C_SMBUS_BLOCK_DATA, &data); } EXPORT_SYMBOL(i2c_smbus_write_block_data); /* Returns the number of read bytes */ s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values) { union i2c_smbus_data data; int status; if (length > I2C_SMBUS_BLOCK_MAX) length = I2C_SMBUS_BLOCK_MAX; data.block[0] = length; status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, command, I2C_SMBUS_I2C_BLOCK_DATA, &data); if (status < 0) return status; memcpy(values, &data.block[1], data.block[0]); return data.block[0]; } EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values) { union i2c_smbus_data data; if (length > I2C_SMBUS_BLOCK_MAX) length = I2C_SMBUS_BLOCK_MAX; data.block[0] = length; memcpy(data.block + 1, values, length); return i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, command, I2C_SMBUS_I2C_BLOCK_DATA, &data); } EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val) { bool is_read = msg->flags & I2C_M_RD; unsigned char *dma_buf; dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL); if (!dma_buf) return; msg->buf = dma_buf; msg->flags |= I2C_M_DMA_SAFE; if (init_val) msg->buf[0] = init_val; } /* * Simulate a SMBus command using the I2C protocol. * No checking of parameters is done! */ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { /* * So we need to generate a series of msgs. In the case of writing, we * need to use only one message; when reading, we need two. We * initialize most things with sane defaults, to keep the code below * somewhat simpler. */ unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; int num = read_write == I2C_SMBUS_READ ? 2 : 1; int i; u8 partial_pec = 0; int status; struct i2c_msg msg[2] = { { .addr = addr, .flags = flags, .len = 1, .buf = msgbuf0, }, { .addr = addr, .flags = flags | I2C_M_RD, .len = 0, .buf = msgbuf1, }, }; msgbuf0[0] = command; switch (size) { case I2C_SMBUS_QUICK: msg[0].len = 0; /* Special case: The read/write field is used as data */ msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? I2C_M_RD : 0); num = 1; break; case I2C_SMBUS_BYTE: if (read_write == I2C_SMBUS_READ) { /* Special case: only a read! */ msg[0].flags = I2C_M_RD | flags; num = 1; } break; case I2C_SMBUS_BYTE_DATA: if (read_write == I2C_SMBUS_READ) msg[1].len = 1; else { msg[0].len = 2; msgbuf0[1] = data->byte; } break; case I2C_SMBUS_WORD_DATA: if (read_write == I2C_SMBUS_READ) msg[1].len = 2; else { msg[0].len = 3; msgbuf0[1] = data->word & 0xff; msgbuf0[2] = data->word >> 8; } break; case I2C_SMBUS_PROC_CALL: num = 2; /* Special case */ read_write = I2C_SMBUS_READ; msg[0].len = 3; msg[1].len = 2; msgbuf0[1] = data->word & 0xff; msgbuf0[2] = data->word >> 8; break; case I2C_SMBUS_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { msg[1].flags |= I2C_M_RECV_LEN; msg[1].len = 1; /* block length will be added by the underlying bus driver */ i2c_smbus_try_get_dmabuf(&msg[1], 0); } else { msg[0].len = data->block[0] + 2; if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { dev_err(&adapter->dev, "Invalid block write size %d\n", data->block[0]); return -EINVAL; } i2c_smbus_try_get_dmabuf(&msg[0], command); for (i = 1; i < msg[0].len; i++) msg[0].buf[i] = data->block[i - 1]; } break; case I2C_SMBUS_BLOCK_PROC_CALL: num = 2; /* Another special case */ read_write = I2C_SMBUS_READ; if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { dev_err(&adapter->dev, "Invalid block write size %d\n", data->block[0]); return -EINVAL; } msg[0].len = data->block[0] + 2; i2c_smbus_try_get_dmabuf(&msg[0], command); for (i = 1; i < msg[0].len; i++) msg[0].buf[i] = data->block[i - 1]; msg[1].flags |= I2C_M_RECV_LEN; msg[1].len = 1; /* block length will be added by the underlying bus driver */ i2c_smbus_try_get_dmabuf(&msg[1], 0); break; case I2C_SMBUS_I2C_BLOCK_DATA: if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { dev_err(&adapter->dev, "Invalid block %s size %d\n", read_write == I2C_SMBUS_READ ? "read" : "write", data->block[0]); return -EINVAL; } if (read_write == I2C_SMBUS_READ) { msg[1].len = data->block[0]; i2c_smbus_try_get_dmabuf(&msg[1], 0); } else { msg[0].len = data->block[0] + 1; i2c_smbus_try_get_dmabuf(&msg[0], command); for (i = 1; i <= data->block[0]; i++) msg[0].buf[i] = data->block[i]; } break; default: dev_err(&adapter->dev, "Unsupported transaction %d\n", size); return -EOPNOTSUPP; } i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA); if (i) { /* Compute PEC if first message is a write */ if (!(msg[0].flags & I2C_M_RD)) { if (num == 1) /* Write only */ i2c_smbus_add_pec(&msg[0]); else /* Write followed by read */ partial_pec = i2c_smbus_msg_pec(0, &msg[0]); } /* Ask for PEC if last message is a read */ if (msg[num-1].flags & I2C_M_RD) msg[num-1].len++; } status = __i2c_transfer(adapter, msg, num); if (status < 0) goto cleanup; if (status != num) { status = -EIO; goto cleanup; } status = 0; /* Check PEC if last message is a read */ if (i && (msg[num-1].flags & I2C_M_RD)) { status = i2c_smbus_check_pec(partial_pec, &msg[num-1]); if (status < 0) goto cleanup; } if (read_write == I2C_SMBUS_READ) switch (size) { case I2C_SMBUS_BYTE: data->byte = msgbuf0[0]; break; case I2C_SMBUS_BYTE_DATA: data->byte = msgbuf1[0]; break; case I2C_SMBUS_WORD_DATA: case I2C_SMBUS_PROC_CALL: data->word = msgbuf1[0] | (msgbuf1[1] << 8); break; case I2C_SMBUS_I2C_BLOCK_DATA: for (i = 0; i < data->block[0]; i++) data->block[i + 1] = msg[1].buf[i]; break; case I2C_SMBUS_BLOCK_DATA: case I2C_SMBUS_BLOCK_PROC_CALL: for (i = 0; i < msg[1].buf[0] + 1; i++) data->block[i] = msg[1].buf[i]; break; } cleanup: if (msg[0].flags & I2C_M_DMA_SAFE) kfree(msg[0].buf); if (msg[1].flags & I2C_M_DMA_SAFE) kfree(msg[1].buf); return status; } /** * i2c_smbus_xfer - execute SMBus protocol operations * @adapter: Handle to I2C bus * @addr: Address of SMBus slave on that bus * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE * @command: Byte interpreted by slave, for protocols which use such bytes * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL * @data: Data to be read or written * * This executes an SMBus protocol operation, and returns a negative * errno code else zero on success. */ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data) { s32 res; res = __i2c_lock_bus_helper(adapter); if (res) return res; res = __i2c_smbus_xfer(adapter, addr, flags, read_write, command, protocol, data); i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); return res; } EXPORT_SYMBOL(i2c_smbus_xfer); s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data) { int (*xfer_func)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); unsigned long orig_jiffies; int try; s32 res; res = __i2c_check_suspended(adapter); if (res) return res; /* If enabled, the following two tracepoints are conditional on * read_write and protocol. */ trace_smbus_write(adapter, addr, flags, read_write, command, protocol, data); trace_smbus_read(adapter, addr, flags, read_write, command, protocol); flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; xfer_func = adapter->algo->smbus_xfer; if (i2c_in_atomic_xfer_mode()) { if (adapter->algo->smbus_xfer_atomic) xfer_func = adapter->algo->smbus_xfer_atomic; else if (adapter->algo->master_xfer_atomic) xfer_func = NULL; /* fallback to I2C emulation */ } if (xfer_func) { /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (res = 0, try = 0; try <= adapter->retries; try++) { res = xfer_func(adapter, addr, flags, read_write, command, protocol, data); if (res != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adapter->timeout)) break; } if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) goto trace; /* * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't * implement native support for the SMBus operation. */ } res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data); trace: /* If enabled, the reply tracepoint is conditional on read_write. */ trace_smbus_reply(adapter, addr, flags, read_write, command, protocol, data, res); trace_smbus_result(adapter, addr, flags, read_write, command, protocol, res); return res; } EXPORT_SYMBOL(__i2c_smbus_xfer); /** * i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate * @client: Handle to slave device * @command: Byte interpreted by slave * @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes * @values: Byte array into which data will be read; big enough to hold * the data returned by the slave. SMBus allows at most * I2C_SMBUS_BLOCK_MAX bytes. * * This executes the SMBus "block read" protocol if supported by the adapter. * If block read is not supported, it emulates it using either word or byte * read protocols depending on availability. * * The addresses of the I2C slave device that are accessed with this function * must be mapped to a linear region, so that a block read will have the same * effect as a byte read. Before using this function you must double-check * if the I2C slave does support exchanging a block transfer with a byte * transfer. */ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, u8 command, u8 length, u8 *values) { u8 i = 0; int status; if (length > I2C_SMBUS_BLOCK_MAX) length = I2C_SMBUS_BLOCK_MAX; if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) return i2c_smbus_read_i2c_block_data(client, command, length, values); if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) return -EOPNOTSUPP; if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) { while ((i + 2) <= length) { status = i2c_smbus_read_word_data(client, command + i); if (status < 0) return status; values[i] = status & 0xff; values[i + 1] = status >> 8; i += 2; } } while (i < length) { status = i2c_smbus_read_byte_data(client, command + i); if (status < 0) return status; values[i] = status; i++; } return i; } EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); /** * i2c_setup_smbus_alert - Setup SMBus alert support * @adapter: the target adapter * @setup: setup data for the SMBus alert handler * Context: can sleep * * Setup handling of the SMBus alert protocol on a given I2C bus segment. * * Handling can be done either through our IRQ handler, or by the * adapter (from its handler, periodic polling, or whatever). * * NOTE that if we manage the IRQ, we *MUST* know if it's level or * edge triggered in order to hand it to the workqueue correctly. * If triggering the alert seems to wedge the system, you probably * should have said it's level triggered. * * This returns the ara client, which should be saved for later use with * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL * to indicate an error. */ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup) { struct i2c_board_info ara_board_info = { I2C_BOARD_INFO("smbus_alert", 0x0c), .platform_data = setup, }; return i2c_new_device(adapter, &ara_board_info); } EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); #if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) { struct i2c_client *client; int irq; irq = of_property_match_string(adapter->dev.of_node, "interrupt-names", "smbus_alert"); if (irq == -EINVAL || irq == -ENODATA) return 0; else if (irq < 0) return irq; client = i2c_setup_smbus_alert(adapter, NULL); if (!client) return -ENODEV; return 0; } EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert); #endif ================================================ FILE: t/tree/drivers/i2c/i2c-core.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * i2c-core.h - interfaces internal to the I2C framework */ #include struct i2c_devinfo { struct list_head list; int busnum; struct i2c_board_info board_info; }; /* board_lock protects board_list and first_dynamic_bus_num. * only i2c core components are allowed to use these symbols. */ extern struct rw_semaphore __i2c_board_lock; extern struct list_head __i2c_board_list; extern int __i2c_first_dynamic_bus_num; int i2c_check_7bit_addr_validity_strict(unsigned short addr); int i2c_dev_irq_from_resources(const struct resource *resources, unsigned int num_resources); /* * We only allow atomic transfers for very late communication, e.g. to send * the powerdown command to a PMIC. Atomic transfers are a corner case and not * for generic use! */ static inline bool i2c_in_atomic_xfer_mode(void) { return system_state > SYSTEM_RUNNING && irqs_disabled(); } static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap) { int ret = 0; if (i2c_in_atomic_xfer_mode()) { WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic, "No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev)); ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN; } else { i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } return ret; } static inline int __i2c_check_suspended(struct i2c_adapter *adap) { if (test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)) { if (!test_and_set_bit(I2C_ALF_SUSPEND_REPORTED, &adap->locked_flags)) dev_WARN(&adap->dev, "Transfer while suspended\n"); return -ESHUTDOWN; } return 0; } #ifdef CONFIG_ACPI const struct acpi_device_id * i2c_acpi_match_device(const struct acpi_device_id *matches, struct i2c_client *client); void i2c_acpi_register_devices(struct i2c_adapter *adap); int i2c_acpi_get_irq(struct i2c_client *client); #else /* CONFIG_ACPI */ static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } static inline const struct acpi_device_id * i2c_acpi_match_device(const struct acpi_device_id *matches, struct i2c_client *client) { return NULL; } static inline int i2c_acpi_get_irq(struct i2c_client *client) { return 0; } #endif /* CONFIG_ACPI */ extern struct notifier_block i2c_acpi_notifier; #ifdef CONFIG_ACPI_I2C_OPREGION int i2c_acpi_install_space_handler(struct i2c_adapter *adapter); void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter); #else /* CONFIG_ACPI_I2C_OPREGION */ static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { return 0; } static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { } #endif /* CONFIG_ACPI_I2C_OPREGION */ #ifdef CONFIG_OF void of_i2c_register_devices(struct i2c_adapter *adap); #else static inline void of_i2c_register_devices(struct i2c_adapter *adap) { } #endif extern struct notifier_block i2c_of_notifier; ================================================ FILE: t/tree/drivers/i2c/i2c-dev.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* i2c-dev.c - i2c-bus driver, char device interface Copyright (C) 1995-97 Simon G. Vogl Copyright (C) 1998-99 Frodo Looijaard Copyright (C) 2003 Greg Kroah-Hartman */ /* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. But I have used so much of his original code and ideas that it seems only fair to recognize him as co-author -- Frodo */ /* The I2C_RDWR ioctl code is written by Kolja Waschk */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a * slave (i2c_client) with which messages will be exchanged. It's coupled * with a character special file which is accessed by user mode drivers. * * The list of i2c_dev structures is parallel to the i2c_adapter lists * maintained by the driver model, and is updated using bus notifications. */ struct i2c_dev { struct list_head list; struct i2c_adapter *adap; struct device *dev; struct cdev cdev; }; #define I2C_MINORS (MINORMASK + 1) static LIST_HEAD(i2c_dev_list); static DEFINE_SPINLOCK(i2c_dev_list_lock); static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) { struct i2c_dev *i2c_dev; spin_lock(&i2c_dev_list_lock); list_for_each_entry(i2c_dev, &i2c_dev_list, list) { if (i2c_dev->adap->nr == index) goto found; } i2c_dev = NULL; found: spin_unlock(&i2c_dev_list_lock); return i2c_dev; } static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; if (adap->nr >= I2C_MINORS) { printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n", adap->nr); return ERR_PTR(-ENODEV); } i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) return ERR_PTR(-ENOMEM); i2c_dev->adap = adap; spin_lock(&i2c_dev_list_lock); list_add_tail(&i2c_dev->list, &i2c_dev_list); spin_unlock(&i2c_dev_list_lock); return i2c_dev; } static void put_i2c_dev(struct i2c_dev *i2c_dev) { spin_lock(&i2c_dev_list_lock); list_del(&i2c_dev->list); spin_unlock(&i2c_dev_list_lock); kfree(i2c_dev); } static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt)); if (!i2c_dev) return -ENODEV; return sprintf(buf, "%s\n", i2c_dev->adap->name); } static DEVICE_ATTR_RO(name); static struct attribute *i2c_attrs[] = { &dev_attr_name.attr, NULL, }; ATTRIBUTE_GROUPS(i2c); /* ------------------------------------------------------------------------- */ /* * After opening an instance of this character special file, a file * descriptor starts out associated only with an i2c_adapter (and bus). * * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg * traffic to any devices on the bus used by that adapter. That's because * the i2c_msg vectors embed all the addressing information they need, and * are submitted directly to an i2c_adapter. However, SMBus-only adapters * don't support that interface. * * To use read()/write() system calls on that file descriptor, or to use * SMBus interfaces (and work with SMBus-only hosts!), you must first issue * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous * (never registered) i2c_client so it holds the addressing information * needed by those system calls and by this SMBus interface. */ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { char *tmp; int ret; struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; tmp = kmalloc(count, GFP_KERNEL); if (tmp == NULL) return -ENOMEM; pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count); ret = i2c_master_recv(client, tmp, count); if (ret >= 0) ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; kfree(tmp); return ret; } static ssize_t i2cdev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { int ret; char *tmp; struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; tmp = memdup_user(buf, count); if (IS_ERR(tmp)) return PTR_ERR(tmp); pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count); ret = i2c_master_send(client, tmp, count); kfree(tmp); return ret; } static int i2cdev_check(struct device *dev, void *addrp) { struct i2c_client *client = i2c_verify_client(dev); if (!client || client->addr != *(unsigned int *)addrp) return 0; return dev->driver ? -EBUSY : 0; } /* walk up mux tree */ static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result; result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); if (!result && parent) result = i2cdev_check_mux_parents(parent, addr); return result; } /* recurse down mux tree */ static int i2cdev_check_mux_children(struct device *dev, void *addrp) { int result; if (dev->type == &i2c_adapter_type) result = device_for_each_child(dev, addrp, i2cdev_check_mux_children); else result = i2cdev_check(dev, addrp); return result; } /* This address checking function differs from the one in i2c-core in that it considers an address with a registered device, but no driver bound to it, as NOT busy. */ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result = 0; if (parent) result = i2cdev_check_mux_parents(parent, addr); if (!result) result = device_for_each_child(&adapter->dev, &addr, i2cdev_check_mux_children); return result; } static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, unsigned nmsgs, struct i2c_msg *msgs) { u8 __user **data_ptrs; int i, res; data_ptrs = kmalloc_array(nmsgs, sizeof(u8 __user *), GFP_KERNEL); if (data_ptrs == NULL) { kfree(msgs); return -ENOMEM; } res = 0; for (i = 0; i < nmsgs; i++) { /* Limit the size of the message to a sane amount */ if (msgs[i].len > 8192) { res = -EINVAL; break; } data_ptrs[i] = (u8 __user *)msgs[i].buf; msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len); if (IS_ERR(msgs[i].buf)) { res = PTR_ERR(msgs[i].buf); break; } /* memdup_user allocates with GFP_KERNEL, so DMA is ok */ msgs[i].flags |= I2C_M_DMA_SAFE; /* * If the message length is received from the slave (similar * to SMBus block read), we must ensure that the buffer will * be large enough to cope with a message length of * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus * drivers allow. The first byte in the buffer must be * pre-filled with the number of extra bytes, which must be * at least one to hold the message length, but can be * greater (for example to account for a checksum byte at * the end of the message.) */ if (msgs[i].flags & I2C_M_RECV_LEN) { if (!(msgs[i].flags & I2C_M_RD) || msgs[i].len < 1 || msgs[i].buf[0] < 1 || msgs[i].len < msgs[i].buf[0] + I2C_SMBUS_BLOCK_MAX) { i++; res = -EINVAL; break; } msgs[i].len = msgs[i].buf[0]; } } if (res < 0) { int j; for (j = 0; j < i; ++j) kfree(msgs[j].buf); kfree(data_ptrs); kfree(msgs); return res; } res = i2c_transfer(client->adapter, msgs, nmsgs); while (i-- > 0) { if (res >= 0 && (msgs[i].flags & I2C_M_RD)) { if (copy_to_user(data_ptrs[i], msgs[i].buf, msgs[i].len)) res = -EFAULT; } kfree(msgs[i].buf); } kfree(data_ptrs); kfree(msgs); return res; } static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, u8 read_write, u8 command, u32 size, union i2c_smbus_data __user *data) { union i2c_smbus_data temp = {}; int datasize, res; if ((size != I2C_SMBUS_BYTE) && (size != I2C_SMBUS_QUICK) && (size != I2C_SMBUS_BYTE_DATA) && (size != I2C_SMBUS_WORD_DATA) && (size != I2C_SMBUS_PROC_CALL) && (size != I2C_SMBUS_BLOCK_DATA) && (size != I2C_SMBUS_I2C_BLOCK_BROKEN) && (size != I2C_SMBUS_I2C_BLOCK_DATA) && (size != I2C_SMBUS_BLOCK_PROC_CALL)) { dev_dbg(&client->adapter->dev, "size out of range (%x) in ioctl I2C_SMBUS.\n", size); return -EINVAL; } /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, so the check is valid if size==I2C_SMBUS_QUICK too. */ if ((read_write != I2C_SMBUS_READ) && (read_write != I2C_SMBUS_WRITE)) { dev_dbg(&client->adapter->dev, "read_write out of range (%x) in ioctl I2C_SMBUS.\n", read_write); return -EINVAL; } /* Note that command values are always valid! */ if ((size == I2C_SMBUS_QUICK) || ((size == I2C_SMBUS_BYTE) && (read_write == I2C_SMBUS_WRITE))) /* These are special: we do not use data */ return i2c_smbus_xfer(client->adapter, client->addr, client->flags, read_write, command, size, NULL); if (data == NULL) { dev_dbg(&client->adapter->dev, "data is NULL pointer in ioctl I2C_SMBUS.\n"); return -EINVAL; } if ((size == I2C_SMBUS_BYTE_DATA) || (size == I2C_SMBUS_BYTE)) datasize = sizeof(data->byte); else if ((size == I2C_SMBUS_WORD_DATA) || (size == I2C_SMBUS_PROC_CALL)) datasize = sizeof(data->word); else /* size == smbus block, i2c block, or block proc. call */ datasize = sizeof(data->block); if ((size == I2C_SMBUS_PROC_CALL) || (size == I2C_SMBUS_BLOCK_PROC_CALL) || (size == I2C_SMBUS_I2C_BLOCK_DATA) || (read_write == I2C_SMBUS_WRITE)) { if (copy_from_user(&temp, data, datasize)) return -EFAULT; } if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) { /* Convert old I2C block commands to the new convention. This preserves binary compatibility. */ size = I2C_SMBUS_I2C_BLOCK_DATA; if (read_write == I2C_SMBUS_READ) temp.block[0] = I2C_SMBUS_BLOCK_MAX; } res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, read_write, command, size, &temp); if (!res && ((size == I2C_SMBUS_PROC_CALL) || (size == I2C_SMBUS_BLOCK_PROC_CALL) || (read_write == I2C_SMBUS_READ))) { if (copy_to_user(data, &temp, datasize)) return -EFAULT; } return res; } static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct i2c_client *client = file->private_data; unsigned long funcs; dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg); switch (cmd) { case I2C_SLAVE: case I2C_SLAVE_FORCE: if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) return -EINVAL; if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) return -EBUSY; /* REVISIT: address could become busy later */ client->addr = arg; return 0; case I2C_TENBIT: if (arg) client->flags |= I2C_M_TEN; else client->flags &= ~I2C_M_TEN; return 0; case I2C_PEC: /* * Setting the PEC flag here won't affect kernel drivers, * which will be using the i2c_client node registered with * the driver model core. Likewise, when that client has * the PEC flag already set, the i2c-dev driver won't see * (or use) this setting. */ if (arg) client->flags |= I2C_CLIENT_PEC; else client->flags &= ~I2C_CLIENT_PEC; return 0; case I2C_FUNCS: funcs = i2c_get_functionality(client->adapter); return put_user(funcs, (unsigned long __user *)arg); case I2C_RDWR: { struct i2c_rdwr_ioctl_data rdwr_arg; struct i2c_msg *rdwr_pa; if (copy_from_user(&rdwr_arg, (struct i2c_rdwr_ioctl_data __user *)arg, sizeof(rdwr_arg))) return -EFAULT; /* Put an arbitrary limit on the number of messages that can * be sent at once */ if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) return -EINVAL; rdwr_pa = memdup_user(rdwr_arg.msgs, rdwr_arg.nmsgs * sizeof(struct i2c_msg)); if (IS_ERR(rdwr_pa)) return PTR_ERR(rdwr_pa); return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); } case I2C_SMBUS: { struct i2c_smbus_ioctl_data data_arg; if (copy_from_user(&data_arg, (struct i2c_smbus_ioctl_data __user *) arg, sizeof(struct i2c_smbus_ioctl_data))) return -EFAULT; return i2cdev_ioctl_smbus(client, data_arg.read_write, data_arg.command, data_arg.size, data_arg.data); } case I2C_RETRIES: if (arg > INT_MAX) return -EINVAL; client->adapter->retries = arg; break; case I2C_TIMEOUT: if (arg > INT_MAX) return -EINVAL; /* For historical reasons, user-space sets the timeout * value in units of 10 ms. */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: /* NOTE: returning a fault code here could cause trouble * in buggy userspace code. Some old kernel bugs returned * zero in this case, and userspace code might accidentally * have depended on that bug. */ return -ENOTTY; } return 0; } #ifdef CONFIG_COMPAT struct i2c_smbus_ioctl_data32 { u8 read_write; u8 command; u32 size; compat_caddr_t data; /* union i2c_smbus_data *data */ }; struct i2c_msg32 { u16 addr; u16 flags; u16 len; compat_caddr_t buf; }; struct i2c_rdwr_ioctl_data32 { compat_caddr_t msgs; /* struct i2c_msg __user *msgs */ u32 nmsgs; }; static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct i2c_client *client = file->private_data; unsigned long funcs; switch (cmd) { case I2C_FUNCS: funcs = i2c_get_functionality(client->adapter); return put_user(funcs, (compat_ulong_t __user *)arg); case I2C_RDWR: { struct i2c_rdwr_ioctl_data32 rdwr_arg; struct i2c_msg32 *p; struct i2c_msg *rdwr_pa; int i; if (copy_from_user(&rdwr_arg, (struct i2c_rdwr_ioctl_data32 __user *)arg, sizeof(rdwr_arg))) return -EFAULT; if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) return -EINVAL; rdwr_pa = kmalloc_array(rdwr_arg.nmsgs, sizeof(struct i2c_msg), GFP_KERNEL); if (!rdwr_pa) return -ENOMEM; p = compat_ptr(rdwr_arg.msgs); for (i = 0; i < rdwr_arg.nmsgs; i++) { struct i2c_msg32 umsg; if (copy_from_user(&umsg, p + i, sizeof(umsg))) { kfree(rdwr_pa); return -EFAULT; } rdwr_pa[i] = (struct i2c_msg) { .addr = umsg.addr, .flags = umsg.flags, .len = umsg.len, .buf = compat_ptr(umsg.buf) }; } return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); } case I2C_SMBUS: { struct i2c_smbus_ioctl_data32 data32; if (copy_from_user(&data32, (void __user *) arg, sizeof(data32))) return -EFAULT; return i2cdev_ioctl_smbus(client, data32.read_write, data32.command, data32.size, compat_ptr(data32.data)); } default: return i2cdev_ioctl(file, cmd, arg); } } #else #define compat_i2cdev_ioctl NULL #endif static int i2cdev_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct i2c_client *client; struct i2c_adapter *adap; adap = i2c_get_adapter(minor); if (!adap) return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. * * This client is ** NEVER REGISTERED ** with the driver model * or I2C core code!! It just holds private copies of addressing * information and maybe a PEC flag. */ client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { i2c_put_adapter(adap); return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->adapter = adap; file->private_data = client; return 0; } static int i2cdev_release(struct inode *inode, struct file *file) { struct i2c_client *client = file->private_data; i2c_put_adapter(client->adapter); kfree(client); file->private_data = NULL; return 0; } static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, .compat_ioctl = compat_i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, }; /* ------------------------------------------------------------------------- */ static struct class *i2c_dev_class; static int i2cdev_attach_adapter(struct device *dev, void *dummy) { struct i2c_adapter *adap; struct i2c_dev *i2c_dev; int res; if (dev->type != &i2c_adapter_type) return 0; adap = to_i2c_adapter(dev); i2c_dev = get_free_i2c_dev(adap); if (IS_ERR(i2c_dev)) return PTR_ERR(i2c_dev); cdev_init(&i2c_dev->cdev, &i2cdev_fops); i2c_dev->cdev.owner = THIS_MODULE; res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1); if (res) goto error_cdev; /* register this i2c device with the driver core */ i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, MKDEV(I2C_MAJOR, adap->nr), NULL, "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { res = PTR_ERR(i2c_dev->dev); goto error; } pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", adap->name, adap->nr); return 0; error: cdev_del(&i2c_dev->cdev); error_cdev: put_i2c_dev(i2c_dev); return res; } static int i2cdev_detach_adapter(struct device *dev, void *dummy) { struct i2c_adapter *adap; struct i2c_dev *i2c_dev; if (dev->type != &i2c_adapter_type) return 0; adap = to_i2c_adapter(dev); i2c_dev = i2c_dev_get_by_minor(adap->nr); if (!i2c_dev) /* attach_adapter must have failed */ return 0; cdev_del(&i2c_dev->cdev); put_i2c_dev(i2c_dev); device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); return 0; } static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { struct device *dev = data; switch (action) { case BUS_NOTIFY_ADD_DEVICE: return i2cdev_attach_adapter(dev, NULL); case BUS_NOTIFY_DEL_DEVICE: return i2cdev_detach_adapter(dev, NULL); } return 0; } static struct notifier_block i2cdev_notifier = { .notifier_call = i2cdev_notifier_call, }; /* ------------------------------------------------------------------------- */ /* * module load/unload record keeping */ static int __init i2c_dev_init(void) { int res; printk(KERN_INFO "i2c /dev entries driver\n"); res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); if (IS_ERR(i2c_dev_class)) { res = PTR_ERR(i2c_dev_class); goto out_unreg_chrdev; } i2c_dev_class->dev_groups = i2c_groups; /* Keep track of adapters which will be added or removed later */ res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); if (res) goto out_unreg_class; /* Bind to already existing adapters right away */ i2c_for_each_dev(NULL, i2cdev_attach_adapter); return 0; out_unreg_class: class_destroy(i2c_dev_class); out_unreg_chrdev: unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); out: printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); return res; } static void __exit i2c_dev_exit(void) { bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier); i2c_for_each_dev(NULL, i2cdev_detach_adapter); class_destroy(i2c_dev_class); unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); } MODULE_AUTHOR("Frodo Looijaard and " "Simon G. Vogl "); MODULE_DESCRIPTION("I2C /dev entries driver"); MODULE_LICENSE("GPL"); module_init(i2c_dev_init); module_exit(i2c_dev_exit); ================================================ FILE: t/tree/drivers/i2c/i2c-smbus.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* * i2c-smbus.c - SMBus extensions to the I2C protocol * * Copyright (C) 2008 David Brownell * Copyright (C) 2010 Jean Delvare */ #include #include #include #include #include #include #include #include #include struct i2c_smbus_alert { struct work_struct alert; struct i2c_client *ara; /* Alert response address */ }; struct alert_data { unsigned short addr; enum i2c_alert_protocol type; unsigned int data; }; /* If this is the alerting device, notify its driver */ static int smbus_do_alert(struct device *dev, void *addrp) { struct i2c_client *client = i2c_verify_client(dev); struct alert_data *data = addrp; struct i2c_driver *driver; if (!client || client->addr != data->addr) return 0; if (client->flags & I2C_CLIENT_TEN) return 0; /* * Drivers should either disable alerts, or provide at least * a minimal handler. Lock so the driver won't change. */ device_lock(dev); if (client->dev.driver) { driver = to_i2c_driver(client->dev.driver); if (driver->alert) driver->alert(client, data->type, data->data); else dev_warn(&client->dev, "no driver alert()!\n"); } else dev_dbg(&client->dev, "alert with no driver\n"); device_unlock(dev); /* Stop iterating after we find the device */ return -EBUSY; } /* * The alert IRQ handler needs to hand work off to a task which can issue * SMBus calls, because those sleeping calls can't be made in IRQ context. */ static irqreturn_t smbus_alert(int irq, void *d) { struct i2c_smbus_alert *alert = d; struct i2c_client *ara; unsigned short prev_addr = 0; /* Not a valid address */ ara = alert->ara; for (;;) { s32 status; struct alert_data data; /* * Devices with pending alerts reply in address order, low * to high, because of slave transmit arbitration. After * responding, an SMBus device stops asserting SMBALERT#. * * Note that SMBus 2.0 reserves 10-bit addresses for future * use. We neither handle them, nor try to use PEC here. */ status = i2c_smbus_read_byte(ara); if (status < 0) break; data.data = status & 1; data.addr = status >> 1; data.type = I2C_PROTOCOL_SMBUS_ALERT; if (data.addr == prev_addr) { dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " "0x%02x, skipping\n", data.addr); break; } dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", data.addr, data.data); /* Notify driver for the device which issued the alert */ device_for_each_child(&ara->adapter->dev, &data, smbus_do_alert); prev_addr = data.addr; } return IRQ_HANDLED; } static void smbalert_work(struct work_struct *work) { struct i2c_smbus_alert *alert; alert = container_of(work, struct i2c_smbus_alert, alert); smbus_alert(0, alert); } /* Setup SMBALERT# infrastructure */ static int smbalert_probe(struct i2c_client *ara, const struct i2c_device_id *id) { struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev); struct i2c_smbus_alert *alert; struct i2c_adapter *adapter = ara->adapter; int res, irq; alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert), GFP_KERNEL); if (!alert) return -ENOMEM; if (setup) { irq = setup->irq; } else { irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert"); if (irq <= 0) return irq; } INIT_WORK(&alert->alert, smbalert_work); alert->ara = ara; if (irq > 0) { res = devm_request_threaded_irq(&ara->dev, irq, NULL, smbus_alert, IRQF_SHARED | IRQF_ONESHOT, "smbus_alert", alert); if (res) return res; } i2c_set_clientdata(ara, alert); dev_info(&adapter->dev, "supports SMBALERT#\n"); return 0; } /* IRQ and memory resources are managed so they are freed automatically */ static int smbalert_remove(struct i2c_client *ara) { struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); cancel_work_sync(&alert->alert); return 0; } static const struct i2c_device_id smbalert_ids[] = { { "smbus_alert", 0 }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, smbalert_ids); static struct i2c_driver smbalert_driver = { .driver = { .name = "smbus_alert", }, .probe = smbalert_probe, .remove = smbalert_remove, .id_table = smbalert_ids, }; /** * i2c_handle_smbus_alert - Handle an SMBus alert * @ara: the ARA client on the relevant adapter * Context: can't sleep * * Helper function to be called from an I2C bus driver's interrupt * handler. It will schedule the alert work, in turn calling the * corresponding I2C device driver's alert function. * * It is assumed that ara is a valid i2c client previously returned by * i2c_setup_smbus_alert(). */ int i2c_handle_smbus_alert(struct i2c_client *ara) { struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); return schedule_work(&alert->alert); } EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); module_i2c_driver(smbalert_driver); MODULE_AUTHOR("Jean Delvare "); MODULE_DESCRIPTION("SMBus protocol extensions support"); MODULE_LICENSE("GPL"); ================================================ FILE: t/tree/drivers/i2c/i2c-stub.c ================================================ // SPDX-License-Identifier: GPL-2.0-or-later /* i2c-stub.c - I2C/SMBus chip emulator Copyright (c) 2004 Mark M. Hoffman Copyright (C) 2007-2014 Jean Delvare */ #define DEBUG 1 #define pr_fmt(fmt) "i2c-stub: " fmt #include #include #include #include #include #include #include #define MAX_CHIPS 10 /* * Support for I2C_FUNC_SMBUS_BLOCK_DATA is disabled by default and must * be enabled explicitly by setting the I2C_FUNC_SMBUS_BLOCK_DATA bits * in the 'functionality' module parameter. */ #define STUB_FUNC_DEFAULT \ (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ I2C_FUNC_SMBUS_I2C_BLOCK) #define STUB_FUNC_ALL \ (STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA) static unsigned short chip_addr[MAX_CHIPS]; module_param_array(chip_addr, ushort, NULL, S_IRUGO); MODULE_PARM_DESC(chip_addr, "Chip addresses (up to 10, between 0x03 and 0x77)"); static unsigned long functionality = STUB_FUNC_DEFAULT; module_param(functionality, ulong, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(functionality, "Override functionality bitfield"); /* Some chips have banked register ranges */ static u8 bank_reg[MAX_CHIPS]; module_param_array(bank_reg, byte, NULL, S_IRUGO); MODULE_PARM_DESC(bank_reg, "Bank register"); static u8 bank_mask[MAX_CHIPS]; module_param_array(bank_mask, byte, NULL, S_IRUGO); MODULE_PARM_DESC(bank_mask, "Bank value mask"); static u8 bank_start[MAX_CHIPS]; module_param_array(bank_start, byte, NULL, S_IRUGO); MODULE_PARM_DESC(bank_start, "First banked register"); static u8 bank_end[MAX_CHIPS]; module_param_array(bank_end, byte, NULL, S_IRUGO); MODULE_PARM_DESC(bank_end, "Last banked register"); struct smbus_block_data { struct list_head node; u8 command; u8 len; u8 block[I2C_SMBUS_BLOCK_MAX]; }; struct stub_chip { u8 pointer; u16 words[256]; /* Byte operations use the LSB as per SMBus specification */ struct list_head smbus_blocks; /* For chips with banks, extra registers are allocated dynamically */ u8 bank_reg; u8 bank_shift; u8 bank_mask; u8 bank_sel; /* Currently selected bank */ u8 bank_start; u8 bank_end; u16 bank_size; u16 *bank_words; /* Room for bank_mask * bank_size registers */ }; static struct stub_chip *stub_chips; static int stub_chips_nr; static struct smbus_block_data *stub_find_block(struct device *dev, struct stub_chip *chip, u8 command, bool create) { struct smbus_block_data *b, *rb = NULL; list_for_each_entry(b, &chip->smbus_blocks, node) { if (b->command == command) { rb = b; break; } } if (rb == NULL && create) { rb = devm_kzalloc(dev, sizeof(*rb), GFP_KERNEL); if (rb == NULL) return rb; rb->command = command; list_add(&rb->node, &chip->smbus_blocks); } return rb; } static u16 *stub_get_wordp(struct stub_chip *chip, u8 offset) { if (chip->bank_sel && offset >= chip->bank_start && offset <= chip->bank_end) return chip->bank_words + (chip->bank_sel - 1) * chip->bank_size + offset - chip->bank_start; else return chip->words + offset; } /* Return negative errno on error. */ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { s32 ret; int i, len; struct stub_chip *chip = NULL; struct smbus_block_data *b; u16 *wordp; /* Search for the right chip */ for (i = 0; i < stub_chips_nr; i++) { if (addr == chip_addr[i]) { chip = stub_chips + i; break; } } if (!chip) return -ENODEV; switch (size) { case I2C_SMBUS_QUICK: dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); ret = 0; break; case I2C_SMBUS_BYTE: if (read_write == I2C_SMBUS_WRITE) { chip->pointer = command; dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, wrote 0x%02x.\n", addr, command); } else { wordp = stub_get_wordp(chip, chip->pointer++); data->byte = *wordp & 0xff; dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, read 0x%02x.\n", addr, data->byte); } ret = 0; break; case I2C_SMBUS_BYTE_DATA: wordp = stub_get_wordp(chip, command); if (read_write == I2C_SMBUS_WRITE) { *wordp &= 0xff00; *wordp |= data->byte; dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n", addr, data->byte, command); /* Set the bank as needed */ if (chip->bank_words && command == chip->bank_reg) { chip->bank_sel = (data->byte >> chip->bank_shift) & chip->bank_mask; dev_dbg(&adap->dev, "switching to bank %u.\n", chip->bank_sel); } } else { data->byte = *wordp & 0xff; dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x.\n", addr, data->byte, command); } chip->pointer = command + 1; ret = 0; break; case I2C_SMBUS_WORD_DATA: wordp = stub_get_wordp(chip, command); if (read_write == I2C_SMBUS_WRITE) { *wordp = data->word; dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n", addr, data->word, command); } else { data->word = *wordp; dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, read 0x%04x at 0x%02x.\n", addr, data->word, command); } ret = 0; break; case I2C_SMBUS_I2C_BLOCK_DATA: /* * We ignore banks here, because banked chips don't use I2C * block transfers */ if (data->block[0] > 256 - command) /* Avoid overrun */ data->block[0] = 256 - command; len = data->block[0]; if (read_write == I2C_SMBUS_WRITE) { for (i = 0; i < len; i++) { chip->words[command + i] &= 0xff00; chip->words[command + i] |= data->block[1 + i]; } dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", addr, len, command); } else { for (i = 0; i < len; i++) { data->block[1 + i] = chip->words[command + i] & 0xff; } dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, read %d bytes at 0x%02x.\n", addr, len, command); } ret = 0; break; case I2C_SMBUS_BLOCK_DATA: /* * We ignore banks here, because chips typically don't use both * banks and SMBus block transfers */ b = stub_find_block(&adap->dev, chip, command, false); if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { ret = -EINVAL; break; } if (b == NULL) { b = stub_find_block(&adap->dev, chip, command, true); if (b == NULL) { ret = -ENOMEM; break; } } /* Largest write sets read block length */ if (len > b->len) b->len = len; for (i = 0; i < len; i++) b->block[i] = data->block[i + 1]; /* update for byte and word commands */ chip->words[command] = (b->block[0] << 8) | b->len; dev_dbg(&adap->dev, "smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", addr, len, command); } else { if (b == NULL) { dev_dbg(&adap->dev, "SMBus block read command without prior block write not supported\n"); ret = -EOPNOTSUPP; break; } len = b->len; data->block[0] = len; for (i = 0; i < len; i++) data->block[i + 1] = b->block[i]; dev_dbg(&adap->dev, "smbus block data - addr 0x%02x, read %d bytes at 0x%02x.\n", addr, len, command); } ret = 0; break; default: dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); ret = -EOPNOTSUPP; break; } /* switch (size) */ return ret; } static u32 stub_func(struct i2c_adapter *adapter) { return STUB_FUNC_ALL & functionality; } static const struct i2c_algorithm smbus_algorithm = { .functionality = stub_func, .smbus_xfer = stub_xfer, }; static struct i2c_adapter stub_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, .name = "SMBus stub driver", }; static int __init i2c_stub_allocate_banks(int i) { struct stub_chip *chip = stub_chips + i; chip->bank_reg = bank_reg[i]; chip->bank_start = bank_start[i]; chip->bank_end = bank_end[i]; chip->bank_size = bank_end[i] - bank_start[i] + 1; /* We assume that all bits in the mask are contiguous */ chip->bank_mask = bank_mask[i]; while (!(chip->bank_mask & 1)) { chip->bank_shift++; chip->bank_mask >>= 1; } chip->bank_words = kcalloc(chip->bank_mask * chip->bank_size, sizeof(u16), GFP_KERNEL); if (!chip->bank_words) return -ENOMEM; pr_debug("Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n", chip->bank_mask, chip->bank_size, chip->bank_start, chip->bank_end); return 0; } static void i2c_stub_free(void) { int i; for (i = 0; i < stub_chips_nr; i++) kfree(stub_chips[i].bank_words); kfree(stub_chips); } static int __init i2c_stub_init(void) { int i, ret; if (!chip_addr[0]) { pr_err("Please specify a chip address\n"); return -ENODEV; } for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { pr_err("Invalid chip address 0x%02x\n", chip_addr[i]); return -EINVAL; } pr_info("Virtual chip at 0x%02x\n", chip_addr[i]); } /* Allocate memory for all chips at once */ stub_chips_nr = i; stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip), GFP_KERNEL); if (!stub_chips) return -ENOMEM; for (i = 0; i < stub_chips_nr; i++) { INIT_LIST_HEAD(&stub_chips[i].smbus_blocks); /* Allocate extra memory for banked register ranges */ if (bank_mask[i]) { ret = i2c_stub_allocate_banks(i); if (ret) goto fail_free; } } ret = i2c_add_adapter(&stub_adapter); if (ret) goto fail_free; return 0; fail_free: i2c_stub_free(); return ret; } static void __exit i2c_stub_exit(void) { i2c_del_adapter(&stub_adapter); i2c_stub_free(); } MODULE_AUTHOR("Mark M. Hoffman "); MODULE_DESCRIPTION("I2C stub driver"); MODULE_LICENSE("GPL"); module_init(i2c_stub_init); module_exit(i2c_stub_exit); ================================================ FILE: t/tree/include/acpi/acpi_bus.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * acpi_bus.h - ACPI Bus Driver ($Revision: 22 $) * * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh */ #ifndef __ACPI_BUS_H__ #define __ACPI_BUS_H__ #include #include /* TBD: Make dynamic */ #define ACPI_MAX_HANDLES 10 struct acpi_handle_list { u32 count; acpi_handle handles[ACPI_MAX_HANDLES]; }; /* acpi_utils.h */ acpi_status acpi_extract_package(union acpi_object *package, struct acpi_buffer *format, struct acpi_buffer *buffer); acpi_status acpi_evaluate_integer(acpi_handle handle, acpi_string pathname, struct acpi_object_list *arguments, unsigned long long *data); acpi_status acpi_evaluate_reference(acpi_handle handle, acpi_string pathname, struct acpi_object_list *arguments, struct acpi_handle_list *list); acpi_status acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, struct acpi_buffer *status_buf); acpi_status acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld); bool acpi_has_method(acpi_handle handle, char *name); acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, u64 arg); acpi_status acpi_evaluate_ej0(acpi_handle handle); acpi_status acpi_evaluate_lck(acpi_handle handle, int lock); bool acpi_ata_match(acpi_handle handle); bool acpi_bay_match(acpi_handle handle); bool acpi_dock_match(acpi_handle handle); bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs); union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4); static inline union acpi_object * acpi_evaluate_dsm_typed(acpi_handle handle, const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4, acpi_object_type type) { union acpi_object *obj; obj = acpi_evaluate_dsm(handle, guid, rev, func, argv4); if (obj && obj->type != type) { ACPI_FREE(obj); obj = NULL; } return obj; } #define ACPI_INIT_DSM_ARGV4(cnt, eles) \ { \ .package.type = ACPI_TYPE_PACKAGE, \ .package.count = (cnt), \ .package.elements = (eles) \ } bool acpi_dev_found(const char *hid); bool acpi_dev_present(const char *hid, const char *uid, s64 hrv); struct acpi_device * acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv); #ifdef CONFIG_ACPI #include #define ACPI_BUS_FILE_ROOT "acpi" extern struct proc_dir_entry *acpi_root_dir; enum acpi_bus_device_type { ACPI_BUS_TYPE_DEVICE = 0, ACPI_BUS_TYPE_POWER, ACPI_BUS_TYPE_PROCESSOR, ACPI_BUS_TYPE_THERMAL, ACPI_BUS_TYPE_POWER_BUTTON, ACPI_BUS_TYPE_SLEEP_BUTTON, ACPI_BUS_TYPE_ECDT_EC, ACPI_BUS_DEVICE_TYPE_COUNT }; struct acpi_driver; struct acpi_device; /* * ACPI Scan Handler * ----------------- */ struct acpi_hotplug_profile { struct kobject kobj; int (*scan_dependent)(struct acpi_device *adev); void (*notify_online)(struct acpi_device *adev); bool enabled:1; bool demand_offline:1; }; static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( struct kobject *kobj) { return container_of(kobj, struct acpi_hotplug_profile, kobj); } struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; bool (*match)(const char *idstr, const struct acpi_device_id **matchid); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); void (*bind)(struct device *phys_dev); void (*unbind)(struct device *phys_dev); struct acpi_hotplug_profile hotplug; }; /* * ACPI Hotplug Context * -------------------- */ struct acpi_hotplug_context { struct acpi_device *self; int (*notify)(struct acpi_device *, u32); void (*uevent)(struct acpi_device *, u32); void (*fixup)(struct acpi_device *); }; /* * ACPI Driver * ----------- */ typedef int (*acpi_op_add) (struct acpi_device * device); typedef int (*acpi_op_remove) (struct acpi_device * device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); struct acpi_device_ops { acpi_op_add add; acpi_op_remove remove; acpi_op_notify notify; }; #define ACPI_DRIVER_ALL_NOTIFY_EVENTS 0x1 /* system AND device events */ struct acpi_driver { char name[80]; char class[80]; const struct acpi_device_id *ids; /* Supported Hardware IDs */ unsigned int flags; struct acpi_device_ops ops; struct device_driver drv; struct module *owner; }; /* * ACPI Device * ----------- */ /* Status (_STA) */ struct acpi_device_status { u32 present:1; u32 enabled:1; u32 show_in_ui:1; u32 functional:1; u32 battery_present:1; u32 reserved:27; }; /* Flags */ struct acpi_device_flags { u32 dynamic_status:1; u32 removable:1; u32 ejectable:1; u32 power_manageable:1; u32 match_driver:1; u32 initialized:1; u32 visited:1; u32 hotplug_notify:1; u32 is_dock_station:1; u32 of_compatible_ok:1; u32 coherent_dma:1; u32 cca_seen:1; u32 enumeration_by_parent:1; u32 reserved:19; }; /* File System */ struct acpi_device_dir { struct proc_dir_entry *entry; }; #define acpi_device_dir(d) ((d)->dir.entry) /* Plug and Play */ typedef char acpi_bus_id[8]; typedef u64 acpi_bus_address; typedef char acpi_device_name[40]; typedef char acpi_device_class[20]; struct acpi_hardware_id { struct list_head list; const char *id; }; struct acpi_pnp_type { u32 hardware_id:1; u32 bus_address:1; u32 platform_id:1; u32 reserved:29; }; struct acpi_device_pnp { acpi_bus_id bus_id; /* Object name */ struct acpi_pnp_type type; /* ID type */ acpi_bus_address bus_address; /* _ADR */ char *unique_id; /* _UID */ struct list_head ids; /* _HID and _CIDs */ acpi_device_name device_name; /* Driver-determined */ acpi_device_class device_class; /* " */ union acpi_object *str_obj; /* unicode string for _STR method */ }; #define acpi_device_bid(d) ((d)->pnp.bus_id) #define acpi_device_adr(d) ((d)->pnp.bus_address) const char *acpi_device_hid(struct acpi_device *device); #define acpi_device_uid(d) ((d)->pnp.unique_id) #define acpi_device_name(d) ((d)->pnp.device_name) #define acpi_device_class(d) ((d)->pnp.device_class) /* Power Management */ struct acpi_device_power_flags { u32 explicit_get:1; /* _PSC present? */ u32 power_resources:1; /* Power resources */ u32 inrush_current:1; /* Serialize Dx->D0 */ u32 power_removed:1; /* Optimize Dx->D0 */ u32 ignore_parent:1; /* Power is independent of parent power state */ u32 dsw_present:1; /* _DSW present? */ u32 reserved:26; }; struct acpi_device_power_state { struct { u8 valid:1; u8 explicit_set:1; /* _PSx present? */ u8 reserved:6; } flags; int power; /* % Power (compared to D0) */ int latency; /* Dx->D0 time (microseconds) */ struct list_head resources; /* Power resources referenced */ }; struct acpi_device_power { int state; /* Current state */ struct acpi_device_power_flags flags; struct acpi_device_power_state states[ACPI_D_STATE_COUNT]; /* Power states (D0-D3Cold) */ }; /* Performance Management */ struct acpi_device_perf_flags { u8 reserved:8; }; struct acpi_device_perf_state { struct { u8 valid:1; u8 reserved:7; } flags; u8 power; /* % Power (compared to P0) */ u8 performance; /* % Performance ( " ) */ int latency; /* Px->P0 time (microseconds) */ }; struct acpi_device_perf { int state; struct acpi_device_perf_flags flags; int state_count; struct acpi_device_perf_state *states; }; /* Wakeup Management */ struct acpi_device_wakeup_flags { u8 valid:1; /* Can successfully enable wakeup? */ u8 notifier_present:1; /* Wake-up notify handler has been installed */ }; struct acpi_device_wakeup_context { void (*func)(struct acpi_device_wakeup_context *context); struct device *dev; }; struct acpi_device_wakeup { acpi_handle gpe_device; u64 gpe_number; u64 sleep_state; struct list_head resources; struct acpi_device_wakeup_flags flags; struct acpi_device_wakeup_context context; struct wakeup_source *ws; int prepare_count; int enable_count; }; struct acpi_device_physical_node { unsigned int node_id; struct list_head node; struct device *dev; bool put_online:1; }; struct acpi_device_properties { const guid_t *guid; const union acpi_object *properties; struct list_head list; }; /* ACPI Device Specific Data (_DSD) */ struct acpi_device_data { const union acpi_object *pointer; struct list_head properties; const union acpi_object *of_compatible; struct list_head subnodes; }; struct acpi_gpio_mapping; /* Device */ struct acpi_device { int device_type; acpi_handle handle; /* no handle for fixed hardware */ struct fwnode_handle fwnode; struct acpi_device *parent; struct list_head children; struct list_head node; struct list_head wakeup_list; struct list_head del_list; struct acpi_device_status status; struct acpi_device_flags flags; struct acpi_device_pnp pnp; struct acpi_device_power power; struct acpi_device_wakeup wakeup; struct acpi_device_perf performance; struct acpi_device_dir dir; struct acpi_device_data data; struct acpi_scan_handler *handler; struct acpi_hotplug_context *hp; struct acpi_driver *driver; const struct acpi_gpio_mapping *driver_gpios; void *driver_data; struct device dev; unsigned int physical_node_count; unsigned int dep_unmet; struct list_head physical_node_list; struct mutex physical_node_lock; void (*remove)(struct acpi_device *); }; /* Non-device subnode */ struct acpi_data_node { const char *name; acpi_handle handle; struct fwnode_handle fwnode; struct fwnode_handle *parent; struct acpi_device_data data; struct list_head sibling; struct kobject kobj; struct completion kobj_done; }; extern const struct fwnode_operations acpi_device_fwnode_ops; extern const struct fwnode_operations acpi_data_fwnode_ops; extern const struct fwnode_operations acpi_static_fwnode_ops; bool is_acpi_device_node(const struct fwnode_handle *fwnode); bool is_acpi_data_node(const struct fwnode_handle *fwnode); static inline bool is_acpi_node(const struct fwnode_handle *fwnode) { return (is_acpi_device_node(fwnode) || is_acpi_data_node(fwnode)); } #define to_acpi_device_node(__fwnode) \ ({ \ typeof(__fwnode) __to_acpi_device_node_fwnode = __fwnode; \ \ is_acpi_device_node(__to_acpi_device_node_fwnode) ? \ container_of(__to_acpi_device_node_fwnode, \ struct acpi_device, fwnode) : \ NULL; \ }) #define to_acpi_data_node(__fwnode) \ ({ \ typeof(__fwnode) __to_acpi_data_node_fwnode = __fwnode; \ \ is_acpi_data_node(__to_acpi_data_node_fwnode) ? \ container_of(__to_acpi_data_node_fwnode, \ struct acpi_data_node, fwnode) : \ NULL; \ }) static inline bool is_acpi_static_node(const struct fwnode_handle *fwnode) { return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &acpi_static_fwnode_ops; } static inline bool acpi_data_node_match(const struct fwnode_handle *fwnode, const char *name) { return is_acpi_data_node(fwnode) ? (!strcmp(to_acpi_data_node(fwnode)->name, name)) : false; } static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) { return &adev->fwnode; } static inline void *acpi_driver_data(struct acpi_device *d) { return d->driver_data; } #define to_acpi_device(d) container_of(d, struct acpi_device, dev) #define to_acpi_driver(d) container_of(d, struct acpi_driver, drv) static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta) { *((u32 *)&adev->status) = sta; } static inline void acpi_set_hp_context(struct acpi_device *adev, struct acpi_hotplug_context *hp) { hp->self = adev; adev->hp = hp; } void acpi_initialize_hp_context(struct acpi_device *adev, struct acpi_hotplug_context *hp, int (*notify)(struct acpi_device *, u32), void (*uevent)(struct acpi_device *, u32)); /* acpi_device.dev.bus == &acpi_bus_type */ extern struct bus_type acpi_bus_type; /* * Events * ------ */ struct acpi_bus_event { struct list_head node; acpi_device_class device_class; acpi_bus_id bus_id; u32 type; u32 data; }; extern struct kobject *acpi_kobj; extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); void acpi_bus_private_data_handler(acpi_handle, void *); int acpi_bus_get_private_data(acpi_handle, void **); int acpi_bus_attach_private_data(acpi_handle, void *); void acpi_bus_detach_private_data(acpi_handle); extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); extern int register_acpi_notifier(struct notifier_block *); extern int unregister_acpi_notifier(struct notifier_block *); /* * External Functions */ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device); struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle); void acpi_bus_put_acpi_device(struct acpi_device *adev); acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_set_power(acpi_handle handle, int state); const char *acpi_power_state_string(int state); int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_init_power(struct acpi_device *device); int acpi_device_fix_up_power(struct acpi_device *device); int acpi_bus_update_power(acpi_handle handle, int *state_p); int acpi_device_update_power(struct acpi_device *device, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); int acpi_device_power_add_dependent(struct acpi_device *adev, struct device *dev); void acpi_device_power_remove_dependent(struct acpi_device *adev, struct device *dev); #ifdef CONFIG_PM bool acpi_bus_can_wakeup(acpi_handle handle); #else static inline bool acpi_bus_can_wakeup(acpi_handle handle) { return false; } #endif void acpi_scan_lock_acquire(void); void acpi_scan_lock_release(void); void acpi_lock_hp_context(void); void acpi_unlock_hp_context(void); int acpi_scan_add_handler(struct acpi_scan_handler *handler); int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_scan(acpi_handle handle); void acpi_bus_trim(struct acpi_device *start); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids); void acpi_set_modalias(struct acpi_device *adev, const char *default_id, char *modalias, size_t len); int acpi_create_dir(struct acpi_device *); void acpi_remove_dir(struct acpi_device *); static inline bool acpi_device_enumerated(struct acpi_device *adev) { return adev && adev->flags.initialized && adev->flags.visited; } /** * module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver * @__acpi_driver: acpi_driver struct * * Helper macro for ACPI drivers which do not do anything special in module * init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ #define module_acpi_driver(__acpi_driver) \ module_driver(__acpi_driver, acpi_bus_register_driver, \ acpi_bus_unregister_driver) /* * Bind physical devices with ACPI devices */ struct acpi_bus_type { struct list_head list; const char *name; bool (*match)(struct device *dev); struct acpi_device * (*find_companion)(struct device *); void (*setup)(struct device *); void (*cleanup)(struct device *); }; int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); int acpi_bind_one(struct device *dev, struct acpi_device *adev); int acpi_unbind_one(struct device *dev); struct acpi_pci_root { struct acpi_device * device; struct pci_bus *bus; u16 segment; struct resource secondary; /* downstream bus range */ u32 osc_support_set; /* _OSC state of support bits */ u32 osc_control_set; /* _OSC state of control bits */ phys_addr_t mcfg_addr; }; /* helper */ bool acpi_dma_supported(struct acpi_device *adev); enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev); int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset, u64 *size); int acpi_dma_configure(struct device *dev, enum dev_dma_attr attr); struct acpi_device *acpi_find_child_device(struct acpi_device *parent, u64 address, bool check_children); int acpi_is_root_bridge(acpi_handle); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); int acpi_disable_wakeup_device_power(struct acpi_device *dev); #ifdef CONFIG_X86 bool acpi_device_always_present(struct acpi_device *adev); #else static inline bool acpi_device_always_present(struct acpi_device *adev) { return false; } #endif #ifdef CONFIG_PM void acpi_pm_wakeup_event(struct device *dev); acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, void (*func)(struct acpi_device_wakeup_context *context)); acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); bool acpi_pm_device_can_wakeup(struct device *dev); int acpi_pm_device_sleep_state(struct device *, int *, int); int acpi_pm_set_device_wakeup(struct device *dev, bool enable); int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable); #else static inline void acpi_pm_wakeup_event(struct device *dev) { } static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, void (*func)(struct acpi_device_wakeup_context *context)) { return AE_SUPPORT; } static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) { return AE_SUPPORT; } static inline bool acpi_pm_device_can_wakeup(struct device *dev) { return false; } static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { if (p) *p = ACPI_STATE_D0; return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ? m : ACPI_STATE_D0; } static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable) { return -ENODEV; } static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable) { return -ENODEV; } #endif #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT bool acpi_sleep_state_supported(u8 sleep_state); #else static inline bool acpi_sleep_state_supported(u8 sleep_state) { return false; } #endif #ifdef CONFIG_ACPI_SLEEP u32 acpi_target_system_state(void); #else static inline u32 acpi_target_system_state(void) { return ACPI_STATE_S0; } #endif static inline bool acpi_device_power_manageable(struct acpi_device *adev) { return adev->flags.power_manageable; } static inline bool acpi_device_can_wakeup(struct acpi_device *adev) { return adev->wakeup.flags.valid; } static inline bool acpi_device_can_poweroff(struct acpi_device *adev) { return adev->power.states[ACPI_STATE_D3_COLD].flags.valid || ((acpi_gbl_FADT.header.revision < 6) && adev->power.states[ACPI_STATE_D3_HOT].flags.explicit_set); } static inline void acpi_dev_put(struct acpi_device *adev) { put_device(&adev->dev); } #else /* CONFIG_ACPI */ static inline int register_acpi_bus_type(void *bus) { return 0; } static inline int unregister_acpi_bus_type(void *bus) { return 0; } #endif /* CONFIG_ACPI */ #endif /*__ACPI_BUS_H__*/ ================================================ FILE: t/tree/include/acpi/acpi_drivers.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * acpi_drivers.h ($Revision: 31 $) * * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh */ #ifndef __ACPI_DRIVERS_H__ #define __ACPI_DRIVERS_H__ #define ACPI_MAX_STRING 80 /* * Please update drivers/acpi/debug.c and Documentation/firmware-guide/acpi/debug.rst * if you add to this list. */ #define ACPI_BUS_COMPONENT 0x00010000 #define ACPI_AC_COMPONENT 0x00020000 #define ACPI_BATTERY_COMPONENT 0x00040000 #define ACPI_BUTTON_COMPONENT 0x00080000 #define ACPI_SBS_COMPONENT 0x00100000 #define ACPI_FAN_COMPONENT 0x00200000 #define ACPI_PCI_COMPONENT 0x00400000 #define ACPI_POWER_COMPONENT 0x00800000 #define ACPI_CONTAINER_COMPONENT 0x01000000 #define ACPI_SYSTEM_COMPONENT 0x02000000 #define ACPI_THERMAL_COMPONENT 0x04000000 #define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000 #define ACPI_VIDEO_COMPONENT 0x10000000 #define ACPI_PROCESSOR_COMPONENT 0x20000000 /* * _HID definitions * HIDs must conform to ACPI spec(6.1.4) * Linux specific HIDs do not apply to this and begin with LNX: */ #define ACPI_POWER_HID "LNXPOWER" #define ACPI_PROCESSOR_OBJECT_HID "LNXCPU" #define ACPI_SYSTEM_HID "LNXSYSTM" #define ACPI_THERMAL_HID "LNXTHERM" #define ACPI_BUTTON_HID_POWERF "LNXPWRBN" #define ACPI_BUTTON_HID_SLEEPF "LNXSLPBN" #define ACPI_VIDEO_HID "LNXVIDEO" #define ACPI_BAY_HID "LNXIOBAY" #define ACPI_DOCK_HID "LNXDOCK" #define ACPI_ECDT_HID "LNXEC" /* Quirk for broken IBM BIOSes */ #define ACPI_SMBUS_IBM_HID "SMBUSIBM" /* * For fixed hardware buttons, we fabricate acpi_devices with HID * ACPI_BUTTON_HID_POWERF or ACPI_BUTTON_HID_SLEEPF. Fixed hardware * signals only an event; it doesn't supply a notification value. * To allow drivers to treat notifications from fixed hardware the * same as those from real devices, we turn the events into this * notification value. */ #define ACPI_FIXED_HARDWARE_EVENT 0x100 /* -------------------------------------------------------------------------- PCI -------------------------------------------------------------------------- */ /* ACPI PCI Interrupt Link (pci_link.c) */ int acpi_irq_penalty_init(void); int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name); int acpi_pci_link_free_irq(acpi_handle handle); /* ACPI PCI Device Binding (pci_bind.c) */ struct pci_bus; #ifdef CONFIG_PCI struct pci_dev *acpi_get_pci_dev(acpi_handle); #else static inline struct pci_dev *acpi_get_pci_dev(acpi_handle handle) { return NULL; } #endif /* Arch-defined function to add a bus to the system */ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root); #ifdef CONFIG_X86 void pci_acpi_crs_quirks(void); #else static inline void pci_acpi_crs_quirks(void) { } #endif /* -------------------------------------------------------------------------- Processor -------------------------------------------------------------------------- */ #define ACPI_PROCESSOR_LIMIT_NONE 0x00 #define ACPI_PROCESSOR_LIMIT_INCREMENT 0x01 #define ACPI_PROCESSOR_LIMIT_DECREMENT 0x02 /*-------------------------------------------------------------------------- Dock Station -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_DOCK extern int is_dock_device(struct acpi_device *adev); #else static inline int is_dock_device(struct acpi_device *adev) { return 0; } #endif /* CONFIG_ACPI_DOCK */ #endif /*__ACPI_DRIVERS_H__*/ ================================================ FILE: t/tree/include/asm-generic/barrier.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Generic barrier definitions. * * It should be possible to use these on really simple architectures, * but it serves more as a starting point for new ports. * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #ifndef __ASM_GENERIC_BARRIER_H #define __ASM_GENERIC_BARRIER_H #ifndef __ASSEMBLY__ #include #ifndef nop #define nop() asm volatile ("nop") #endif /* * Force strict CPU ordering. And yes, this is required on UP too when we're * talking to devices. * * Fall back to compiler barriers if nothing better is provided. */ #ifndef mb #define mb() barrier() #endif #ifndef rmb #define rmb() mb() #endif #ifndef wmb #define wmb() mb() #endif #ifndef dma_rmb #define dma_rmb() rmb() #endif #ifndef dma_wmb #define dma_wmb() wmb() #endif #ifndef read_barrier_depends #define read_barrier_depends() do { } while (0) #endif #ifndef __smp_mb #define __smp_mb() mb() #endif #ifndef __smp_rmb #define __smp_rmb() rmb() #endif #ifndef __smp_wmb #define __smp_wmb() wmb() #endif #ifndef __smp_read_barrier_depends #define __smp_read_barrier_depends() read_barrier_depends() #endif #ifdef CONFIG_SMP #ifndef smp_mb #define smp_mb() __smp_mb() #endif #ifndef smp_rmb #define smp_rmb() __smp_rmb() #endif #ifndef smp_wmb #define smp_wmb() __smp_wmb() #endif #ifndef smp_read_barrier_depends #define smp_read_barrier_depends() __smp_read_barrier_depends() #endif #else /* !CONFIG_SMP */ #ifndef smp_mb #define smp_mb() barrier() #endif #ifndef smp_rmb #define smp_rmb() barrier() #endif #ifndef smp_wmb #define smp_wmb() barrier() #endif #ifndef smp_read_barrier_depends #define smp_read_barrier_depends() do { } while (0) #endif #endif /* CONFIG_SMP */ #ifndef __smp_store_mb #define __smp_store_mb(var, value) do { WRITE_ONCE(var, value); __smp_mb(); } while (0) #endif #ifndef __smp_mb__before_atomic #define __smp_mb__before_atomic() __smp_mb() #endif #ifndef __smp_mb__after_atomic #define __smp_mb__after_atomic() __smp_mb() #endif #ifndef __smp_store_release #define __smp_store_release(p, v) \ do { \ compiletime_assert_atomic_type(*p); \ __smp_mb(); \ WRITE_ONCE(*p, v); \ } while (0) #endif #ifndef __smp_load_acquire #define __smp_load_acquire(p) \ ({ \ typeof(*p) ___p1 = READ_ONCE(*p); \ compiletime_assert_atomic_type(*p); \ __smp_mb(); \ ___p1; \ }) #endif #ifdef CONFIG_SMP #ifndef smp_store_mb #define smp_store_mb(var, value) __smp_store_mb(var, value) #endif #ifndef smp_mb__before_atomic #define smp_mb__before_atomic() __smp_mb__before_atomic() #endif #ifndef smp_mb__after_atomic #define smp_mb__after_atomic() __smp_mb__after_atomic() #endif #ifndef smp_store_release #define smp_store_release(p, v) __smp_store_release(p, v) #endif #ifndef smp_load_acquire #define smp_load_acquire(p) __smp_load_acquire(p) #endif #else /* !CONFIG_SMP */ #ifndef smp_store_mb #define smp_store_mb(var, value) do { WRITE_ONCE(var, value); barrier(); } while (0) #endif #ifndef smp_mb__before_atomic #define smp_mb__before_atomic() barrier() #endif #ifndef smp_mb__after_atomic #define smp_mb__after_atomic() barrier() #endif #ifndef smp_store_release #define smp_store_release(p, v) \ do { \ compiletime_assert_atomic_type(*p); \ barrier(); \ WRITE_ONCE(*p, v); \ } while (0) #endif #ifndef smp_load_acquire #define smp_load_acquire(p) \ ({ \ typeof(*p) ___p1 = READ_ONCE(*p); \ compiletime_assert_atomic_type(*p); \ barrier(); \ ___p1; \ }) #endif #endif /* CONFIG_SMP */ /* Barriers for virtual machine guests when talking to an SMP host */ #define virt_mb() __smp_mb() #define virt_rmb() __smp_rmb() #define virt_wmb() __smp_wmb() #define virt_read_barrier_depends() __smp_read_barrier_depends() #define virt_store_mb(var, value) __smp_store_mb(var, value) #define virt_mb__before_atomic() __smp_mb__before_atomic() #define virt_mb__after_atomic() __smp_mb__after_atomic() #define virt_store_release(p, v) __smp_store_release(p, v) #define virt_load_acquire(p) __smp_load_acquire(p) /** * smp_acquire__after_ctrl_dep() - Provide ACQUIRE ordering after a control dependency * * A control dependency provides a LOAD->STORE order, the additional RMB * provides LOAD->LOAD order, together they provide LOAD->{LOAD,STORE} order, * aka. (load)-ACQUIRE. * * Architectures that do not do load speculation can have this be barrier(). */ #ifndef smp_acquire__after_ctrl_dep #define smp_acquire__after_ctrl_dep() smp_rmb() #endif /** * smp_cond_load_relaxed() - (Spin) wait for cond with no ordering guarantees * @ptr: pointer to the variable to wait on * @cond: boolean expression to wait for * * Equivalent to using READ_ONCE() on the condition variable. * * Due to C lacking lambda expressions we load the value of *ptr into a * pre-named variable @VAL to be used in @cond. */ #ifndef smp_cond_load_relaxed #define smp_cond_load_relaxed(ptr, cond_expr) ({ \ typeof(ptr) __PTR = (ptr); \ typeof(*ptr) VAL; \ for (;;) { \ VAL = READ_ONCE(*__PTR); \ if (cond_expr) \ break; \ cpu_relax(); \ } \ VAL; \ }) #endif /** * smp_cond_load_acquire() - (Spin) wait for cond with ACQUIRE ordering * @ptr: pointer to the variable to wait on * @cond: boolean expression to wait for * * Equivalent to using smp_load_acquire() on the condition variable but employs * the control dependency of the wait to reduce the barrier on many platforms. */ #ifndef smp_cond_load_acquire #define smp_cond_load_acquire(ptr, cond_expr) ({ \ typeof(*ptr) _val; \ _val = smp_cond_load_relaxed(ptr, cond_expr); \ smp_acquire__after_ctrl_dep(); \ _val; \ }) #endif #endif /* !__ASSEMBLY__ */ #endif /* __ASM_GENERIC_BARRIER_H */ ================================================ FILE: t/tree/include/asm-generic/io.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Generic I/O port emulation. * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #ifndef __ASM_GENERIC_IO_H #define __ASM_GENERIC_IO_H #include /* I/O is all done through memory accesses */ #include /* for memset() and memcpy() */ #include #ifdef CONFIG_GENERIC_IOMAP #include #endif #include #include #ifndef __io_br #define __io_br() barrier() #endif /* prevent prefetching of coherent DMA data ahead of a dma-complete */ #ifndef __io_ar #ifdef rmb #define __io_ar(v) rmb() #else #define __io_ar(v) barrier() #endif #endif /* flush writes to coherent DMA data before possibly triggering a DMA read */ #ifndef __io_bw #ifdef wmb #define __io_bw() wmb() #else #define __io_bw() barrier() #endif #endif /* serialize device access against a spin_unlock, usually handled there. */ #ifndef __io_aw #define __io_aw() mmiowb_set_pending() #endif #ifndef __io_pbw #define __io_pbw() __io_bw() #endif #ifndef __io_paw #define __io_paw() __io_aw() #endif #ifndef __io_pbr #define __io_pbr() __io_br() #endif #ifndef __io_par #define __io_par(v) __io_ar(v) #endif /* * __raw_{read,write}{b,w,l,q}() access memory in native endianness. * * On some architectures memory mapped IO needs to be accessed differently. * On the simple architectures, we just read/write the memory location * directly. */ #ifndef __raw_readb #define __raw_readb __raw_readb static inline u8 __raw_readb(const volatile void __iomem *addr) { return *(const volatile u8 __force *)addr; } #endif #ifndef __raw_readw #define __raw_readw __raw_readw static inline u16 __raw_readw(const volatile void __iomem *addr) { return *(const volatile u16 __force *)addr; } #endif #ifndef __raw_readl #define __raw_readl __raw_readl static inline u32 __raw_readl(const volatile void __iomem *addr) { return *(const volatile u32 __force *)addr; } #endif #ifdef CONFIG_64BIT #ifndef __raw_readq #define __raw_readq __raw_readq static inline u64 __raw_readq(const volatile void __iomem *addr) { return *(const volatile u64 __force *)addr; } #endif #endif /* CONFIG_64BIT */ #ifndef __raw_writeb #define __raw_writeb __raw_writeb static inline void __raw_writeb(u8 value, volatile void __iomem *addr) { *(volatile u8 __force *)addr = value; } #endif #ifndef __raw_writew #define __raw_writew __raw_writew static inline void __raw_writew(u16 value, volatile void __iomem *addr) { *(volatile u16 __force *)addr = value; } #endif #ifndef __raw_writel #define __raw_writel __raw_writel static inline void __raw_writel(u32 value, volatile void __iomem *addr) { *(volatile u32 __force *)addr = value; } #endif #ifdef CONFIG_64BIT #ifndef __raw_writeq #define __raw_writeq __raw_writeq static inline void __raw_writeq(u64 value, volatile void __iomem *addr) { *(volatile u64 __force *)addr = value; } #endif #endif /* CONFIG_64BIT */ /* * {read,write}{b,w,l,q}() access little endian memory and return result in * native endianness. */ #ifndef readb #define readb readb static inline u8 readb(const volatile void __iomem *addr) { u8 val; __io_br(); val = __raw_readb(addr); __io_ar(val); return val; } #endif #ifndef readw #define readw readw static inline u16 readw(const volatile void __iomem *addr) { u16 val; __io_br(); val = __le16_to_cpu(__raw_readw(addr)); __io_ar(val); return val; } #endif #ifndef readl #define readl readl static inline u32 readl(const volatile void __iomem *addr) { u32 val; __io_br(); val = __le32_to_cpu(__raw_readl(addr)); __io_ar(val); return val; } #endif #ifdef CONFIG_64BIT #ifndef readq #define readq readq static inline u64 readq(const volatile void __iomem *addr) { u64 val; __io_br(); val = __le64_to_cpu(__raw_readq(addr)); __io_ar(val); return val; } #endif #endif /* CONFIG_64BIT */ #ifndef writeb #define writeb writeb static inline void writeb(u8 value, volatile void __iomem *addr) { __io_bw(); __raw_writeb(value, addr); __io_aw(); } #endif #ifndef writew #define writew writew static inline void writew(u16 value, volatile void __iomem *addr) { __io_bw(); __raw_writew(cpu_to_le16(value), addr); __io_aw(); } #endif #ifndef writel #define writel writel static inline void writel(u32 value, volatile void __iomem *addr) { __io_bw(); __raw_writel(__cpu_to_le32(value), addr); __io_aw(); } #endif #ifdef CONFIG_64BIT #ifndef writeq #define writeq writeq static inline void writeq(u64 value, volatile void __iomem *addr) { __io_bw(); __raw_writeq(__cpu_to_le64(value), addr); __io_aw(); } #endif #endif /* CONFIG_64BIT */ /* * {read,write}{b,w,l,q}_relaxed() are like the regular version, but * are not guaranteed to provide ordering against spinlocks or memory * accesses. */ #ifndef readb_relaxed #define readb_relaxed readb_relaxed static inline u8 readb_relaxed(const volatile void __iomem *addr) { return __raw_readb(addr); } #endif #ifndef readw_relaxed #define readw_relaxed readw_relaxed static inline u16 readw_relaxed(const volatile void __iomem *addr) { return __le16_to_cpu(__raw_readw(addr)); } #endif #ifndef readl_relaxed #define readl_relaxed readl_relaxed static inline u32 readl_relaxed(const volatile void __iomem *addr) { return __le32_to_cpu(__raw_readl(addr)); } #endif #if defined(readq) && !defined(readq_relaxed) #define readq_relaxed readq_relaxed static inline u64 readq_relaxed(const volatile void __iomem *addr) { return __le64_to_cpu(__raw_readq(addr)); } #endif #ifndef writeb_relaxed #define writeb_relaxed writeb_relaxed static inline void writeb_relaxed(u8 value, volatile void __iomem *addr) { __raw_writeb(value, addr); } #endif #ifndef writew_relaxed #define writew_relaxed writew_relaxed static inline void writew_relaxed(u16 value, volatile void __iomem *addr) { __raw_writew(cpu_to_le16(value), addr); } #endif #ifndef writel_relaxed #define writel_relaxed writel_relaxed static inline void writel_relaxed(u32 value, volatile void __iomem *addr) { __raw_writel(__cpu_to_le32(value), addr); } #endif #if defined(writeq) && !defined(writeq_relaxed) #define writeq_relaxed writeq_relaxed static inline void writeq_relaxed(u64 value, volatile void __iomem *addr) { __raw_writeq(__cpu_to_le64(value), addr); } #endif /* * {read,write}s{b,w,l,q}() repeatedly access the same memory address in * native endianness in 8-, 16-, 32- or 64-bit chunks (@count times). */ #ifndef readsb #define readsb readsb static inline void readsb(const volatile void __iomem *addr, void *buffer, unsigned int count) { if (count) { u8 *buf = buffer; do { u8 x = __raw_readb(addr); *buf++ = x; } while (--count); } } #endif #ifndef readsw #define readsw readsw static inline void readsw(const volatile void __iomem *addr, void *buffer, unsigned int count) { if (count) { u16 *buf = buffer; do { u16 x = __raw_readw(addr); *buf++ = x; } while (--count); } } #endif #ifndef readsl #define readsl readsl static inline void readsl(const volatile void __iomem *addr, void *buffer, unsigned int count) { if (count) { u32 *buf = buffer; do { u32 x = __raw_readl(addr); *buf++ = x; } while (--count); } } #endif #ifdef CONFIG_64BIT #ifndef readsq #define readsq readsq static inline void readsq(const volatile void __iomem *addr, void *buffer, unsigned int count) { if (count) { u64 *buf = buffer; do { u64 x = __raw_readq(addr); *buf++ = x; } while (--count); } } #endif #endif /* CONFIG_64BIT */ #ifndef writesb #define writesb writesb static inline void writesb(volatile void __iomem *addr, const void *buffer, unsigned int count) { if (count) { const u8 *buf = buffer; do { __raw_writeb(*buf++, addr); } while (--count); } } #endif #ifndef writesw #define writesw writesw static inline void writesw(volatile void __iomem *addr, const void *buffer, unsigned int count) { if (count) { const u16 *buf = buffer; do { __raw_writew(*buf++, addr); } while (--count); } } #endif #ifndef writesl #define writesl writesl static inline void writesl(volatile void __iomem *addr, const void *buffer, unsigned int count) { if (count) { const u32 *buf = buffer; do { __raw_writel(*buf++, addr); } while (--count); } } #endif #ifdef CONFIG_64BIT #ifndef writesq #define writesq writesq static inline void writesq(volatile void __iomem *addr, const void *buffer, unsigned int count) { if (count) { const u64 *buf = buffer; do { __raw_writeq(*buf++, addr); } while (--count); } } #endif #endif /* CONFIG_64BIT */ #ifndef PCI_IOBASE #define PCI_IOBASE ((void __iomem *)0) #endif #ifndef IO_SPACE_LIMIT #define IO_SPACE_LIMIT 0xffff #endif #include /* * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be * implemented on hardware that needs an additional delay for I/O accesses to * take effect. */ #ifndef inb #define inb inb static inline u8 inb(unsigned long addr) { u8 val; __io_pbr(); val = __raw_readb(PCI_IOBASE + addr); __io_par(val); return val; } #endif #ifndef inw #define inw inw static inline u16 inw(unsigned long addr) { u16 val; __io_pbr(); val = __le16_to_cpu(__raw_readw(PCI_IOBASE + addr)); __io_par(val); return val; } #endif #ifndef inl #define inl inl static inline u32 inl(unsigned long addr) { u32 val; __io_pbr(); val = __le32_to_cpu(__raw_readl(PCI_IOBASE + addr)); __io_par(val); return val; } #endif #ifndef outb #define outb outb static inline void outb(u8 value, unsigned long addr) { __io_pbw(); __raw_writeb(value, PCI_IOBASE + addr); __io_paw(); } #endif #ifndef outw #define outw outw static inline void outw(u16 value, unsigned long addr) { __io_pbw(); __raw_writew(cpu_to_le16(value), PCI_IOBASE + addr); __io_paw(); } #endif #ifndef outl #define outl outl static inline void outl(u32 value, unsigned long addr) { __io_pbw(); __raw_writel(cpu_to_le32(value), PCI_IOBASE + addr); __io_paw(); } #endif #ifndef inb_p #define inb_p inb_p static inline u8 inb_p(unsigned long addr) { return inb(addr); } #endif #ifndef inw_p #define inw_p inw_p static inline u16 inw_p(unsigned long addr) { return inw(addr); } #endif #ifndef inl_p #define inl_p inl_p static inline u32 inl_p(unsigned long addr) { return inl(addr); } #endif #ifndef outb_p #define outb_p outb_p static inline void outb_p(u8 value, unsigned long addr) { outb(value, addr); } #endif #ifndef outw_p #define outw_p outw_p static inline void outw_p(u16 value, unsigned long addr) { outw(value, addr); } #endif #ifndef outl_p #define outl_p outl_p static inline void outl_p(u32 value, unsigned long addr) { outl(value, addr); } #endif /* * {in,out}s{b,w,l}{,_p}() are variants of the above that repeatedly access a * single I/O port multiple times. */ #ifndef insb #define insb insb static inline void insb(unsigned long addr, void *buffer, unsigned int count) { readsb(PCI_IOBASE + addr, buffer, count); } #endif #ifndef insw #define insw insw static inline void insw(unsigned long addr, void *buffer, unsigned int count) { readsw(PCI_IOBASE + addr, buffer, count); } #endif #ifndef insl #define insl insl static inline void insl(unsigned long addr, void *buffer, unsigned int count) { readsl(PCI_IOBASE + addr, buffer, count); } #endif #ifndef outsb #define outsb outsb static inline void outsb(unsigned long addr, const void *buffer, unsigned int count) { writesb(PCI_IOBASE + addr, buffer, count); } #endif #ifndef outsw #define outsw outsw static inline void outsw(unsigned long addr, const void *buffer, unsigned int count) { writesw(PCI_IOBASE + addr, buffer, count); } #endif #ifndef outsl #define outsl outsl static inline void outsl(unsigned long addr, const void *buffer, unsigned int count) { writesl(PCI_IOBASE + addr, buffer, count); } #endif #ifndef insb_p #define insb_p insb_p static inline void insb_p(unsigned long addr, void *buffer, unsigned int count) { insb(addr, buffer, count); } #endif #ifndef insw_p #define insw_p insw_p static inline void insw_p(unsigned long addr, void *buffer, unsigned int count) { insw(addr, buffer, count); } #endif #ifndef insl_p #define insl_p insl_p static inline void insl_p(unsigned long addr, void *buffer, unsigned int count) { insl(addr, buffer, count); } #endif #ifndef outsb_p #define outsb_p outsb_p static inline void outsb_p(unsigned long addr, const void *buffer, unsigned int count) { outsb(addr, buffer, count); } #endif #ifndef outsw_p #define outsw_p outsw_p static inline void outsw_p(unsigned long addr, const void *buffer, unsigned int count) { outsw(addr, buffer, count); } #endif #ifndef outsl_p #define outsl_p outsl_p static inline void outsl_p(unsigned long addr, const void *buffer, unsigned int count) { outsl(addr, buffer, count); } #endif #ifndef CONFIG_GENERIC_IOMAP #ifndef ioread8 #define ioread8 ioread8 static inline u8 ioread8(const volatile void __iomem *addr) { return readb(addr); } #endif #ifndef ioread16 #define ioread16 ioread16 static inline u16 ioread16(const volatile void __iomem *addr) { return readw(addr); } #endif #ifndef ioread32 #define ioread32 ioread32 static inline u32 ioread32(const volatile void __iomem *addr) { return readl(addr); } #endif #ifdef CONFIG_64BIT #ifndef ioread64 #define ioread64 ioread64 static inline u64 ioread64(const volatile void __iomem *addr) { return readq(addr); } #endif #endif /* CONFIG_64BIT */ #ifndef iowrite8 #define iowrite8 iowrite8 static inline void iowrite8(u8 value, volatile void __iomem *addr) { writeb(value, addr); } #endif #ifndef iowrite16 #define iowrite16 iowrite16 static inline void iowrite16(u16 value, volatile void __iomem *addr) { writew(value, addr); } #endif #ifndef iowrite32 #define iowrite32 iowrite32 static inline void iowrite32(u32 value, volatile void __iomem *addr) { writel(value, addr); } #endif #ifdef CONFIG_64BIT #ifndef iowrite64 #define iowrite64 iowrite64 static inline void iowrite64(u64 value, volatile void __iomem *addr) { writeq(value, addr); } #endif #endif /* CONFIG_64BIT */ #ifndef ioread16be #define ioread16be ioread16be static inline u16 ioread16be(const volatile void __iomem *addr) { return swab16(readw(addr)); } #endif #ifndef ioread32be #define ioread32be ioread32be static inline u32 ioread32be(const volatile void __iomem *addr) { return swab32(readl(addr)); } #endif #ifdef CONFIG_64BIT #ifndef ioread64be #define ioread64be ioread64be static inline u64 ioread64be(const volatile void __iomem *addr) { return swab64(readq(addr)); } #endif #endif /* CONFIG_64BIT */ #ifndef iowrite16be #define iowrite16be iowrite16be static inline void iowrite16be(u16 value, void volatile __iomem *addr) { writew(swab16(value), addr); } #endif #ifndef iowrite32be #define iowrite32be iowrite32be static inline void iowrite32be(u32 value, volatile void __iomem *addr) { writel(swab32(value), addr); } #endif #ifdef CONFIG_64BIT #ifndef iowrite64be #define iowrite64be iowrite64be static inline void iowrite64be(u64 value, volatile void __iomem *addr) { writeq(swab64(value), addr); } #endif #endif /* CONFIG_64BIT */ #ifndef ioread8_rep #define ioread8_rep ioread8_rep static inline void ioread8_rep(const volatile void __iomem *addr, void *buffer, unsigned int count) { readsb(addr, buffer, count); } #endif #ifndef ioread16_rep #define ioread16_rep ioread16_rep static inline void ioread16_rep(const volatile void __iomem *addr, void *buffer, unsigned int count) { readsw(addr, buffer, count); } #endif #ifndef ioread32_rep #define ioread32_rep ioread32_rep static inline void ioread32_rep(const volatile void __iomem *addr, void *buffer, unsigned int count) { readsl(addr, buffer, count); } #endif #ifdef CONFIG_64BIT #ifndef ioread64_rep #define ioread64_rep ioread64_rep static inline void ioread64_rep(const volatile void __iomem *addr, void *buffer, unsigned int count) { readsq(addr, buffer, count); } #endif #endif /* CONFIG_64BIT */ #ifndef iowrite8_rep #define iowrite8_rep iowrite8_rep static inline void iowrite8_rep(volatile void __iomem *addr, const void *buffer, unsigned int count) { writesb(addr, buffer, count); } #endif #ifndef iowrite16_rep #define iowrite16_rep iowrite16_rep static inline void iowrite16_rep(volatile void __iomem *addr, const void *buffer, unsigned int count) { writesw(addr, buffer, count); } #endif #ifndef iowrite32_rep #define iowrite32_rep iowrite32_rep static inline void iowrite32_rep(volatile void __iomem *addr, const void *buffer, unsigned int count) { writesl(addr, buffer, count); } #endif #ifdef CONFIG_64BIT #ifndef iowrite64_rep #define iowrite64_rep iowrite64_rep static inline void iowrite64_rep(volatile void __iomem *addr, const void *buffer, unsigned int count) { writesq(addr, buffer, count); } #endif #endif /* CONFIG_64BIT */ #endif /* CONFIG_GENERIC_IOMAP */ #ifdef __KERNEL__ #include #define __io_virt(x) ((void __force *)(x)) #ifndef CONFIG_GENERIC_IOMAP struct pci_dev; extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); #ifndef pci_iounmap #define pci_iounmap pci_iounmap static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p) { } #endif #endif /* CONFIG_GENERIC_IOMAP */ /* * Change virtual addresses to physical addresses and vv. * These are pretty trivial */ #ifndef virt_to_phys #define virt_to_phys virt_to_phys static inline unsigned long virt_to_phys(volatile void *address) { return __pa((unsigned long)address); } #endif #ifndef phys_to_virt #define phys_to_virt phys_to_virt static inline void *phys_to_virt(unsigned long address) { return __va(address); } #endif /** * DOC: ioremap() and ioremap_*() variants * * If you have an IOMMU your architecture is expected to have both ioremap() * and iounmap() implemented otherwise the asm-generic helpers will provide a * direct mapping. * * There are ioremap_*() call variants, if you have no IOMMU we naturally will * default to direct mapping for all of them, you can override these defaults. * If you have an IOMMU you are highly encouraged to provide your own * ioremap variant implementation as there currently is no safe architecture * agnostic default. To avoid possible improper behaviour default asm-generic * ioremap_*() variants all return NULL when an IOMMU is available. If you've * defined your own ioremap_*() variant you must then declare your own * ioremap_*() variant as defined to itself to avoid the default NULL return. */ #ifdef CONFIG_MMU #ifndef ioremap_uc #define ioremap_uc ioremap_uc static inline void __iomem *ioremap_uc(phys_addr_t offset, size_t size) { return NULL; } #endif #else /* !CONFIG_MMU */ /* * Change "struct page" to physical address. * * This implementation is for the no-MMU case only... if you have an MMU * you'll need to provide your own definitions. */ #ifndef ioremap #define ioremap ioremap static inline void __iomem *ioremap(phys_addr_t offset, size_t size) { return (void __iomem *)(unsigned long)offset; } #endif #ifndef iounmap #define iounmap iounmap static inline void iounmap(void __iomem *addr) { } #endif #endif /* CONFIG_MMU */ #ifndef ioremap_nocache void __iomem *ioremap(phys_addr_t phys_addr, size_t size); #define ioremap_nocache ioremap_nocache static inline void __iomem *ioremap_nocache(phys_addr_t offset, size_t size) { return ioremap(offset, size); } #endif #ifndef ioremap_uc #define ioremap_uc ioremap_uc static inline void __iomem *ioremap_uc(phys_addr_t offset, size_t size) { return ioremap_nocache(offset, size); } #endif #ifndef ioremap_wc #define ioremap_wc ioremap_wc static inline void __iomem *ioremap_wc(phys_addr_t offset, size_t size) { return ioremap_nocache(offset, size); } #endif #ifndef ioremap_wt #define ioremap_wt ioremap_wt static inline void __iomem *ioremap_wt(phys_addr_t offset, size_t size) { return ioremap_nocache(offset, size); } #endif #ifdef CONFIG_HAS_IOPORT_MAP #ifndef CONFIG_GENERIC_IOMAP #ifndef ioport_map #define ioport_map ioport_map static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) { port &= IO_SPACE_LIMIT; return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port; } #endif #ifndef ioport_unmap #define ioport_unmap ioport_unmap static inline void ioport_unmap(void __iomem *p) { } #endif #else /* CONFIG_GENERIC_IOMAP */ extern void __iomem *ioport_map(unsigned long port, unsigned int nr); extern void ioport_unmap(void __iomem *p); #endif /* CONFIG_GENERIC_IOMAP */ #endif /* CONFIG_HAS_IOPORT_MAP */ /* * Convert a virtual cached pointer to an uncached pointer */ #ifndef xlate_dev_kmem_ptr #define xlate_dev_kmem_ptr xlate_dev_kmem_ptr static inline void *xlate_dev_kmem_ptr(void *addr) { return addr; } #endif #ifndef xlate_dev_mem_ptr #define xlate_dev_mem_ptr xlate_dev_mem_ptr static inline void *xlate_dev_mem_ptr(phys_addr_t addr) { return __va(addr); } #endif #ifndef unxlate_dev_mem_ptr #define unxlate_dev_mem_ptr unxlate_dev_mem_ptr static inline void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) { } #endif #ifdef CONFIG_VIRT_TO_BUS #ifndef virt_to_bus static inline unsigned long virt_to_bus(void *address) { return (unsigned long)address; } static inline void *bus_to_virt(unsigned long address) { return (void *)address; } #endif #endif #ifndef memset_io #define memset_io memset_io /** * memset_io Set a range of I/O memory to a constant value * @addr: The beginning of the I/O-memory range to set * @val: The value to set the memory to * @count: The number of bytes to set * * Set a range of I/O memory to a given value. */ static inline void memset_io(volatile void __iomem *addr, int value, size_t size) { memset(__io_virt(addr), value, size); } #endif #ifndef memcpy_fromio #define memcpy_fromio memcpy_fromio /** * memcpy_fromio Copy a block of data from I/O memory * @dst: The (RAM) destination for the copy * @src: The (I/O memory) source for the data * @count: The number of bytes to copy * * Copy a block of data from I/O memory. */ static inline void memcpy_fromio(void *buffer, const volatile void __iomem *addr, size_t size) { memcpy(buffer, __io_virt(addr), size); } #endif #ifndef memcpy_toio #define memcpy_toio memcpy_toio /** * memcpy_toio Copy a block of data into I/O memory * @dst: The (I/O memory) destination for the copy * @src: The (RAM) source for the data * @count: The number of bytes to copy * * Copy a block of data to I/O memory. */ static inline void memcpy_toio(volatile void __iomem *addr, const void *buffer, size_t size) { memcpy(__io_virt(addr), buffer, size); } #endif #endif /* __KERNEL__ */ #endif /* __ASM_GENERIC_IO_H */ ================================================ FILE: t/tree/include/asm-generic/pci_iomap.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* Generic I/O port emulation. * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #ifndef __ASM_GENERIC_PCI_IOMAP_H #define __ASM_GENERIC_PCI_IOMAP_H struct pci_dev; #ifdef CONFIG_PCI /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); extern void __iomem *pci_iomap_wc(struct pci_dev *dev, int bar, unsigned long max); extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, unsigned long offset, unsigned long maxlen); extern void __iomem *pci_iomap_wc_range(struct pci_dev *dev, int bar, unsigned long offset, unsigned long maxlen); /* Create a virtual mapping cookie for a port on a given PCI device. * Do not call this directly, it exists to make it easier for architectures * to override */ #ifdef CONFIG_NO_GENERIC_PCI_IOPORT_MAP extern void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port, unsigned int nr); #else #define __pci_ioport_map(dev, port, nr) ioport_map((port), (nr)) #endif #elif defined(CONFIG_GENERIC_PCI_IOMAP) static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) { return NULL; } static inline void __iomem *pci_iomap_wc(struct pci_dev *dev, int bar, unsigned long max) { return NULL; } static inline void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, unsigned long offset, unsigned long maxlen) { return NULL; } static inline void __iomem *pci_iomap_wc_range(struct pci_dev *dev, int bar, unsigned long offset, unsigned long maxlen) { return NULL; } #endif #endif /* __ASM_GENERIC_IO_H */ ================================================ FILE: t/tree/include/asm-generic/qrwlock.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Queue read/write lock * * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P. * * Authors: Waiman Long */ #ifndef __ASM_GENERIC_QRWLOCK_H #define __ASM_GENERIC_QRWLOCK_H #include #include #include #include /* * Writer states & reader shift and bias. */ #define _QW_WAITING 0x100 /* A writer is waiting */ #define _QW_LOCKED 0x0ff /* A writer holds the lock */ #define _QW_WMASK 0x1ff /* Writer mask */ #define _QR_SHIFT 9 /* Reader count shift */ #define _QR_BIAS (1U << _QR_SHIFT) /* * External function declarations */ extern void queued_read_lock_slowpath(struct qrwlock *lock); extern void queued_write_lock_slowpath(struct qrwlock *lock); /** * queued_read_trylock - try to acquire read lock of a queue rwlock * @lock : Pointer to queue rwlock structure * Return: 1 if lock acquired, 0 if failed */ static inline int queued_read_trylock(struct qrwlock *lock) { u32 cnts; cnts = atomic_read(&lock->cnts); if (likely(!(cnts & _QW_WMASK))) { cnts = (u32)atomic_add_return_acquire(_QR_BIAS, &lock->cnts); if (likely(!(cnts & _QW_WMASK))) return 1; atomic_sub(_QR_BIAS, &lock->cnts); } return 0; } /** * queued_write_trylock - try to acquire write lock of a queue rwlock * @lock : Pointer to queue rwlock structure * Return: 1 if lock acquired, 0 if failed */ static inline int queued_write_trylock(struct qrwlock *lock) { u32 cnts; cnts = atomic_read(&lock->cnts); if (unlikely(cnts)) return 0; return likely(atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, _QW_LOCKED)); } /** * queued_read_lock - acquire read lock of a queue rwlock * @lock: Pointer to queue rwlock structure */ static inline void queued_read_lock(struct qrwlock *lock) { u32 cnts; cnts = atomic_add_return_acquire(_QR_BIAS, &lock->cnts); if (likely(!(cnts & _QW_WMASK))) return; /* The slowpath will decrement the reader count, if necessary. */ queued_read_lock_slowpath(lock); } /** * queued_write_lock - acquire write lock of a queue rwlock * @lock : Pointer to queue rwlock structure */ static inline void queued_write_lock(struct qrwlock *lock) { u32 cnts = 0; /* Optimize for the unfair lock case where the fair flag is 0. */ if (likely(atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, _QW_LOCKED))) return; queued_write_lock_slowpath(lock); } /** * queued_read_unlock - release read lock of a queue rwlock * @lock : Pointer to queue rwlock structure */ static inline void queued_read_unlock(struct qrwlock *lock) { /* * Atomically decrement the reader count */ (void)atomic_sub_return_release(_QR_BIAS, &lock->cnts); } /** * queued_write_unlock - release write lock of a queue rwlock * @lock : Pointer to queue rwlock structure */ static inline void queued_write_unlock(struct qrwlock *lock) { smp_store_release(&lock->wlocked, 0); } /* * Remapping rwlock architecture specific functions to the corresponding * queue rwlock functions. */ #define arch_read_lock(l) queued_read_lock(l) #define arch_write_lock(l) queued_write_lock(l) #define arch_read_trylock(l) queued_read_trylock(l) #define arch_write_trylock(l) queued_write_trylock(l) #define arch_read_unlock(l) queued_read_unlock(l) #define arch_write_unlock(l) queued_write_unlock(l) #endif /* __ASM_GENERIC_QRWLOCK_H */ ================================================ FILE: t/tree/include/asm-generic/qspinlock.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Queued spinlock * * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P. * (C) Copyright 2015 Hewlett-Packard Enterprise Development LP * * Authors: Waiman Long */ #ifndef __ASM_GENERIC_QSPINLOCK_H #define __ASM_GENERIC_QSPINLOCK_H #include /** * queued_spin_is_locked - is the spinlock locked? * @lock: Pointer to queued spinlock structure * Return: 1 if it is locked, 0 otherwise */ static __always_inline int queued_spin_is_locked(struct qspinlock *lock) { /* * Any !0 state indicates it is locked, even if _Q_LOCKED_VAL * isn't immediately observable. */ return atomic_read(&lock->val); } /** * queued_spin_value_unlocked - is the spinlock structure unlocked? * @lock: queued spinlock structure * Return: 1 if it is unlocked, 0 otherwise * * N.B. Whenever there are tasks waiting for the lock, it is considered * locked wrt the lockref code to avoid lock stealing by the lockref * code and change things underneath the lock. This also allows some * optimizations to be applied without conflict with lockref. */ static __always_inline int queued_spin_value_unlocked(struct qspinlock lock) { return !atomic_read(&lock.val); } /** * queued_spin_is_contended - check if the lock is contended * @lock : Pointer to queued spinlock structure * Return: 1 if lock contended, 0 otherwise */ static __always_inline int queued_spin_is_contended(struct qspinlock *lock) { return atomic_read(&lock->val) & ~_Q_LOCKED_MASK; } /** * queued_spin_trylock - try to acquire the queued spinlock * @lock : Pointer to queued spinlock structure * Return: 1 if lock acquired, 0 if failed */ static __always_inline int queued_spin_trylock(struct qspinlock *lock) { u32 val = atomic_read(&lock->val); if (unlikely(val)) return 0; return likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)); } extern void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); /** * queued_spin_lock - acquire a queued spinlock * @lock: Pointer to queued spinlock structure */ static __always_inline void queued_spin_lock(struct qspinlock *lock) { u32 val = 0; if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL))) return; queued_spin_lock_slowpath(lock, val); } #ifndef queued_spin_unlock /** * queued_spin_unlock - release a queued spinlock * @lock : Pointer to queued spinlock structure */ static __always_inline void queued_spin_unlock(struct qspinlock *lock) { /* * unlock() needs release semantics: */ smp_store_release(&lock->locked, 0); } #endif #ifndef virt_spin_lock static __always_inline bool virt_spin_lock(struct qspinlock *lock) { return false; } #endif /* * Remapping spinlock architecture specific functions to the corresponding * queued spinlock functions. */ #define arch_spin_is_locked(l) queued_spin_is_locked(l) #define arch_spin_is_contended(l) queued_spin_is_contended(l) #define arch_spin_value_unlocked(l) queued_spin_value_unlocked(l) #define arch_spin_lock(l) queued_spin_lock(l) #define arch_spin_trylock(l) queued_spin_trylock(l) #define arch_spin_unlock(l) queued_spin_unlock(l) #endif /* __ASM_GENERIC_QSPINLOCK_H */ ================================================ FILE: t/tree/include/asm-generic/qspinlock_types.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Queued spinlock * * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P. * * Authors: Waiman Long */ #ifndef __ASM_GENERIC_QSPINLOCK_TYPES_H #define __ASM_GENERIC_QSPINLOCK_TYPES_H /* * Including atomic.h with PARAVIRT on will cause compilation errors because * of recursive header file incluson via paravirt_types.h. So don't include * it if PARAVIRT is on. */ #ifndef CONFIG_PARAVIRT #include #include #endif typedef struct qspinlock { union { atomic_t val; /* * By using the whole 2nd least significant byte for the * pending bit, we can allow better optimization of the lock * acquisition for the pending bit holder. */ #ifdef __LITTLE_ENDIAN struct { u8 locked; u8 pending; }; struct { u16 locked_pending; u16 tail; }; #else struct { u16 tail; u16 locked_pending; }; struct { u8 reserved[2]; u8 pending; u8 locked; }; #endif }; } arch_spinlock_t; /* * Initializier */ #define __ARCH_SPIN_LOCK_UNLOCKED { { .val = ATOMIC_INIT(0) } } /* * Bitfields in the atomic value: * * When NR_CPUS < 16K * 0- 7: locked byte * 8: pending * 9-15: not used * 16-17: tail index * 18-31: tail cpu (+1) * * When NR_CPUS >= 16K * 0- 7: locked byte * 8: pending * 9-10: tail index * 11-31: tail cpu (+1) */ #define _Q_SET_MASK(type) (((1U << _Q_ ## type ## _BITS) - 1)\ << _Q_ ## type ## _OFFSET) #define _Q_LOCKED_OFFSET 0 #define _Q_LOCKED_BITS 8 #define _Q_LOCKED_MASK _Q_SET_MASK(LOCKED) #define _Q_PENDING_OFFSET (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS) #if CONFIG_NR_CPUS < (1U << 14) #define _Q_PENDING_BITS 8 #else #define _Q_PENDING_BITS 1 #endif #define _Q_PENDING_MASK _Q_SET_MASK(PENDING) #define _Q_TAIL_IDX_OFFSET (_Q_PENDING_OFFSET + _Q_PENDING_BITS) #define _Q_TAIL_IDX_BITS 2 #define _Q_TAIL_IDX_MASK _Q_SET_MASK(TAIL_IDX) #define _Q_TAIL_CPU_OFFSET (_Q_TAIL_IDX_OFFSET + _Q_TAIL_IDX_BITS) #define _Q_TAIL_CPU_BITS (32 - _Q_TAIL_CPU_OFFSET) #define _Q_TAIL_CPU_MASK _Q_SET_MASK(TAIL_CPU) #define _Q_TAIL_OFFSET _Q_TAIL_IDX_OFFSET #define _Q_TAIL_MASK (_Q_TAIL_IDX_MASK | _Q_TAIL_CPU_MASK) #define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET) #define _Q_PENDING_VAL (1U << _Q_PENDING_OFFSET) #endif /* __ASM_GENERIC_QSPINLOCK_TYPES_H */ ================================================ FILE: t/tree/include/linux/acpi.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * acpi.h - ACPI Interface * * Copyright (C) 2001 Paul Diefenbaugh */ #ifndef _LINUX_ACPI_H #define _LINUX_ACPI_H #include #include /* for struct resource */ #include #include #include #include #include #ifndef _LINUX #define _LINUX #endif #include #ifdef CONFIG_ACPI #include #include #include #include #include #include #include #include #include #include static inline acpi_handle acpi_device_handle(struct acpi_device *adev) { return adev ? adev->handle : NULL; } #define ACPI_COMPANION(dev) to_acpi_device_node((dev)->fwnode) #define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ acpi_fwnode_handle(adev) : NULL) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) #define ACPI_HANDLE_FWNODE(fwnode) \ acpi_device_handle(to_acpi_device_node(fwnode)) static inline struct fwnode_handle *acpi_alloc_fwnode_static(void) { struct fwnode_handle *fwnode; fwnode = kzalloc(sizeof(struct fwnode_handle), GFP_KERNEL); if (!fwnode) return NULL; fwnode->ops = &acpi_static_fwnode_ops; return fwnode; } static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode) { if (WARN_ON(!is_acpi_static_node(fwnode))) return; kfree(fwnode); } /** * ACPI_DEVICE_CLASS - macro used to describe an ACPI device with * the PCI-defined class-code information * * @_cls : the class, subclass, prog-if triple for this device * @_msk : the class mask for this device * * This macro is used to create a struct acpi_device_id that matches a * specific PCI class. The .id and .driver_data fields will be left * initialized with the default value. */ #define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (_cls), .cls_msk = (_msk), static inline bool has_acpi_companion(struct device *dev) { return is_acpi_device_node(dev->fwnode); } static inline void acpi_preset_companion(struct device *dev, struct acpi_device *parent, u64 addr) { ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, false)); } static inline const char *acpi_dev_name(struct acpi_device *adev) { return dev_name(&adev->dev); } struct device *acpi_get_first_physical_node(struct acpi_device *adev); enum acpi_irq_model_id { ACPI_IRQ_MODEL_PIC = 0, ACPI_IRQ_MODEL_IOAPIC, ACPI_IRQ_MODEL_IOSAPIC, ACPI_IRQ_MODEL_PLATFORM, ACPI_IRQ_MODEL_GIC, ACPI_IRQ_MODEL_COUNT }; extern enum acpi_irq_model_id acpi_irq_model; enum acpi_interrupt_id { ACPI_INTERRUPT_PMI = 1, ACPI_INTERRUPT_INIT, ACPI_INTERRUPT_CPEI, ACPI_INTERRUPT_COUNT }; #define ACPI_SPACE_MEM 0 enum acpi_address_range_id { ACPI_ADDRESS_RANGE_MEMORY = 1, ACPI_ADDRESS_RANGE_RESERVED = 2, ACPI_ADDRESS_RANGE_ACPI = 3, ACPI_ADDRESS_RANGE_NVS = 4, ACPI_ADDRESS_RANGE_COUNT }; /* Table Handlers */ union acpi_subtable_headers { struct acpi_subtable_header common; struct acpi_hmat_structure hmat; }; typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table); typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *header, const unsigned long end); /* Debugger support */ struct acpi_debugger_ops { int (*create_thread)(acpi_osd_exec_callback function, void *context); ssize_t (*write_log)(const char *msg); ssize_t (*read_cmd)(char *buffer, size_t length); int (*wait_command_ready)(bool single_step, char *buffer, size_t length); int (*notify_command_complete)(void); }; struct acpi_debugger { const struct acpi_debugger_ops *ops; struct module *owner; struct mutex lock; }; #ifdef CONFIG_ACPI_DEBUGGER int __init acpi_debugger_init(void); int acpi_register_debugger(struct module *owner, const struct acpi_debugger_ops *ops); void acpi_unregister_debugger(const struct acpi_debugger_ops *ops); int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context); ssize_t acpi_debugger_write_log(const char *msg); ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length); int acpi_debugger_wait_command_ready(void); int acpi_debugger_notify_command_complete(void); #else static inline int acpi_debugger_init(void) { return -ENODEV; } static inline int acpi_register_debugger(struct module *owner, const struct acpi_debugger_ops *ops) { return -ENODEV; } static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops) { } static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context) { return -ENODEV; } static inline int acpi_debugger_write_log(const char *msg) { return -ENODEV; } static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length) { return -ENODEV; } static inline int acpi_debugger_wait_command_ready(void) { return -ENODEV; } static inline int acpi_debugger_notify_command_complete(void) { return -ENODEV; } #endif #define BAD_MADT_ENTRY(entry, end) ( \ (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) struct acpi_subtable_proc { int id; acpi_tbl_entry_handler handler; int count; }; void __iomem *__acpi_map_table(unsigned long phys, unsigned long size); void __acpi_unmap_table(void __iomem *map, unsigned long size); int early_acpi_boot_init(void); int acpi_boot_init (void); void acpi_boot_table_init (void); int acpi_mps_check (void); int acpi_numa_init (void); int acpi_table_init (void); int acpi_table_parse(char *id, acpi_tbl_table_handler handler); int __init acpi_table_parse_entries(char *id, unsigned long table_size, int entry_id, acpi_tbl_entry_handler handler, unsigned int max_entries); int __init acpi_table_parse_entries_array(char *id, unsigned long table_size, struct acpi_subtable_proc *proc, int proc_num, unsigned int max_entries); int acpi_table_parse_madt(enum acpi_madt_type id, acpi_tbl_entry_handler handler, unsigned int max_entries); int acpi_parse_mcfg (struct acpi_table_header *header); void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); /* the following numa functions are architecture-dependent */ void acpi_numa_slit_init (struct acpi_table_slit *slit); #if defined(CONFIG_X86) || defined(CONFIG_IA64) void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); #else static inline void acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) { } #endif void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); #ifdef CONFIG_ARM64 void acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa); #else static inline void acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa) { } #endif int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); #ifndef PHYS_CPUID_INVALID typedef u32 phys_cpuid_t; #define PHYS_CPUID_INVALID (phys_cpuid_t)(-1) #endif static inline bool invalid_logical_cpuid(u32 cpuid) { return (int)cpuid < 0; } static inline bool invalid_phys_cpuid(phys_cpuid_t phys_id) { return phys_id == PHYS_CPUID_INVALID; } /* Validate the processor object's proc_id */ bool acpi_duplicate_processor_id(int proc_id); #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu); int acpi_unmap_cpu(int cpu); #endif /* CONFIG_ACPI_HOTPLUG_CPU */ #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr); #endif int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base); int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base); int acpi_ioapic_registered(acpi_handle handle, u32 gsi_base); void acpi_irq_stats_init(void); extern u32 acpi_irq_handled; extern u32 acpi_irq_not_handled; extern unsigned int acpi_sci_irq; extern bool acpi_no_s5; #define INVALID_ACPI_IRQ ((unsigned)-1) static inline bool acpi_sci_irq_valid(void) { return acpi_sci_irq != INVALID_ACPI_IRQ; } extern int sbf_port; extern unsigned long acpi_realmode_flags; int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity); int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi); void acpi_set_irq_model(enum acpi_irq_model_id model, struct fwnode_handle *fwnode); struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, unsigned int size, struct fwnode_handle *fwnode, const struct irq_domain_ops *ops, void *host_data); #ifdef CONFIG_X86_IO_APIC extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity); #else static inline int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity) { return -1; } #endif /* * This function undoes the effect of one call to acpi_register_gsi(). * If this matches the last registration, any IRQ resources for gsi * are freed. */ void acpi_unregister_gsi (u32 gsi); struct pci_dev; int acpi_pci_irq_enable (struct pci_dev *dev); void acpi_penalize_isa_irq(int irq, int active); bool acpi_isa_irq_available(int irq); #ifdef CONFIG_PCI void acpi_penalize_sci_irq(int irq, int trigger, int polarity); #else static inline void acpi_penalize_sci_irq(int irq, int trigger, int polarity) { } #endif void acpi_pci_irq_disable (struct pci_dev *dev); extern int ec_read(u8 addr, u8 *val); extern int ec_write(u8 addr, u8 val); extern int ec_transaction(u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len); extern acpi_handle ec_get_handle(void); extern bool acpi_is_pnp_device(struct acpi_device *); #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) typedef void (*wmi_notify_handler) (u32 value, void *context); extern acpi_status wmi_evaluate_method(const char *guid, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out); extern acpi_status wmi_query_block(const char *guid, u8 instance, struct acpi_buffer *out); extern acpi_status wmi_set_block(const char *guid, u8 instance, const struct acpi_buffer *in); extern acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data); extern acpi_status wmi_remove_notify_handler(const char *guid); extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out); extern bool wmi_has_guid(const char *guid); extern char *wmi_get_acpi_device_uid(const char *guid); #endif /* CONFIG_ACPI_WMI */ #define ACPI_VIDEO_OUTPUT_SWITCHING 0x0001 #define ACPI_VIDEO_DEVICE_POSTING 0x0002 #define ACPI_VIDEO_ROM_AVAILABLE 0x0004 #define ACPI_VIDEO_BACKLIGHT 0x0008 #define ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR 0x0010 #define ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO 0x0020 #define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR 0x0040 #define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO 0x0080 #define ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 0x0100 #define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200 #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400 #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800 extern char acpi_video_backlight_string[]; extern long acpi_is_video_device(acpi_handle handle); extern int acpi_blacklisted(void); extern void acpi_osi_setup(char *str); extern bool acpi_osi_is_win8(void); #ifdef CONFIG_ACPI_NUMA int acpi_map_pxm_to_online_node(int pxm); int acpi_map_pxm_to_node(int pxm); int acpi_get_node(acpi_handle handle); #else static inline int acpi_map_pxm_to_online_node(int pxm) { return 0; } static inline int acpi_map_pxm_to_node(int pxm) { return 0; } static inline int acpi_get_node(acpi_handle handle) { return 0; } #endif extern int acpi_paddr_to_node(u64 start_addr, u64 size); extern int pnpacpi_disabled; #define PXM_INVAL (-1) bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res); bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res); bool acpi_dev_resource_address_space(struct acpi_resource *ares, struct resource_win *win); bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, struct resource_win *win); unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable); unsigned int acpi_dev_get_irq_type(int triggering, int polarity); bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, struct resource *res); void acpi_dev_free_resource_list(struct list_head *list); int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, int (*preproc)(struct acpi_resource *, void *), void *preproc_data); int acpi_dev_get_dma_resources(struct acpi_device *adev, struct list_head *list); int acpi_dev_filter_resource_type(struct acpi_resource *ares, unsigned long types); static inline int acpi_dev_filter_resource_type_cb(struct acpi_resource *ares, void *arg) { return acpi_dev_filter_resource_type(ares, (unsigned long)arg); } struct acpi_device *acpi_resource_consumer(struct resource *res); int acpi_check_resource_conflict(const struct resource *res); int acpi_check_region(resource_size_t start, resource_size_t n, const char *name); acpi_status acpi_release_memory(acpi_handle handle, struct resource *res, u32 level); int acpi_resources_are_enforced(void); #ifdef CONFIG_HIBERNATION void __init acpi_no_s4_hw_signature(void); #endif #ifdef CONFIG_PM_SLEEP void __init acpi_old_suspend_ordering(void); void __init acpi_nvs_nosave(void); void __init acpi_nvs_nosave_s3(void); void __init acpi_sleep_no_blacklist(void); #endif /* CONFIG_PM_SLEEP */ struct acpi_osc_context { char *uuid_str; /* UUID string */ int rev; struct acpi_buffer cap; /* list of DWORD capabilities */ struct acpi_buffer ret; /* free by caller if success */ }; acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); /* Indexes into _OSC Capabilities Buffer (DWORDs 2 & 3 are device-specific) */ #define OSC_QUERY_DWORD 0 /* DWORD 1 */ #define OSC_SUPPORT_DWORD 1 /* DWORD 2 */ #define OSC_CONTROL_DWORD 2 /* DWORD 3 */ /* _OSC Capabilities DWORD 1: Query/Control and Error Returns (generic) */ #define OSC_QUERY_ENABLE 0x00000001 /* input */ #define OSC_REQUEST_ERROR 0x00000002 /* return */ #define OSC_INVALID_UUID_ERROR 0x00000004 /* return */ #define OSC_INVALID_REVISION_ERROR 0x00000008 /* return */ #define OSC_CAPABILITIES_MASK_ERROR 0x00000010 /* return */ /* Platform-Wide Capabilities _OSC: Capabilities DWORD 2: Support Field */ #define OSC_SB_PAD_SUPPORT 0x00000001 #define OSC_SB_PPC_OST_SUPPORT 0x00000002 #define OSC_SB_PR3_SUPPORT 0x00000004 #define OSC_SB_HOTPLUG_OST_SUPPORT 0x00000008 #define OSC_SB_APEI_SUPPORT 0x00000010 #define OSC_SB_CPC_SUPPORT 0x00000020 #define OSC_SB_CPCV2_SUPPORT 0x00000040 #define OSC_SB_PCLPI_SUPPORT 0x00000080 #define OSC_SB_OSLPI_SUPPORT 0x00000100 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 extern bool osc_sb_apei_support_acked; extern bool osc_pc_lpi_support_confirmed; /* PCI Host Bridge _OSC: Capabilities DWORD 2: Support Field */ #define OSC_PCI_EXT_CONFIG_SUPPORT 0x00000001 #define OSC_PCI_ASPM_SUPPORT 0x00000002 #define OSC_PCI_CLOCK_PM_SUPPORT 0x00000004 #define OSC_PCI_SEGMENT_GROUPS_SUPPORT 0x00000008 #define OSC_PCI_MSI_SUPPORT 0x00000010 #define OSC_PCI_HPX_TYPE_3_SUPPORT 0x00000100 #define OSC_PCI_SUPPORT_MASKS 0x0000011f /* PCI Host Bridge _OSC: Capabilities DWORD 3: Control Field */ #define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 0x00000001 #define OSC_PCI_SHPC_NATIVE_HP_CONTROL 0x00000002 #define OSC_PCI_EXPRESS_PME_CONTROL 0x00000004 #define OSC_PCI_EXPRESS_AER_CONTROL 0x00000008 #define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010 #define OSC_PCI_EXPRESS_LTR_CONTROL 0x00000020 #define OSC_PCI_CONTROL_MASKS 0x0000003f #define ACPI_GSB_ACCESS_ATTRIB_QUICK 0x00000002 #define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV 0x00000004 #define ACPI_GSB_ACCESS_ATTRIB_BYTE 0x00000006 #define ACPI_GSB_ACCESS_ATTRIB_WORD 0x00000008 #define ACPI_GSB_ACCESS_ATTRIB_BLOCK 0x0000000A #define ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE 0x0000000B #define ACPI_GSB_ACCESS_ATTRIB_WORD_CALL 0x0000000C #define ACPI_GSB_ACCESS_ATTRIB_BLOCK_CALL 0x0000000D #define ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES 0x0000000E #define ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS 0x0000000F extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); /* Enable _OST when all relevant hotplug operations are enabled */ #if defined(CONFIG_ACPI_HOTPLUG_CPU) && \ defined(CONFIG_ACPI_HOTPLUG_MEMORY) && \ defined(CONFIG_ACPI_CONTAINER) #define ACPI_HOTPLUG_OST #endif /* _OST Source Event Code (OSPM Action) */ #define ACPI_OST_EC_OSPM_SHUTDOWN 0x100 #define ACPI_OST_EC_OSPM_EJECT 0x103 #define ACPI_OST_EC_OSPM_INSERTION 0x200 /* _OST General Processing Status Code */ #define ACPI_OST_SC_SUCCESS 0x0 #define ACPI_OST_SC_NON_SPECIFIC_FAILURE 0x1 #define ACPI_OST_SC_UNRECOGNIZED_NOTIFY 0x2 /* _OST OS Shutdown Processing (0x100) Status Code */ #define ACPI_OST_SC_OS_SHUTDOWN_DENIED 0x80 #define ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS 0x81 #define ACPI_OST_SC_OS_SHUTDOWN_COMPLETED 0x82 #define ACPI_OST_SC_OS_SHUTDOWN_NOT_SUPPORTED 0x83 /* _OST Ejection Request (0x3, 0x103) Status Code */ #define ACPI_OST_SC_EJECT_NOT_SUPPORTED 0x80 #define ACPI_OST_SC_DEVICE_IN_USE 0x81 #define ACPI_OST_SC_DEVICE_BUSY 0x82 #define ACPI_OST_SC_EJECT_DEPENDENCY_BUSY 0x83 #define ACPI_OST_SC_EJECT_IN_PROGRESS 0x84 /* _OST Insertion Request (0x200) Status Code */ #define ACPI_OST_SC_INSERT_IN_PROGRESS 0x80 #define ACPI_OST_SC_DRIVER_LOAD_FAILURE 0x81 #define ACPI_OST_SC_INSERT_NOT_SUPPORTED 0x82 enum acpi_predicate { all_versions, less_than_or_equal, equal, greater_than_or_equal, }; /* Table must be terminted by a NULL entry */ struct acpi_platform_list { char oem_id[ACPI_OEM_ID_SIZE+1]; char oem_table_id[ACPI_OEM_TABLE_ID_SIZE+1]; u32 oem_revision; char *table; enum acpi_predicate pred; char *reason; u32 data; }; int acpi_match_platform_list(const struct acpi_platform_list *plat); extern void acpi_early_init(void); extern void acpi_subsystem_init(void); extern void arch_post_acpi_subsys_init(void); extern int acpi_nvs_register(__u64 start, __u64 size); extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), void *data); const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct device *dev); const void *acpi_device_get_match_data(const struct device *dev); extern bool acpi_driver_match_device(struct device *dev, const struct device_driver *drv); int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); int acpi_device_modalias(struct device *, char *, int); void acpi_walk_dep_device_list(acpi_handle handle); struct platform_device *acpi_create_platform_device(struct acpi_device *, struct property_entry *); #define ACPI_PTR(_ptr) (_ptr) static inline void acpi_device_set_enumerated(struct acpi_device *adev) { adev->flags.visited = true; } static inline void acpi_device_clear_enumerated(struct acpi_device *adev) { adev->flags.visited = false; } enum acpi_reconfig_event { ACPI_RECONFIG_DEVICE_ADD = 0, ACPI_RECONFIG_DEVICE_REMOVE, }; int acpi_reconfig_notifier_register(struct notifier_block *nb); int acpi_reconfig_notifier_unregister(struct notifier_block *nb); #ifdef CONFIG_ACPI_GTDT int acpi_gtdt_init(struct acpi_table_header *table, int *platform_timer_count); int acpi_gtdt_map_ppi(int type); bool acpi_gtdt_c3stop(int type); int acpi_arch_timer_mem_init(struct arch_timer_mem *timer_mem, int *timer_count); #endif #ifndef ACPI_HAVE_ARCH_SET_ROOT_POINTER static inline void acpi_arch_set_root_pointer(u64 addr) { } #endif #ifndef ACPI_HAVE_ARCH_GET_ROOT_POINTER static inline u64 acpi_arch_get_root_pointer(void) { return 0; } #endif #else /* !CONFIG_ACPI */ #define acpi_disabled 1 #define ACPI_COMPANION(dev) (NULL) #define ACPI_COMPANION_SET(dev, adev) do { } while (0) #define ACPI_HANDLE(dev) (NULL) #define ACPI_HANDLE_FWNODE(fwnode) (NULL) #define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (0), .cls_msk = (0), struct fwnode_handle; static inline bool acpi_dev_found(const char *hid) { return false; } static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv) { return false; } static inline struct acpi_device * acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv) { return NULL; } static inline void acpi_dev_put(struct acpi_device *adev) {} static inline bool is_acpi_node(struct fwnode_handle *fwnode) { return false; } static inline bool is_acpi_device_node(struct fwnode_handle *fwnode) { return false; } static inline struct acpi_device *to_acpi_device_node(struct fwnode_handle *fwnode) { return NULL; } static inline bool is_acpi_data_node(struct fwnode_handle *fwnode) { return false; } static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwnode) { return NULL; } static inline bool acpi_data_node_match(struct fwnode_handle *fwnode, const char *name) { return false; } static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) { return NULL; } static inline bool has_acpi_companion(struct device *dev) { return false; } static inline void acpi_preset_companion(struct device *dev, struct acpi_device *parent, u64 addr) { } static inline const char *acpi_dev_name(struct acpi_device *adev) { return NULL; } static inline struct device *acpi_get_first_physical_node(struct acpi_device *adev) { return NULL; } static inline void acpi_early_init(void) { } static inline void acpi_subsystem_init(void) { } static inline int early_acpi_boot_init(void) { return 0; } static inline int acpi_boot_init(void) { return 0; } static inline void acpi_boot_table_init(void) { return; } static inline int acpi_mps_check(void) { return 0; } static inline int acpi_check_resource_conflict(struct resource *res) { return 0; } static inline int acpi_check_region(resource_size_t start, resource_size_t n, const char *name) { return 0; } struct acpi_table_header; static inline int acpi_table_parse(char *id, int (*handler)(struct acpi_table_header *)) { return -ENODEV; } static inline int acpi_nvs_register(__u64 start, __u64 size) { return 0; } static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), void *data) { return 0; } struct acpi_device_id; static inline const struct acpi_device_id *acpi_match_device( const struct acpi_device_id *ids, const struct device *dev) { return NULL; } static inline const void *acpi_device_get_match_data(const struct device *dev) { return NULL; } static inline bool acpi_driver_match_device(struct device *dev, const struct device_driver *drv) { return false; } static inline union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, int rev, int func, union acpi_object *argv4) { return NULL; } static inline int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) { return -ENODEV; } static inline int acpi_device_modalias(struct device *dev, char *buf, int size) { return -ENODEV; } static inline bool acpi_dma_supported(struct acpi_device *adev) { return false; } static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev) { return DEV_DMA_NOT_SUPPORTED; } static inline int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset, u64 *size) { return -ENODEV; } static inline int acpi_dma_configure(struct device *dev, enum dev_dma_attr attr) { return 0; } #define ACPI_PTR(_ptr) (NULL) static inline void acpi_device_set_enumerated(struct acpi_device *adev) { } static inline void acpi_device_clear_enumerated(struct acpi_device *adev) { } static inline int acpi_reconfig_notifier_register(struct notifier_block *nb) { return -EINVAL; } static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb) { return -EINVAL; } static inline struct acpi_device *acpi_resource_consumer(struct resource *res) { return NULL; } #endif /* !CONFIG_ACPI */ #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC int acpi_ioapic_add(acpi_handle root); #else static inline int acpi_ioapic_add(acpi_handle root) { return 0; } #endif #ifdef CONFIG_ACPI void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, u32 pm1a_ctrl, u32 pm1b_ctrl)); acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control); void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, u32 val_a, u32 val_b)); acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, u32 val_a, u32 val_b); #ifdef CONFIG_X86 void arch_reserve_mem_area(acpi_physical_address addr, size_t size); #else static inline void arch_reserve_mem_area(acpi_physical_address addr, size_t size) { } #endif /* CONFIG_X86 */ #else #define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) #endif #if defined(CONFIG_ACPI) && defined(CONFIG_PM) int acpi_dev_suspend(struct device *dev, bool wakeup); int acpi_dev_resume(struct device *dev); int acpi_subsys_runtime_suspend(struct device *dev); int acpi_subsys_runtime_resume(struct device *dev); int acpi_dev_pm_attach(struct device *dev, bool power_on); #else static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } static inline int acpi_dev_pm_attach(struct device *dev, bool power_on) { return 0; } #endif #if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP) int acpi_subsys_prepare(struct device *dev); void acpi_subsys_complete(struct device *dev); int acpi_subsys_suspend_late(struct device *dev); int acpi_subsys_suspend_noirq(struct device *dev); int acpi_subsys_suspend(struct device *dev); int acpi_subsys_freeze(struct device *dev); int acpi_subsys_poweroff(struct device *dev); void acpi_ec_mark_gpe_for_wake(void); void acpi_ec_set_gpe_wake_mask(u8 action); #else static inline int acpi_subsys_prepare(struct device *dev) { return 0; } static inline void acpi_subsys_complete(struct device *dev) {} static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; } static inline int acpi_subsys_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_freeze(struct device *dev) { return 0; } static inline int acpi_subsys_poweroff(struct device *dev) { return 0; } static inline void acpi_ec_mark_gpe_for_wake(void) {} static inline void acpi_ec_set_gpe_wake_mask(u8 action) {} #endif #ifdef CONFIG_ACPI __printf(3, 4) void acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...); #else /* !CONFIG_ACPI */ static inline __printf(3, 4) void acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {} #endif /* !CONFIG_ACPI */ #if defined(CONFIG_ACPI) && defined(CONFIG_DYNAMIC_DEBUG) __printf(3, 4) void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const char *fmt, ...); #endif /* * acpi_handle_: Print message with ACPI prefix and object path * * These interfaces acquire the global namespace mutex to obtain an object * path. In interrupt context, it shows the object path as . */ #define acpi_handle_emerg(handle, fmt, ...) \ acpi_handle_printk(KERN_EMERG, handle, fmt, ##__VA_ARGS__) #define acpi_handle_alert(handle, fmt, ...) \ acpi_handle_printk(KERN_ALERT, handle, fmt, ##__VA_ARGS__) #define acpi_handle_crit(handle, fmt, ...) \ acpi_handle_printk(KERN_CRIT, handle, fmt, ##__VA_ARGS__) #define acpi_handle_err(handle, fmt, ...) \ acpi_handle_printk(KERN_ERR, handle, fmt, ##__VA_ARGS__) #define acpi_handle_warn(handle, fmt, ...) \ acpi_handle_printk(KERN_WARNING, handle, fmt, ##__VA_ARGS__) #define acpi_handle_notice(handle, fmt, ...) \ acpi_handle_printk(KERN_NOTICE, handle, fmt, ##__VA_ARGS__) #define acpi_handle_info(handle, fmt, ...) \ acpi_handle_printk(KERN_INFO, handle, fmt, ##__VA_ARGS__) #if defined(DEBUG) #define acpi_handle_debug(handle, fmt, ...) \ acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__) #else #if defined(CONFIG_DYNAMIC_DEBUG) #define acpi_handle_debug(handle, fmt, ...) \ _dynamic_func_call(fmt, __acpi_handle_debug, \ handle, pr_fmt(fmt), ##__VA_ARGS__) #else #define acpi_handle_debug(handle, fmt, ...) \ ({ \ if (0) \ acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__); \ 0; \ }) #endif #endif #if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, struct acpi_resource_gpio **agpio); int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index); #else static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, struct acpi_resource_gpio **agpio) { return false; } static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) { return -ENXIO; } #endif /* Device properties */ #ifdef CONFIG_ACPI int acpi_dev_get_property(const struct acpi_device *adev, const char *name, acpi_object_type type, const union acpi_object **obj); int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, const char *name, size_t index, size_t num_args, struct fwnode_reference_args *args); static inline int acpi_node_get_property_reference( const struct fwnode_handle *fwnode, const char *name, size_t index, struct fwnode_reference_args *args) { return __acpi_node_get_property_reference(fwnode, name, index, NR_FWNODE_REFERENCE_ARGS, args); } static inline bool acpi_dev_has_props(const struct acpi_device *adev) { return !list_empty(&adev->data.properties); } struct acpi_device_properties * acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, const union acpi_object *properties); int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, void **valptr); int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val); int acpi_node_prop_read(const struct fwnode_handle *fwnode, const char *propname, enum dev_prop_type proptype, void *val, size_t nval); int acpi_dev_prop_read(const struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val, size_t nval); struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, struct fwnode_handle *child); struct fwnode_handle *acpi_node_get_parent(const struct fwnode_handle *fwnode); struct acpi_probe_entry; typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, struct acpi_probe_entry *); #define ACPI_TABLE_ID_LEN 5 /** * struct acpi_probe_entry - boot-time probing entry * @id: ACPI table name * @type: Optional subtable type to match * (if @id contains subtables) * @subtable_valid: Optional callback to check the validity of * the subtable * @probe_table: Callback to the driver being probed when table * match is successful * @probe_subtbl: Callback to the driver being probed when table and * subtable match (and optional callback is successful) * @driver_data: Sideband data provided back to the driver */ struct acpi_probe_entry { __u8 id[ACPI_TABLE_ID_LEN]; __u8 type; acpi_probe_entry_validate_subtbl subtable_valid; union { acpi_tbl_table_handler probe_table; acpi_tbl_entry_handler probe_subtbl; }; kernel_ulong_t driver_data; }; #define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \ static const struct acpi_probe_entry __acpi_probe_##name \ __used __section(__##table##_acpi_probe_table) \ = { \ .id = table_id, \ .type = subtable, \ .subtable_valid = valid, \ .probe_table = (acpi_tbl_table_handler)fn, \ .driver_data = data, \ } #define ACPI_PROBE_TABLE(name) __##name##_acpi_probe_table #define ACPI_PROBE_TABLE_END(name) __##name##_acpi_probe_table_end int __acpi_probe_device_table(struct acpi_probe_entry *start, int nr); #define acpi_probe_device_table(t) \ ({ \ extern struct acpi_probe_entry ACPI_PROBE_TABLE(t), \ ACPI_PROBE_TABLE_END(t); \ __acpi_probe_device_table(&ACPI_PROBE_TABLE(t), \ (&ACPI_PROBE_TABLE_END(t) - \ &ACPI_PROBE_TABLE(t))); \ }) #else static inline int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, const union acpi_object **obj) { return -ENXIO; } static inline int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, const char *name, size_t index, size_t num_args, struct fwnode_reference_args *args) { return -ENXIO; } static inline int acpi_node_get_property_reference(const struct fwnode_handle *fwnode, const char *name, size_t index, struct fwnode_reference_args *args) { return -ENXIO; } static inline int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, void **valptr) { return -ENXIO; } static inline int acpi_dev_prop_get(const struct acpi_device *adev, const char *propname, void **valptr) { return -ENXIO; } static inline int acpi_dev_prop_read_single(const struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val) { return -ENXIO; } static inline int acpi_node_prop_read(const struct fwnode_handle *fwnode, const char *propname, enum dev_prop_type proptype, void *val, size_t nval) { return -ENXIO; } static inline int acpi_dev_prop_read(const struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val, size_t nval) { return -ENXIO; } static inline struct fwnode_handle * acpi_get_next_subnode(const struct fwnode_handle *fwnode, struct fwnode_handle *child) { return NULL; } static inline struct fwnode_handle * acpi_node_get_parent(const struct fwnode_handle *fwnode) { return NULL; } static inline struct fwnode_handle * acpi_graph_get_next_endpoint(const struct fwnode_handle *fwnode, struct fwnode_handle *prev) { return ERR_PTR(-ENXIO); } static inline int acpi_graph_get_remote_endpoint(const struct fwnode_handle *fwnode, struct fwnode_handle **remote, struct fwnode_handle **port, struct fwnode_handle **endpoint) { return -ENXIO; } #define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \ static const void * __acpi_table_##name[] \ __attribute__((unused)) \ = { (void *) table_id, \ (void *) subtable, \ (void *) valid, \ (void *) fn, \ (void *) data } #define acpi_probe_device_table(t) ({ int __r = 0; __r;}) #endif #ifdef CONFIG_ACPI_TABLE_UPGRADE void acpi_table_upgrade(void); #else static inline void acpi_table_upgrade(void) { } #endif #if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_WATCHDOG) extern bool acpi_has_watchdog(void); #else static inline bool acpi_has_watchdog(void) { return false; } #endif #ifdef CONFIG_ACPI_SPCR_TABLE extern bool qdf2400_e44_present; int acpi_parse_spcr(bool enable_earlycon, bool enable_console); #else static inline int acpi_parse_spcr(bool enable_earlycon, bool enable_console) { return 0; } #endif #if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI) int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res); #else static inline int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res) { return -EINVAL; } #endif #ifdef CONFIG_ACPI_LPIT int lpit_read_residency_count_address(u64 *address); #else static inline int lpit_read_residency_count_address(u64 *address) { return -EINVAL; } #endif #ifdef CONFIG_ACPI_PPTT int acpi_pptt_cpu_is_thread(unsigned int cpu); int find_acpi_cpu_topology(unsigned int cpu, int level); int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); int find_acpi_cpu_cache_topology(unsigned int cpu, int level); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { return -EINVAL; } static inline int find_acpi_cpu_topology(unsigned int cpu, int level) { return -EINVAL; } static inline int find_acpi_cpu_topology_package(unsigned int cpu) { return -EINVAL; } static inline int find_acpi_cpu_topology_hetero_id(unsigned int cpu) { return -EINVAL; } static inline int find_acpi_cpu_cache_topology(unsigned int cpu, int level) { return -EINVAL; } #endif #ifdef CONFIG_ACPI extern int acpi_platform_notify(struct device *dev, enum kobject_action action); #else static inline int acpi_platform_notify(struct device *dev, enum kobject_action action) { return 0; } #endif #endif /*_LINUX_ACPI_H*/ ================================================ FILE: t/tree/include/linux/apm_bios.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Include file for the interface to an APM BIOS * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au) */ #ifndef _LINUX_APM_H #define _LINUX_APM_H #include #define APM_CS (GDT_ENTRY_APMBIOS_BASE * 8) #define APM_CS_16 (APM_CS + 8) #define APM_DS (APM_CS_16 + 8) /* Results of APM Installation Check */ #define APM_16_BIT_SUPPORT 0x0001 #define APM_32_BIT_SUPPORT 0x0002 #define APM_IDLE_SLOWS_CLOCK 0x0004 #define APM_BIOS_DISABLED 0x0008 #define APM_BIOS_DISENGAGED 0x0010 /* * Data for APM that is persistent across module unload/load */ struct apm_info { struct apm_bios_info bios; unsigned short connection_version; int get_power_status_broken; int get_power_status_swabinminutes; int allow_ints; int forbid_idle; int realmode_power_off; int disabled; }; /* * The APM function codes */ #define APM_FUNC_INST_CHECK 0x5300 #define APM_FUNC_REAL_CONN 0x5301 #define APM_FUNC_16BIT_CONN 0x5302 #define APM_FUNC_32BIT_CONN 0x5303 #define APM_FUNC_DISCONN 0x5304 #define APM_FUNC_IDLE 0x5305 #define APM_FUNC_BUSY 0x5306 #define APM_FUNC_SET_STATE 0x5307 #define APM_FUNC_ENABLE_PM 0x5308 #define APM_FUNC_RESTORE_BIOS 0x5309 #define APM_FUNC_GET_STATUS 0x530a #define APM_FUNC_GET_EVENT 0x530b #define APM_FUNC_GET_STATE 0x530c #define APM_FUNC_ENABLE_DEV_PM 0x530d #define APM_FUNC_VERSION 0x530e #define APM_FUNC_ENGAGE_PM 0x530f #define APM_FUNC_GET_CAP 0x5310 #define APM_FUNC_RESUME_TIMER 0x5311 #define APM_FUNC_RESUME_ON_RING 0x5312 #define APM_FUNC_TIMER 0x5313 /* * Function code for APM_FUNC_RESUME_TIMER */ #define APM_FUNC_DISABLE_TIMER 0 #define APM_FUNC_GET_TIMER 1 #define APM_FUNC_SET_TIMER 2 /* * Function code for APM_FUNC_RESUME_ON_RING */ #define APM_FUNC_DISABLE_RING 0 #define APM_FUNC_ENABLE_RING 1 #define APM_FUNC_GET_RING 2 /* * Function code for APM_FUNC_TIMER_STATUS */ #define APM_FUNC_TIMER_DISABLE 0 #define APM_FUNC_TIMER_ENABLE 1 #define APM_FUNC_TIMER_GET 2 /* * in arch/i386/kernel/setup.c */ extern struct apm_info apm_info; /* * This is the "All Devices" ID communicated to the BIOS */ #define APM_DEVICE_BALL ((apm_info.connection_version > 0x0100) ? \ APM_DEVICE_ALL : APM_DEVICE_OLD_ALL) #endif /* LINUX_APM_H */ ================================================ FILE: t/tree/include/linux/assoc_array.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Generic associative array implementation. * * See Documentation/core-api/assoc_array.rst for information. * * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #ifndef _LINUX_ASSOC_ARRAY_H #define _LINUX_ASSOC_ARRAY_H #ifdef CONFIG_ASSOCIATIVE_ARRAY #include #define ASSOC_ARRAY_KEY_CHUNK_SIZE BITS_PER_LONG /* Key data retrieved in chunks of this size */ /* * Generic associative array. */ struct assoc_array { struct assoc_array_ptr *root; /* The node at the root of the tree */ unsigned long nr_leaves_on_tree; }; /* * Operations on objects and index keys for use by array manipulation routines. */ struct assoc_array_ops { /* Method to get a chunk of an index key from caller-supplied data */ unsigned long (*get_key_chunk)(const void *index_key, int level); /* Method to get a piece of an object's index key */ unsigned long (*get_object_key_chunk)(const void *object, int level); /* Is this the object we're looking for? */ bool (*compare_object)(const void *object, const void *index_key); /* How different is an object from an index key, to a bit position in * their keys? (or -1 if they're the same) */ int (*diff_objects)(const void *object, const void *index_key); /* Method to free an object. */ void (*free_object)(void *object); }; /* * Access and manipulation functions. */ struct assoc_array_edit; static inline void assoc_array_init(struct assoc_array *array) { array->root = NULL; array->nr_leaves_on_tree = 0; } extern int assoc_array_iterate(const struct assoc_array *array, int (*iterator)(const void *object, void *iterator_data), void *iterator_data); extern void *assoc_array_find(const struct assoc_array *array, const struct assoc_array_ops *ops, const void *index_key); extern void assoc_array_destroy(struct assoc_array *array, const struct assoc_array_ops *ops); extern struct assoc_array_edit *assoc_array_insert(struct assoc_array *array, const struct assoc_array_ops *ops, const void *index_key, void *object); extern void assoc_array_insert_set_object(struct assoc_array_edit *edit, void *object); extern struct assoc_array_edit *assoc_array_delete(struct assoc_array *array, const struct assoc_array_ops *ops, const void *index_key); extern struct assoc_array_edit *assoc_array_clear(struct assoc_array *array, const struct assoc_array_ops *ops); extern void assoc_array_apply_edit(struct assoc_array_edit *edit); extern void assoc_array_cancel_edit(struct assoc_array_edit *edit); extern int assoc_array_gc(struct assoc_array *array, const struct assoc_array_ops *ops, bool (*iterator)(void *object, void *iterator_data), void *iterator_data); #endif /* CONFIG_ASSOCIATIVE_ARRAY */ #endif /* _LINUX_ASSOC_ARRAY_H */ ================================================ FILE: t/tree/include/linux/cred.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Credentials management - see Documentation/security/credentials.rst * * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #ifndef _LINUX_CRED_H #define _LINUX_CRED_H #include #include #include #include #include #include #include struct cred; struct inode; /* * COW Supplementary groups list */ struct group_info { atomic_t usage; int ngroups; kgid_t gid[0]; } __randomize_layout; /** * get_group_info - Get a reference to a group info structure * @group_info: The group info to reference * * This gets a reference to a set of supplementary groups. * * If the caller is accessing a task's credentials, they must hold the RCU read * lock when reading. */ static inline struct group_info *get_group_info(struct group_info *gi) { atomic_inc(&gi->usage); return gi; } /** * put_group_info - Release a reference to a group info structure * @group_info: The group info to release */ #define put_group_info(group_info) \ do { \ if (atomic_dec_and_test(&(group_info)->usage)) \ groups_free(group_info); \ } while (0) extern struct group_info init_groups; #ifdef CONFIG_MULTIUSER extern struct group_info *groups_alloc(int); extern void groups_free(struct group_info *); extern int in_group_p(kgid_t); extern int in_egroup_p(kgid_t); extern int groups_search(const struct group_info *, kgid_t); extern int set_current_groups(struct group_info *); extern void set_groups(struct cred *, struct group_info *); extern bool may_setgroups(void); extern void groups_sort(struct group_info *); #else static inline void groups_free(struct group_info *group_info) { } static inline int in_group_p(kgid_t grp) { return 1; } static inline int in_egroup_p(kgid_t grp) { return 1; } static inline int groups_search(const struct group_info *group_info, kgid_t grp) { return 1; } #endif /* * The security context of a task * * The parts of the context break down into two categories: * * (1) The objective context of a task. These parts are used when some other * task is attempting to affect this one. * * (2) The subjective context. These details are used when the task is acting * upon another object, be that a file, a task, a key or whatever. * * Note that some members of this structure belong to both categories - the * LSM security pointer for instance. * * A task has two security pointers. task->real_cred points to the objective * context that defines that task's actual details. The objective part of this * context is used whenever that task is acted upon. * * task->cred points to the subjective context that defines the details of how * that task is going to act upon another object. This may be overridden * temporarily to point to another security context, but normally points to the * same context as task->real_cred. */ struct cred { atomic_t usage; #ifdef CONFIG_DEBUG_CREDENTIALS atomic_t subscribers; /* number of processes subscribed */ void *put_addr; unsigned magic; #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ kernel_cap_t cap_ambient; /* Ambient capability set */ #ifdef CONFIG_KEYS unsigned char jit_keyring; /* default keyring to attach requested * keys to */ struct key *session_keyring; /* keyring inherited over fork */ struct key *process_keyring; /* keyring private to this process */ struct key *thread_keyring; /* keyring private to this thread */ struct key *request_key_auth; /* assumed request_key authority */ #endif #ifdef CONFIG_SECURITY void *security; /* subjective LSM security */ #endif struct user_struct *user; /* real user ID subscription */ struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ /* RCU deletion */ union { int non_rcu; /* Can we skip RCU deletion? */ struct rcu_head rcu; /* RCU deletion hook */ }; } __randomize_layout; extern void __put_cred(struct cred *); extern void exit_creds(struct task_struct *); extern int copy_creds(struct task_struct *, unsigned long); extern const struct cred *get_task_cred(struct task_struct *); extern struct cred *cred_alloc_blank(void); extern struct cred *prepare_creds(void); extern struct cred *prepare_exec_creds(void); extern int commit_creds(struct cred *); extern void abort_creds(struct cred *); extern const struct cred *override_creds(const struct cred *); extern void revert_creds(const struct cred *); extern struct cred *prepare_kernel_cred(struct task_struct *); extern int change_create_files_as(struct cred *, struct inode *); extern int set_security_override(struct cred *, u32); extern int set_security_override_from_ctx(struct cred *, const char *); extern int set_create_files_as(struct cred *, struct inode *); extern int cred_fscmp(const struct cred *, const struct cred *); extern void __init cred_init(void); /* * check for validity of credentials */ #ifdef CONFIG_DEBUG_CREDENTIALS extern void __invalid_creds(const struct cred *, const char *, unsigned); extern void __validate_process_creds(struct task_struct *, const char *, unsigned); extern bool creds_are_invalid(const struct cred *cred); static inline void __validate_creds(const struct cred *cred, const char *file, unsigned line) { if (unlikely(creds_are_invalid(cred))) __invalid_creds(cred, file, line); } #define validate_creds(cred) \ do { \ __validate_creds((cred), __FILE__, __LINE__); \ } while(0) #define validate_process_creds() \ do { \ __validate_process_creds(current, __FILE__, __LINE__); \ } while(0) extern void validate_creds_for_do_exit(struct task_struct *); #else static inline void validate_creds(const struct cred *cred) { } static inline void validate_creds_for_do_exit(struct task_struct *tsk) { } static inline void validate_process_creds(void) { } #endif static inline bool cap_ambient_invariant_ok(const struct cred *cred) { return cap_issubset(cred->cap_ambient, cap_intersect(cred->cap_permitted, cred->cap_inheritable)); } /** * get_new_cred - Get a reference on a new set of credentials * @cred: The new credentials to reference * * Get a reference on the specified set of new credentials. The caller must * release the reference. */ static inline struct cred *get_new_cred(struct cred *cred) { atomic_inc(&cred->usage); return cred; } /** * get_cred - Get a reference on a set of credentials * @cred: The credentials to reference * * Get a reference on the specified set of credentials. The caller must * release the reference. If %NULL is passed, it is returned with no action. * * This is used to deal with a committed set of credentials. Although the * pointer is const, this will temporarily discard the const and increment the * usage count. The purpose of this is to attempt to catch at compile time the * accidental alteration of a set of credentials that should be considered * immutable. */ static inline const struct cred *get_cred(const struct cred *cred) { struct cred *nonconst_cred = (struct cred *) cred; if (!cred) return cred; validate_creds(cred); nonconst_cred->non_rcu = 0; return get_new_cred(nonconst_cred); } static inline const struct cred *get_cred_rcu(const struct cred *cred) { struct cred *nonconst_cred = (struct cred *) cred; if (!cred) return NULL; if (!atomic_inc_not_zero(&nonconst_cred->usage)) return NULL; validate_creds(cred); nonconst_cred->non_rcu = 0; return cred; } /** * put_cred - Release a reference to a set of credentials * @cred: The credentials to release * * Release a reference to a set of credentials, deleting them when the last ref * is released. If %NULL is passed, nothing is done. * * This takes a const pointer to a set of credentials because the credentials * on task_struct are attached by const pointers to prevent accidental * alteration of otherwise immutable credential sets. */ static inline void put_cred(const struct cred *_cred) { struct cred *cred = (struct cred *) _cred; if (cred) { validate_creds(cred); if (atomic_dec_and_test(&(cred)->usage)) __put_cred(cred); } } /** * current_cred - Access the current task's subjective credentials * * Access the subjective credentials of the current task. RCU-safe, * since nobody else can modify it. */ #define current_cred() \ rcu_dereference_protected(current->cred, 1) /** * current_real_cred - Access the current task's objective credentials * * Access the objective credentials of the current task. RCU-safe, * since nobody else can modify it. */ #define current_real_cred() \ rcu_dereference_protected(current->real_cred, 1) /** * __task_cred - Access a task's objective credentials * @task: The task to query * * Access the objective credentials of a task. The caller must hold the RCU * readlock. * * The result of this function should not be passed directly to get_cred(); * rather get_task_cred() should be used instead. */ #define __task_cred(task) \ rcu_dereference((task)->real_cred) /** * get_current_cred - Get the current task's subjective credentials * * Get the subjective credentials of the current task, pinning them so that * they can't go away. Accessing the current task's credentials directly is * not permitted. */ #define get_current_cred() \ (get_cred(current_cred())) /** * get_current_user - Get the current task's user_struct * * Get the user record of the current task, pinning it so that it can't go * away. */ #define get_current_user() \ ({ \ struct user_struct *__u; \ const struct cred *__cred; \ __cred = current_cred(); \ __u = get_uid(__cred->user); \ __u; \ }) /** * get_current_groups - Get the current task's supplementary group list * * Get the supplementary group list of the current task, pinning it so that it * can't go away. */ #define get_current_groups() \ ({ \ struct group_info *__groups; \ const struct cred *__cred; \ __cred = current_cred(); \ __groups = get_group_info(__cred->group_info); \ __groups; \ }) #define task_cred_xxx(task, xxx) \ ({ \ __typeof__(((struct cred *)NULL)->xxx) ___val; \ rcu_read_lock(); \ ___val = __task_cred((task))->xxx; \ rcu_read_unlock(); \ ___val; \ }) #define task_uid(task) (task_cred_xxx((task), uid)) #define task_euid(task) (task_cred_xxx((task), euid)) #define current_cred_xxx(xxx) \ ({ \ current_cred()->xxx; \ }) #define current_uid() (current_cred_xxx(uid)) #define current_gid() (current_cred_xxx(gid)) #define current_euid() (current_cred_xxx(euid)) #define current_egid() (current_cred_xxx(egid)) #define current_suid() (current_cred_xxx(suid)) #define current_sgid() (current_cred_xxx(sgid)) #define current_fsuid() (current_cred_xxx(fsuid)) #define current_fsgid() (current_cred_xxx(fsgid)) #define current_cap() (current_cred_xxx(cap_effective)) #define current_user() (current_cred_xxx(user)) extern struct user_namespace init_user_ns; #ifdef CONFIG_USER_NS #define current_user_ns() (current_cred_xxx(user_ns)) #else static inline struct user_namespace *current_user_ns(void) { return &init_user_ns; } #endif #define current_uid_gid(_uid, _gid) \ do { \ const struct cred *__cred; \ __cred = current_cred(); \ *(_uid) = __cred->uid; \ *(_gid) = __cred->gid; \ } while(0) #define current_euid_egid(_euid, _egid) \ do { \ const struct cred *__cred; \ __cred = current_cred(); \ *(_euid) = __cred->euid; \ *(_egid) = __cred->egid; \ } while(0) #define current_fsuid_fsgid(_fsuid, _fsgid) \ do { \ const struct cred *__cred; \ __cred = current_cred(); \ *(_fsuid) = __cred->fsuid; \ *(_fsgid) = __cred->fsgid; \ } while(0) #endif /* _LINUX_CRED_H */ ================================================ FILE: t/tree/include/linux/i2c-smbus.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * i2c-smbus.h - SMBus extensions to the I2C protocol * * Copyright (C) 2010 Jean Delvare */ #ifndef _LINUX_I2C_SMBUS_H #define _LINUX_I2C_SMBUS_H #include #include #include /** * i2c_smbus_alert_setup - platform data for the smbus_alert i2c client * @alert_edge_triggered: whether the alert interrupt is edge (1) or level (0) * triggered * @irq: IRQ number, if the smbus_alert driver should take care of interrupt * handling * * If irq is not specified, the smbus_alert driver doesn't take care of * interrupt handling. In that case it is up to the I2C bus driver to either * handle the interrupts or to poll for alerts. * * If irq is specified then it it crucial that alert_edge_triggered is * properly set. */ struct i2c_smbus_alert_setup { int irq; }; struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup); int i2c_handle_smbus_alert(struct i2c_client *ara); #if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) int of_i2c_setup_smbus_alert(struct i2c_adapter *adap); #else static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap) { return 0; } #endif #endif /* _LINUX_I2C_SMBUS_H */ ================================================ FILE: t/tree/include/linux/i2c.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * i2c.h - definitions for the Linux i2c bus interface * Copyright (C) 1995-2000 Simon G. Vogl * Copyright (C) 2013-2019 Wolfram Sang * * With some changes from Kyösti Mälkki and * Frodo Looijaard */ #ifndef _LINUX_I2C_H #define _LINUX_I2C_H #include /* for acpi_handle */ #include #include /* for struct device */ #include /* for completion */ #include #include #include /* for Host Notify IRQ */ #include /* for struct device_node */ #include /* for swab16 */ #include extern struct bus_type i2c_bus_type; extern struct device_type i2c_adapter_type; extern struct device_type i2c_client_type; /* --- General options ------------------------------------------------ */ struct i2c_msg; struct i2c_algorithm; struct i2c_adapter; struct i2c_client; struct i2c_driver; struct i2c_device_identity; union i2c_smbus_data; struct i2c_board_info; enum i2c_slave_event; typedef int (*i2c_slave_cb_t)(struct i2c_client *client, enum i2c_slave_event event, u8 *val); struct module; struct property_entry; #if IS_ENABLED(CONFIG_I2C) /* * The master routines are the ones normally used to transmit data to devices * on a bus (or read from them). Apart from two basic transfer functions to * transmit one message at a time, a more complex version can be used to * transmit an arbitrary number of messages without interruption. * @count must be be less than 64k since msg.len is u16. */ extern int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, int count, u16 flags); /** * i2c_master_recv - issue a single I2C message in master receive mode * @client: Handle to slave device * @buf: Where to store data read from slave * @count: How many bytes to read, must be less than 64k since msg.len is u16 * * Returns negative errno, or else the number of bytes read. */ static inline int i2c_master_recv(const struct i2c_client *client, char *buf, int count) { return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD); }; /** * i2c_master_recv_dmasafe - issue a single I2C message in master receive mode * using a DMA safe buffer * @client: Handle to slave device * @buf: Where to store data read from slave, must be safe to use with DMA * @count: How many bytes to read, must be less than 64k since msg.len is u16 * * Returns negative errno, or else the number of bytes read. */ static inline int i2c_master_recv_dmasafe(const struct i2c_client *client, char *buf, int count) { return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD | I2C_M_DMA_SAFE); }; /** * i2c_master_send - issue a single I2C message in master transmit mode * @client: Handle to slave device * @buf: Data that will be written to the slave * @count: How many bytes to write, must be less than 64k since msg.len is u16 * * Returns negative errno, or else the number of bytes written. */ static inline int i2c_master_send(const struct i2c_client *client, const char *buf, int count) { return i2c_transfer_buffer_flags(client, (char *)buf, count, 0); }; /** * i2c_master_send_dmasafe - issue a single I2C message in master transmit mode * using a DMA safe buffer * @client: Handle to slave device * @buf: Data that will be written to the slave, must be safe to use with DMA * @count: How many bytes to write, must be less than 64k since msg.len is u16 * * Returns negative errno, or else the number of bytes written. */ static inline int i2c_master_send_dmasafe(const struct i2c_client *client, const char *buf, int count) { return i2c_transfer_buffer_flags(client, (char *)buf, count, I2C_M_DMA_SAFE); }; /* Transfer num messages. */ extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); /* Unlocked flavor */ extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); /* This is the very generalized SMBus access routine. You probably do not want to use this, though; one of the functions below may be much easier, and probably just as fast. Note that we use i2c_adapter here, because you do not need a specific smbus adapter to call this function. */ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data); /* Unlocked flavor */ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data); /* Now follow the 'nice' access routines. These also document the calling conventions of i2c_smbus_xfer. */ extern s32 i2c_smbus_read_byte(const struct i2c_client *client); extern s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value); extern s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command); extern s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value); extern s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command); extern s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value); static inline s32 i2c_smbus_read_word_swapped(const struct i2c_client *client, u8 command) { s32 value = i2c_smbus_read_word_data(client, command); return (value < 0) ? value : swab16(value); } static inline s32 i2c_smbus_write_word_swapped(const struct i2c_client *client, u8 command, u16 value) { return i2c_smbus_write_word_data(client, command, swab16(value)); } /* Returns the number of read bytes */ extern s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values); extern s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values); /* Returns the number of read bytes */ extern s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values); extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values); extern s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, u8 command, u8 length, u8 *values); int i2c_get_device_id(const struct i2c_client *client, struct i2c_device_identity *id); #endif /* I2C */ /** * struct i2c_device_identity - i2c client device identification * @manufacturer_id: 0 - 4095, database maintained by NXP * @part_id: 0 - 511, according to manufacturer * @die_revision: 0 - 7, according to manufacturer */ struct i2c_device_identity { u16 manufacturer_id; #define I2C_DEVICE_ID_NXP_SEMICONDUCTORS 0 #define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_1 1 #define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_2 2 #define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_3 3 #define I2C_DEVICE_ID_RAMTRON_INTERNATIONAL 4 #define I2C_DEVICE_ID_ANALOG_DEVICES 5 #define I2C_DEVICE_ID_STMICROELECTRONICS 6 #define I2C_DEVICE_ID_ON_SEMICONDUCTOR 7 #define I2C_DEVICE_ID_SPRINTEK_CORPORATION 8 #define I2C_DEVICE_ID_ESPROS_PHOTONICS_AG 9 #define I2C_DEVICE_ID_FUJITSU_SEMICONDUCTOR 10 #define I2C_DEVICE_ID_FLIR 11 #define I2C_DEVICE_ID_O2MICRO 12 #define I2C_DEVICE_ID_ATMEL 13 #define I2C_DEVICE_ID_NONE 0xffff u16 part_id; u8 die_revision; }; enum i2c_alert_protocol { I2C_PROTOCOL_SMBUS_ALERT, I2C_PROTOCOL_SMBUS_HOST_NOTIFY, }; /** * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) * @probe: Callback for device binding - soon to be deprecated * @probe_new: New callback for device binding * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown * @alert: Alert callback, for example for the SMBus alert protocol * @command: Callback for bus-wide signaling (optional) * @driver: Device driver model driver * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection * @address_list: The I2C addresses to probe (for detect) * @clients: List of detected clients we created (for i2c-core use only) * @disable_i2c_core_irq_mapping: Tell the i2c-core to not do irq-mapping * * The driver.owner field should be set to the module owner of this driver. * The driver.name field should be set to the name of this driver. * * For automatic device detection, both @detect and @address_list must * be defined. @class should also be set, otherwise only devices forced * with module parameters will be created. The detect function must * fill at least the name field of the i2c_board_info structure it is * handed upon successful detection, and possibly also the flags field. * * If @detect is missing, the driver will still work fine for enumerated * devices. Detected devices simply won't be supported. This is expected * for the many I2C/SMBus devices which can't be detected reliably, and * the ones which can always be enumerated in practice. * * The i2c_client structure which is handed to the @detect callback is * not a real i2c_client. It is initialized just enough so that you can * call i2c_smbus_read_byte_data and friends on it. Don't do anything * else with it. In particular, calling dev_dbg and friends on it is * not allowed. */ struct i2c_driver { unsigned int class; /* Standard driver model interfaces */ int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); int (*remove)(struct i2c_client *client); /* New driver model interface to aid the seamless removal of the * current probe()'s, more commonly unused than used second parameter. */ int (*probe_new)(struct i2c_client *client); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *client); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). * For the SMBus Host Notify protocol, the data corresponds to the * 16-bit payload data reported by the slave device acting as master. */ void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *client, struct i2c_board_info *info); const unsigned short *address_list; struct list_head clients; bool disable_i2c_core_irq_mapping; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) /** * struct i2c_client - represent an I2C slave device * @flags: see I2C_CLIENT_* for possible flags * @addr: Address used on the I2C bus connected to the parent adapter. * @name: Indicates the type of the device, usually a chip name that's * generic enough to hide second-sourcing and compatible revisions. * @adapter: manages the bus segment hosting this I2C device * @dev: Driver model device node for the slave. * @irq: indicates the IRQ generated by this device (if any) * @detected: member of an i2c_driver.clients list or i2c-core's * userspace_devices list * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter * calls it to pass on slave events to the slave driver. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour exposed to Linux is defined by the driver * managing the device. */ struct i2c_client { unsigned short flags; /* div., see below */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ /* Must equal I2C_M_TEN below */ #define I2C_CLIENT_SLAVE 0x20 /* we are the slave */ #define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */ #define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */ #define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ /* Must match I2C_M_STOP|IGNORE_NAK */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ int init_irq; /* irq set at initialization */ int irq; /* irq issued by device */ struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) extern struct i2c_client *i2c_verify_client(struct device *dev); extern struct i2c_adapter *i2c_verify_adapter(struct device *dev); extern const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client); static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj) { struct device * const dev = container_of(kobj, struct device, kobj); return to_i2c_client(dev); } static inline void *i2c_get_clientdata(const struct i2c_client *dev) { return dev_get_drvdata(&dev->dev); } static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) { dev_set_drvdata(&dev->dev, data); } /* I2C slave support */ #if IS_ENABLED(CONFIG_I2C_SLAVE) enum i2c_slave_event { I2C_SLAVE_READ_REQUESTED, I2C_SLAVE_WRITE_REQUESTED, I2C_SLAVE_READ_PROCESSED, I2C_SLAVE_WRITE_RECEIVED, I2C_SLAVE_STOP, }; extern int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb); extern int i2c_slave_unregister(struct i2c_client *client); extern bool i2c_detect_slave_mode(struct device *dev); static inline int i2c_slave_event(struct i2c_client *client, enum i2c_slave_event event, u8 *val) { return client->slave_cb(client, event, val); } #else static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } #endif /** * struct i2c_board_info - template for device creation * @type: chip type, to initialize i2c_client.name * @flags: to initialize i2c_client.flags * @addr: stored in i2c_client.addr * @dev_name: Overrides the default - dev_name if set * @platform_data: stored in i2c_client.dev.platform_data * @of_node: pointer to OpenFirmware device node * @fwnode: device node supplied by the platform firmware * @properties: additional device properties for the device * @resources: resources associated with the device * @num_resources: number of resources in the @resources array * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's * a device at a given address. Drivers commonly need more information than * that, such as chip type, configuration, associated IRQ, and so on. * * i2c_board_info is used to build tables of information listing I2C devices * that are present. This information is used to grow the driver model tree. * For mainboards this is done statically using i2c_register_board_info(); * bus numbers identify adapters that aren't yet available. For add-on boards, * i2c_new_device() does this dynamically with the adapter already known. */ struct i2c_board_info { char type[I2C_NAME_SIZE]; unsigned short flags; unsigned short addr; const char *dev_name; void *platform_data; struct device_node *of_node; struct fwnode_handle *fwnode; const struct property_entry *properties; const struct resource *resources; unsigned int num_resources; int irq; }; /** * I2C_BOARD_INFO - macro used to list an i2c device and its address * @dev_type: identifies the device type * @dev_addr: the device's address on the bus. * * This macro initializes essential fields of a struct i2c_board_info, * declaring what has been provided on a particular board. Optional * fields (such as associated irq, or device-specific platform_data) * are provided using conventional syntax. */ #define I2C_BOARD_INFO(dev_type, dev_addr) \ .type = dev_type, .addr = (dev_addr) #if IS_ENABLED(CONFIG_I2C) /* Add-on boards should register/unregister their devices; e.g. a board * with integrated I2C, a config eeprom, sensors, and a codec that's * used in conjunction with the primary hardware. */ extern struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info); extern struct i2c_client * i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info); /* If you don't know the exact address of an I2C device, use this variant * instead, which can probe for device presence in a list of possible * addresses. The "probe" callback function is optional. If it is provided, * it must return 1 on successful probe, 0 otherwise. If it is not provided, * a default probing method is used. */ extern struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap, struct i2c_board_info *info, unsigned short const *addr_list, int (*probe)(struct i2c_adapter *adap, unsigned short addr)); /* Common custom probe functions */ extern int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr); /* For devices that use several addresses, use i2c_new_dummy() to make * client handles for the extra addresses. */ extern struct i2c_client * i2c_new_dummy(struct i2c_adapter *adap, u16 address); extern struct i2c_client * i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address); extern struct i2c_client * devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adap, u16 address); extern struct i2c_client * i2c_new_ancillary_device(struct i2c_client *client, const char *name, u16 default_addr); extern void i2c_unregister_device(struct i2c_client *client); #endif /* I2C */ /* Mainboard arch_initcall() code should register all its I2C devices. * This is done at arch_initcall time, before declaring any i2c adapters. * Modules for add-on boards must use other calls. */ #ifdef CONFIG_I2C_BOARDINFO extern int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned n); #else static inline int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned n) { return 0; } #endif /* I2C_BOARDINFO */ /** * struct i2c_algorithm - represent I2C transfer method * @master_xfer: Issue a set of i2c transactions to the given I2C adapter * defined by the msgs array, with num messages available to transfer via * the adapter specified by adap. * @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context * so e.g. PMICs can be accessed very late before shutdown. Optional. * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this * is not present, then the bus layer will try and convert the SMBus calls * into I2C transfers instead. * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context * so e.g. PMICs can be accessed very late before shutdown. Optional. * @functionality: Return the flags that this algorithm/adapter pair supports * from the I2C_FUNC_* flags. * @reg_slave: Register given client to I2C slave mode of this adapter * @unreg_slave: Unregister given client from I2C slave mode of this adapter * * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. * * The return codes from the @master_xfer{_atomic} fields should indicate the * type of error code that occurred during the transfer, as documented in the * Kernel Documentation file Documentation/i2c/fault-codes.rst. */ struct i2c_algorithm { /* * If an adapter algorithm can't do I2C-level access, set master_xfer * to NULL. If an adapter algorithm can do SMBus access, set * smbus_xfer. If set to NULL, the SMBus protocol is simulated * using common I2C messages. * * master_xfer should return the number of messages successfully * processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*master_xfer_atomic)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality)(struct i2c_adapter *adap); #if IS_ENABLED(CONFIG_I2C_SLAVE) int (*reg_slave)(struct i2c_client *client); int (*unreg_slave)(struct i2c_client *client); #endif }; /** * struct i2c_lock_operations - represent I2C locking operations * @lock_bus: Get exclusive access to an I2C bus segment * @trylock_bus: Try to get exclusive access to an I2C bus segment * @unlock_bus: Release exclusive access to an I2C bus segment * * The main operations are wrapped by i2c_lock_bus and i2c_unlock_bus. */ struct i2c_lock_operations { void (*lock_bus)(struct i2c_adapter *adapter, unsigned int flags); int (*trylock_bus)(struct i2c_adapter *adapter, unsigned int flags); void (*unlock_bus)(struct i2c_adapter *adapter, unsigned int flags); }; /** * struct i2c_timings - I2C timing information * @bus_freq_hz: the bus frequency in Hz * @scl_rise_ns: time SCL signal takes to rise in ns; t(r) in the I2C specification * @scl_fall_ns: time SCL signal takes to fall in ns; t(f) in the I2C specification * @scl_int_delay_ns: time IP core additionally needs to setup SCL in ns * @sda_fall_ns: time SDA signal takes to fall in ns; t(f) in the I2C specification * @sda_hold_ns: time IP core additionally needs to hold SDA in ns */ struct i2c_timings { u32 bus_freq_hz; u32 scl_rise_ns; u32 scl_fall_ns; u32 scl_int_delay_ns; u32 sda_fall_ns; u32 sda_hold_ns; }; /** * struct i2c_bus_recovery_info - I2C bus recovery information * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or * i2c_generic_scl_recovery(). * @get_scl: This gets current value of SCL line. Mandatory for generic SCL * recovery. Populated internally for generic GPIO recovery. * @set_scl: This sets/clears the SCL line. Mandatory for generic SCL recovery. * Populated internally for generic GPIO recovery. * @get_sda: This gets current value of SDA line. This or set_sda() is mandatory * for generic SCL recovery. Populated internally, if sda_gpio is a valid * GPIO, for generic GPIO recovery. * @set_sda: This sets/clears the SDA line. This or get_sda() is mandatory for * generic SCL recovery. Populated internally, if sda_gpio is a valid GPIO, * for generic GPIO recovery. * @get_bus_free: Returns the bus free state as seen from the IP core in case it * has a more complex internal logic than just reading SDA. Optional. * @prepare_recovery: This will be called before starting recovery. Platform may * configure padmux here for SDA/SCL line or something else they want. * @unprepare_recovery: This will be called after completing recovery. Platform * may configure padmux here for SDA/SCL line or something else they want. * @scl_gpiod: gpiod of the SCL line. Only required for GPIO recovery. * @sda_gpiod: gpiod of the SDA line. Only required for GPIO recovery. */ struct i2c_bus_recovery_info { int (*recover_bus)(struct i2c_adapter *adap); int (*get_scl)(struct i2c_adapter *adap); void (*set_scl)(struct i2c_adapter *adap, int val); int (*get_sda)(struct i2c_adapter *adap); void (*set_sda)(struct i2c_adapter *adap, int val); int (*get_bus_free)(struct i2c_adapter *adap); void (*prepare_recovery)(struct i2c_adapter *adap); void (*unprepare_recovery)(struct i2c_adapter *adap); /* gpio recovery */ struct gpio_desc *scl_gpiod; struct gpio_desc *sda_gpiod; }; int i2c_recover_bus(struct i2c_adapter *adap); /* Generic recovery routines */ int i2c_generic_scl_recovery(struct i2c_adapter *adap); /** * struct i2c_adapter_quirks - describe flaws of an i2c adapter * @flags: see I2C_AQ_* for possible flags and read below * @max_num_msgs: maximum number of messages per transfer * @max_write_len: maximum length of a write message * @max_read_len: maximum length of a read message * @max_comb_1st_msg_len: maximum length of the first msg in a combined message * @max_comb_2nd_msg_len: maximum length of the second msg in a combined message * * Note about combined messages: Some I2C controllers can only send one message * per transfer, plus something called combined message or write-then-read. * This is (usually) a small write message followed by a read message and * barely enough to access register based devices like EEPROMs. There is a flag * to support this mode. It implies max_num_msg = 2 and does the length checks * with max_comb_*_len because combined message mode usually has its own * limitations. Because of HW implementations, some controllers can actually do * write-then-anything or other variants. To support that, write-then-read has * been broken out into smaller bits like write-first and read-second which can * be combined as needed. */ struct i2c_adapter_quirks { u64 flags; int max_num_msgs; u16 max_write_len; u16 max_read_len; u16 max_comb_1st_msg_len; u16 max_comb_2nd_msg_len; }; /* enforce max_num_msgs = 2 and use max_comb_*_len for length checks */ #define I2C_AQ_COMB BIT(0) /* first combined message must be write */ #define I2C_AQ_COMB_WRITE_FIRST BIT(1) /* second combined message must be read */ #define I2C_AQ_COMB_READ_SECOND BIT(2) /* both combined messages must have the same target address */ #define I2C_AQ_COMB_SAME_ADDR BIT(3) /* convenience macro for typical write-then read case */ #define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \ I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR) /* clock stretching is not supported */ #define I2C_AQ_NO_CLK_STRETCH BIT(4) /* message cannot have length of 0 */ #define I2C_AQ_NO_ZERO_LEN_READ BIT(5) #define I2C_AQ_NO_ZERO_LEN_WRITE BIT(6) #define I2C_AQ_NO_ZERO_LEN (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE) /* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ const struct i2c_lock_operations *lock_ops; struct rt_mutex bus_lock; struct rt_mutex mux_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ unsigned long locked_flags; /* owned by the I2C core */ #define I2C_ALF_IS_SUSPENDED 0 #define I2C_ALF_SUSPEND_REPORTED 1 int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; struct irq_domain *host_notify_domain; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) static inline void *i2c_get_adapdata(const struct i2c_adapter *adap) { return dev_get_drvdata(&adap->dev); } static inline void i2c_set_adapdata(struct i2c_adapter *adap, void *data) { dev_set_drvdata(&adap->dev, data); } static inline struct i2c_adapter * i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) { #if IS_ENABLED(CONFIG_I2C_MUX) struct device *parent = adapter->dev.parent; if (parent != NULL && parent->type == &i2c_adapter_type) return to_i2c_adapter(parent); else #endif return NULL; } int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data)); /* Adapter locking functions, exported for shared pin cases */ #define I2C_LOCK_ROOT_ADAPTER BIT(0) #define I2C_LOCK_SEGMENT BIT(1) /** * i2c_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT * locks only this branch in the adapter tree */ static inline void i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { adapter->lock_ops->lock_bus(adapter, flags); } /** * i2c_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER tries to locks the root i2c adapter, * I2C_LOCK_SEGMENT tries to lock only this branch in the adapter tree * * Return: true if the I2C bus segment is locked, false otherwise */ static inline int i2c_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) { return adapter->lock_ops->trylock_bus(adapter, flags); } /** * i2c_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT * unlocks only this branch in the adapter tree */ static inline void i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) { adapter->lock_ops->unlock_bus(adapter, flags); } /** * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core * @adap: Adapter to mark as suspended * * When using this helper to mark an adapter as suspended, the core will reject * further transfers to this adapter. The usage of this helper is optional but * recommended for devices having distinct handlers for system suspend and * runtime suspend. More complex devices are free to implement custom solutions * to reject transfers when suspended. */ static inline void i2c_mark_adapter_suspended(struct i2c_adapter *adap) { i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); set_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); } /** * i2c_mark_adapter_resumed - Report resumed state of the adapter to the core * @adap: Adapter to mark as resumed * * When using this helper to mark an adapter as resumed, the core will allow * further transfers to this adapter. See also further notes to * @i2c_mark_adapter_suspended(). */ static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) { i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); clear_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); } /* i2c adapter classes (bitmask) */ #define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */ #define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */ #define I2C_CLASS_SPD (1<<7) /* Memory modules */ /* Warn users that the adapter doesn't support classes anymore */ #define I2C_CLASS_DEPRECATED (1<<8) /* Internal numbers to terminate lists */ #define I2C_CLIENT_END 0xfffeU /* Construct an I2C_CLIENT_END-terminated array of i2c addresses */ #define I2C_ADDRS(addr, addrs...) \ ((const unsigned short []){ addr, ## addrs, I2C_CLIENT_END }) /* ----- functions exported by i2c.o */ /* administration... */ #if IS_ENABLED(CONFIG_I2C) extern int i2c_add_adapter(struct i2c_adapter *adap); extern void i2c_del_adapter(struct i2c_adapter *adap); extern int i2c_add_numbered_adapter(struct i2c_adapter *adap); extern int i2c_register_driver(struct module *owner, struct i2c_driver *driver); extern void i2c_del_driver(struct i2c_driver *driver); /* use a define to avoid include chaining to get THIS_MODULE */ #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver) extern struct i2c_client *i2c_use_client(struct i2c_client *client); extern void i2c_release_client(struct i2c_client *client); /* call the i2c_client->command() of all attached clients with * the given arguments */ extern void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg); extern struct i2c_adapter *i2c_get_adapter(int nr); extern void i2c_put_adapter(struct i2c_adapter *adap); extern unsigned int i2c_adapter_depth(struct i2c_adapter *adapter); void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults); /* Return the functionality mask */ static inline u32 i2c_get_functionality(struct i2c_adapter *adap) { return adap->algo->functionality(adap); } /* Return 1 if adapter supports everything we need, 0 if not. */ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) { return (func & i2c_get_functionality(adap)) == func; } /** * i2c_check_quirks() - Function for checking the quirk flags in an i2c adapter * @adap: i2c adapter * @quirks: quirk flags * * Return: true if the adapter has all the specified quirk flags, false if not */ static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks) { if (!adap->quirks) return false; return (adap->quirks->flags & quirks) == quirks; } /* Return the adapter number for a specific adapter */ static inline int i2c_adapter_id(struct i2c_adapter *adap) { return adap->nr; } static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) { return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); } u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold); void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred); int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr); /** * module_i2c_driver() - Helper macro for registering a modular I2C driver * @__i2c_driver: i2c_driver struct * * Helper macro for I2C drivers which do not do anything special in module * init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ #define module_i2c_driver(__i2c_driver) \ module_driver(__i2c_driver, i2c_add_driver, \ i2c_del_driver) /** * builtin_i2c_driver() - Helper macro for registering a builtin I2C driver * @__i2c_driver: i2c_driver struct * * Helper macro for I2C drivers which do not do anything special in their * init. This eliminates a lot of boilerplate. Each driver may only * use this macro once, and calling it replaces device_initcall(). */ #define builtin_i2c_driver(__i2c_driver) \ builtin_driver(__i2c_driver, i2c_add_driver) #endif /* I2C */ #if IS_ENABLED(CONFIG_OF) /* must call put_device() when done with returned i2c_client device */ extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); /* must call put_device() when done with returned i2c_adapter device */ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node); /* must call i2c_put_adapter() when done with returned i2c_adapter device */ struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node); extern const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches, struct i2c_client *client); int of_i2c_get_board_info(struct device *dev, struct device_node *node, struct i2c_board_info *info); #else static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) { return NULL; } static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) { return NULL; } static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) { return NULL; } static inline const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches, struct i2c_client *client) { return NULL; } static inline int of_i2c_get_board_info(struct device *dev, struct device_node *node, struct i2c_board_info *info) { return -ENOTSUPP; } #endif /* CONFIG_OF */ struct acpi_resource; struct acpi_resource_i2c_serialbus; #if IS_ENABLED(CONFIG_ACPI) bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, struct acpi_resource_i2c_serialbus **i2c); u32 i2c_acpi_find_bus_speed(struct device *dev); struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, struct i2c_board_info *info); struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle); #else static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, struct acpi_resource_i2c_serialbus **i2c) { return false; } static inline u32 i2c_acpi_find_bus_speed(struct device *dev) { return 0; } static inline struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, struct i2c_board_info *info) { return NULL; } static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) { return NULL; } #endif /* CONFIG_ACPI */ #endif /* _LINUX_I2C_H */ ================================================ FILE: t/tree/include/linux/key.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Authentication token and access key management * * Copyright (C) 2004, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * See Documentation/security/keys/core.rst for information on keys/keyrings. */ #ifndef _LINUX_KEY_H #define _LINUX_KEY_H #include #include #include #include #include #include #include #include #include #include #ifdef __KERNEL__ #include /* key handle serial number */ typedef int32_t key_serial_t; /* key handle permissions mask */ typedef uint32_t key_perm_t; struct key; struct net; #ifdef CONFIG_KEYS #undef KEY_DEBUGGING #define KEY_POS_VIEW 0x01000000 /* possessor can view a key's attributes */ #define KEY_POS_READ 0x02000000 /* possessor can read key payload / view keyring */ #define KEY_POS_WRITE 0x04000000 /* possessor can update key payload / add link to keyring */ #define KEY_POS_SEARCH 0x08000000 /* possessor can find a key in search / search a keyring */ #define KEY_POS_LINK 0x10000000 /* possessor can create a link to a key/keyring */ #define KEY_POS_SETATTR 0x20000000 /* possessor can set key attributes */ #define KEY_POS_ALL 0x3f000000 #define KEY_USR_VIEW 0x00010000 /* user permissions... */ #define KEY_USR_READ 0x00020000 #define KEY_USR_WRITE 0x00040000 #define KEY_USR_SEARCH 0x00080000 #define KEY_USR_LINK 0x00100000 #define KEY_USR_SETATTR 0x00200000 #define KEY_USR_ALL 0x003f0000 #define KEY_GRP_VIEW 0x00000100 /* group permissions... */ #define KEY_GRP_READ 0x00000200 #define KEY_GRP_WRITE 0x00000400 #define KEY_GRP_SEARCH 0x00000800 #define KEY_GRP_LINK 0x00001000 #define KEY_GRP_SETATTR 0x00002000 #define KEY_GRP_ALL 0x00003f00 #define KEY_OTH_VIEW 0x00000001 /* third party permissions... */ #define KEY_OTH_READ 0x00000002 #define KEY_OTH_WRITE 0x00000004 #define KEY_OTH_SEARCH 0x00000008 #define KEY_OTH_LINK 0x00000010 #define KEY_OTH_SETATTR 0x00000020 #define KEY_OTH_ALL 0x0000003f #define KEY_PERM_UNDEF 0xffffffff struct seq_file; struct user_struct; struct signal_struct; struct cred; struct key_type; struct key_owner; struct key_tag; struct keyring_list; struct keyring_name; struct key_tag { struct rcu_head rcu; refcount_t usage; bool removed; /* T when subject removed */ }; struct keyring_index_key { /* [!] If this structure is altered, the union in struct key must change too! */ unsigned long hash; /* Hash value */ union { struct { #ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */ u16 desc_len; char desc[sizeof(long) - 2]; /* First few chars of description */ #else char desc[sizeof(long) - 2]; /* First few chars of description */ u16 desc_len; #endif }; unsigned long x; }; struct key_type *type; struct key_tag *domain_tag; /* Domain of operation */ const char *description; }; union key_payload { void __rcu *rcu_data0; void *data[4]; }; /*****************************************************************************/ /* * key reference with possession attribute handling * * NOTE! key_ref_t is a typedef'd pointer to a type that is not actually * defined. This is because we abuse the bottom bit of the reference to carry a * flag to indicate whether the calling process possesses that key in one of * its keyrings. * * the key_ref_t has been made a separate type so that the compiler can reject * attempts to dereference it without proper conversion. * * the three functions are used to assemble and disassemble references */ typedef struct __key_reference_with_attributes *key_ref_t; static inline key_ref_t make_key_ref(const struct key *key, bool possession) { return (key_ref_t) ((unsigned long) key | possession); } static inline struct key *key_ref_to_ptr(const key_ref_t key_ref) { return (struct key *) ((unsigned long) key_ref & ~1UL); } static inline bool is_key_possessed(const key_ref_t key_ref) { return (unsigned long) key_ref & 1UL; } typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, const struct key_type *type, const union key_payload *payload, struct key *restriction_key); struct key_restriction { key_restrict_link_func_t check; struct key *key; struct key_type *keytype; }; enum key_state { KEY_IS_UNINSTANTIATED, KEY_IS_POSITIVE, /* Positively instantiated */ }; /*****************************************************************************/ /* * authentication token / access credential / keyring * - types of key include: * - keyrings * - disk encryption IDs * - Kerberos TGTs and tickets */ struct key { refcount_t usage; /* number of references */ key_serial_t serial; /* key serial number */ union { struct list_head graveyard_link; struct rb_node serial_node; }; struct rw_semaphore sem; /* change vs change sem */ struct key_user *user; /* owner of this key */ void *security; /* security data for this key */ union { time64_t expiry; /* time at which key expires (or 0) */ time64_t revoked_at; /* time at which key was revoked */ }; time64_t last_used_at; /* last time used for LRU keyring discard */ kuid_t uid; kgid_t gid; key_perm_t perm; /* access permissions */ unsigned short quotalen; /* length added to quota */ unsigned short datalen; /* payload data length * - may not match RCU dereferenced payload * - payload should contain own length */ short state; /* Key state (+) or rejection error (-) */ #ifdef KEY_DEBUGGING unsigned magic; #define KEY_DEBUG_MAGIC 0x18273645u #endif unsigned long flags; /* status flags (change with bitops) */ #define KEY_FLAG_DEAD 0 /* set if key type has been deleted */ #define KEY_FLAG_REVOKED 1 /* set if key had been revoked */ #define KEY_FLAG_IN_QUOTA 2 /* set if key consumes quota */ #define KEY_FLAG_USER_CONSTRUCT 3 /* set if key is being constructed in userspace */ #define KEY_FLAG_ROOT_CAN_CLEAR 4 /* set if key can be cleared by root without permission */ #define KEY_FLAG_INVALIDATED 5 /* set if key has been invalidated */ #define KEY_FLAG_BUILTIN 6 /* set if key is built in to the kernel */ #define KEY_FLAG_ROOT_CAN_INVAL 7 /* set if key can be invalidated by root without permission */ #define KEY_FLAG_KEEP 8 /* set if key should not be removed */ #define KEY_FLAG_UID_KEYRING 9 /* set if key is a user or user session keyring */ /* the key type and key description string * - the desc is used to match a key against search criteria * - it should be a printable string * - eg: for krb5 AFS, this might be "afs@REDHAT.COM" */ union { struct keyring_index_key index_key; struct { unsigned long hash; unsigned long len_desc; struct key_type *type; /* type of key */ struct key_tag *domain_tag; /* Domain of operation */ char *description; }; }; /* key data * - this is used to hold the data actually used in cryptography or * whatever */ union { union key_payload payload; struct { /* Keyring bits */ struct list_head name_link; struct assoc_array keys; }; }; /* This is set on a keyring to restrict the addition of a link to a key * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring * keys. Only set this value using keyring_restrict(), keyring_alloc(), * or key_alloc(). * * This is intended for use with rings of trusted keys whereby addition * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION * overrides this, allowing the kernel to add extra keys without * restriction. */ struct key_restriction *restrict_link; }; extern struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, struct key_restriction *restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ #define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ #define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ #define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */ #define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */ #define KEY_ALLOC_UID_KEYRING 0x0010 /* allocating a user or user session keyring */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); extern void key_put(struct key *key); extern bool key_put_tag(struct key_tag *tag); extern void key_remove_domain(struct key_tag *domain_tag); static inline struct key *__key_get(struct key *key) { refcount_inc(&key->usage); return key; } static inline struct key *key_get(struct key *key) { return key ? __key_get(key) : key; } static inline void key_ref_put(key_ref_t key_ref) { key_put(key_ref_to_ptr(key_ref)); } extern struct key *request_key_tag(struct key_type *type, const char *description, struct key_tag *domain_tag, const char *callout_info); extern struct key *request_key_rcu(struct key_type *type, const char *description, struct key_tag *domain_tag); extern struct key *request_key_with_auxdata(struct key_type *type, const char *description, struct key_tag *domain_tag, const void *callout_info, size_t callout_len, void *aux); /** * request_key - Request a key and wait for construction * @type: Type of key. * @description: The searchable description of the key. * @callout_info: The data to pass to the instantiation upcall (or NULL). * * As for request_key_tag(), but with the default global domain tag. */ static inline struct key *request_key(struct key_type *type, const char *description, const char *callout_info) { return request_key_tag(type, description, NULL, callout_info); } #ifdef CONFIG_NET /** * request_key_net - Request a key for a net namespace and wait for construction * @type: Type of key. * @description: The searchable description of the key. * @net: The network namespace that is the key's domain of operation. * @callout_info: The data to pass to the instantiation upcall (or NULL). * * As for request_key() except that it does not add the returned key to a * keyring if found, new keys are always allocated in the user's quota, the * callout_info must be a NUL-terminated string and no auxiliary data can be * passed. Only keys that operate the specified network namespace are used. * * Furthermore, it then works as wait_for_key_construction() to wait for the * completion of keys undergoing construction with a non-interruptible wait. */ #define request_key_net(type, description, net, callout_info) \ request_key_tag(type, description, net->key_domain, callout_info); /** * request_key_net_rcu - Request a key for a net namespace under RCU conditions * @type: Type of key. * @description: The searchable description of the key. * @net: The network namespace that is the key's domain of operation. * * As for request_key_rcu() except that only keys that operate the specified * network namespace are used. */ #define request_key_net_rcu(type, description, net) \ request_key_rcu(type, description, net->key_domain); #endif /* CONFIG_NET */ extern int wait_for_key_construction(struct key *key, bool intr); extern int key_validate(const struct key *key); extern key_ref_t key_create_or_update(key_ref_t keyring, const char *type, const char *description, const void *payload, size_t plen, key_perm_t perm, unsigned long flags); extern int key_update(key_ref_t key, const void *payload, size_t plen); extern int key_link(struct key *keyring, struct key *key); extern int key_move(struct key *key, struct key *from_keyring, struct key *to_keyring, unsigned int flags); extern int key_unlink(struct key *keyring, struct key *key); extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, struct key_restriction *restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, const struct key_type *type, const union key_payload *payload, struct key *restriction_key); extern int keyring_clear(struct key *keyring); extern key_ref_t keyring_search(key_ref_t keyring, struct key_type *type, const char *description, bool recurse); extern int keyring_add_key(struct key *keyring, struct key *key); extern int keyring_restrict(key_ref_t keyring, const char *type, const char *restriction); extern struct key *key_lookup(key_serial_t id); static inline key_serial_t key_serial(const struct key *key) { return key ? key->serial : 0; } extern void key_set_timeout(struct key *, unsigned); extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, key_perm_t perm); extern void key_free_user_ns(struct user_namespace *); /* * The permissions required on a key that we're looking up. */ #define KEY_NEED_VIEW 0x01 /* Require permission to view attributes */ #define KEY_NEED_READ 0x02 /* Require permission to read content */ #define KEY_NEED_WRITE 0x04 /* Require permission to update / modify */ #define KEY_NEED_SEARCH 0x08 /* Require permission to search (keyring) or find (key) */ #define KEY_NEED_LINK 0x10 /* Require permission to link */ #define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */ #define KEY_NEED_ALL 0x3f /* All the above permissions */ static inline short key_read_state(const struct key *key) { /* Barrier versus mark_key_instantiated(). */ return smp_load_acquire(&key->state); } /** * key_is_positive - Determine if a key has been positively instantiated * @key: The key to check. * * Return true if the specified key has been positively instantiated, false * otherwise. */ static inline bool key_is_positive(const struct key *key) { return key_read_state(key) == KEY_IS_POSITIVE; } static inline bool key_is_negative(const struct key *key) { return key_read_state(key) < 0; } #define dereference_key_rcu(KEY) \ (rcu_dereference((KEY)->payload.rcu_data0)) #define dereference_key_locked(KEY) \ (rcu_dereference_protected((KEY)->payload.rcu_data0, \ rwsem_is_locked(&((struct key *)(KEY))->sem))) #define rcu_assign_keypointer(KEY, PAYLOAD) \ do { \ rcu_assign_pointer((KEY)->payload.rcu_data0, (PAYLOAD)); \ } while (0) #ifdef CONFIG_SYSCTL extern struct ctl_table key_sysctls[]; #endif /* * the userspace interface */ extern int install_thread_keyring_to_cred(struct cred *cred); extern void key_fsuid_changed(struct cred *new_cred); extern void key_fsgid_changed(struct cred *new_cred); extern void key_init(void); #else /* CONFIG_KEYS */ #define key_validate(k) 0 #define key_serial(k) 0 #define key_get(k) ({ NULL; }) #define key_revoke(k) do { } while(0) #define key_invalidate(k) do { } while(0) #define key_put(k) do { } while(0) #define key_ref_put(k) do { } while(0) #define make_key_ref(k, p) NULL #define key_ref_to_ptr(k) NULL #define is_key_possessed(k) 0 #define key_fsuid_changed(c) do { } while(0) #define key_fsgid_changed(c) do { } while(0) #define key_init() do { } while(0) #define key_free_user_ns(ns) do { } while(0) #define key_remove_domain(d) do { } while(0) #endif /* CONFIG_KEYS */ #endif /* __KERNEL__ */ #endif /* _LINUX_KEY_H */ ================================================ FILE: t/tree/include/linux/kmod.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __LINUX_KMOD_H__ #define __LINUX_KMOD_H__ /* * include/linux/kmod.h */ #include #include #include #include #include #include #include #define KMOD_PATH_LEN 256 #ifdef CONFIG_MODULES extern char modprobe_path[]; /* for sysctl */ /* modprobe exit status on success, -ve on error. Return value * usually useless though. */ extern __printf(2, 3) int __request_module(bool wait, const char *name, ...); #define request_module(mod...) __request_module(true, mod) #define request_module_nowait(mod...) __request_module(false, mod) #define try_then_request_module(x, mod...) \ ((x) ?: (__request_module(true, mod), (x))) #else static inline int request_module(const char *name, ...) { return -ENOSYS; } static inline int request_module_nowait(const char *name, ...) { return -ENOSYS; } #define try_then_request_module(x, mod...) (x) #endif #endif /* __LINUX_KMOD_H__ */ ================================================ FILE: t/tree/include/linux/log2.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Integer base 2 logarithm calculation * * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #ifndef _LINUX_LOG2_H #define _LINUX_LOG2_H #include #include /* * non-constant log of base 2 calculators * - the arch may override these in asm/bitops.h if they can be implemented * more efficiently than using fls() and fls64() * - the arch is not required to handle n==0 if implementing the fallback */ #ifndef CONFIG_ARCH_HAS_ILOG2_U32 static inline __attribute__((const)) int __ilog2_u32(u32 n) { return fls(n) - 1; } #endif #ifndef CONFIG_ARCH_HAS_ILOG2_U64 static inline __attribute__((const)) int __ilog2_u64(u64 n) { return fls64(n) - 1; } #endif /** * is_power_of_2() - check if a value is a power of two * @n: the value to check * * Determine whether some value is a power of two, where zero is * *not* considered a power of two. * Return: true if @n is a power of 2, otherwise false. */ static inline __attribute__((const)) bool is_power_of_2(unsigned long n) { return (n != 0 && ((n & (n - 1)) == 0)); } /** * __roundup_pow_of_two() - round up to nearest power of two * @n: value to round up */ static inline __attribute__((const)) unsigned long __roundup_pow_of_two(unsigned long n) { return 1UL << fls_long(n - 1); } /** * __rounddown_pow_of_two() - round down to nearest power of two * @n: value to round down */ static inline __attribute__((const)) unsigned long __rounddown_pow_of_two(unsigned long n) { return 1UL << (fls_long(n) - 1); } /** * const_ilog2 - log base 2 of 32-bit or a 64-bit constant unsigned value * @n: parameter * * Use this where sparse expects a true constant expression, e.g. for array * indices. */ #define const_ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ (n) < 2 ? 0 : \ (n) & (1ULL << 63) ? 63 : \ (n) & (1ULL << 62) ? 62 : \ (n) & (1ULL << 61) ? 61 : \ (n) & (1ULL << 60) ? 60 : \ (n) & (1ULL << 59) ? 59 : \ (n) & (1ULL << 58) ? 58 : \ (n) & (1ULL << 57) ? 57 : \ (n) & (1ULL << 56) ? 56 : \ (n) & (1ULL << 55) ? 55 : \ (n) & (1ULL << 54) ? 54 : \ (n) & (1ULL << 53) ? 53 : \ (n) & (1ULL << 52) ? 52 : \ (n) & (1ULL << 51) ? 51 : \ (n) & (1ULL << 50) ? 50 : \ (n) & (1ULL << 49) ? 49 : \ (n) & (1ULL << 48) ? 48 : \ (n) & (1ULL << 47) ? 47 : \ (n) & (1ULL << 46) ? 46 : \ (n) & (1ULL << 45) ? 45 : \ (n) & (1ULL << 44) ? 44 : \ (n) & (1ULL << 43) ? 43 : \ (n) & (1ULL << 42) ? 42 : \ (n) & (1ULL << 41) ? 41 : \ (n) & (1ULL << 40) ? 40 : \ (n) & (1ULL << 39) ? 39 : \ (n) & (1ULL << 38) ? 38 : \ (n) & (1ULL << 37) ? 37 : \ (n) & (1ULL << 36) ? 36 : \ (n) & (1ULL << 35) ? 35 : \ (n) & (1ULL << 34) ? 34 : \ (n) & (1ULL << 33) ? 33 : \ (n) & (1ULL << 32) ? 32 : \ (n) & (1ULL << 31) ? 31 : \ (n) & (1ULL << 30) ? 30 : \ (n) & (1ULL << 29) ? 29 : \ (n) & (1ULL << 28) ? 28 : \ (n) & (1ULL << 27) ? 27 : \ (n) & (1ULL << 26) ? 26 : \ (n) & (1ULL << 25) ? 25 : \ (n) & (1ULL << 24) ? 24 : \ (n) & (1ULL << 23) ? 23 : \ (n) & (1ULL << 22) ? 22 : \ (n) & (1ULL << 21) ? 21 : \ (n) & (1ULL << 20) ? 20 : \ (n) & (1ULL << 19) ? 19 : \ (n) & (1ULL << 18) ? 18 : \ (n) & (1ULL << 17) ? 17 : \ (n) & (1ULL << 16) ? 16 : \ (n) & (1ULL << 15) ? 15 : \ (n) & (1ULL << 14) ? 14 : \ (n) & (1ULL << 13) ? 13 : \ (n) & (1ULL << 12) ? 12 : \ (n) & (1ULL << 11) ? 11 : \ (n) & (1ULL << 10) ? 10 : \ (n) & (1ULL << 9) ? 9 : \ (n) & (1ULL << 8) ? 8 : \ (n) & (1ULL << 7) ? 7 : \ (n) & (1ULL << 6) ? 6 : \ (n) & (1ULL << 5) ? 5 : \ (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ 1) : \ -1) /** * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value * @n: parameter * * constant-capable log of base 2 calculation * - this can be used to initialise global variables from constant data, hence * the massive ternary operator construction * * selects the appropriately-sized optimised version depending on sizeof(n) */ #define ilog2(n) \ ( \ __builtin_constant_p(n) ? \ const_ilog2(n) : \ (sizeof(n) <= 4) ? \ __ilog2_u32(n) : \ __ilog2_u64(n) \ ) /** * roundup_pow_of_two - round the given value up to nearest power of two * @n: parameter * * round the given value up to the nearest power of two * - the result is undefined when n == 0 * - this can be used to initialise global variables from constant data */ #define roundup_pow_of_two(n) \ ( \ __builtin_constant_p(n) ? ( \ (n == 1) ? 1 : \ (1UL << (ilog2((n) - 1) + 1)) \ ) : \ __roundup_pow_of_two(n) \ ) /** * rounddown_pow_of_two - round the given value down to nearest power of two * @n: parameter * * round the given value down to the nearest power of two * - the result is undefined when n == 0 * - this can be used to initialise global variables from constant data */ #define rounddown_pow_of_two(n) \ ( \ __builtin_constant_p(n) ? ( \ (1UL << ilog2(n))) : \ __rounddown_pow_of_two(n) \ ) static inline __attribute_const__ int __order_base_2(unsigned long n) { return n > 1 ? ilog2(n - 1) + 1 : 0; } /** * order_base_2 - calculate the (rounded up) base 2 order of the argument * @n: parameter * * The first few values calculated by this routine: * ob2(0) = 0 * ob2(1) = 0 * ob2(2) = 1 * ob2(3) = 2 * ob2(4) = 2 * ob2(5) = 3 * ... and so on. */ #define order_base_2(n) \ ( \ __builtin_constant_p(n) ? ( \ ((n) == 0 || (n) == 1) ? 0 : \ ilog2((n) - 1) + 1) : \ __order_base_2(n) \ ) static inline __attribute__((const)) int __bits_per(unsigned long n) { if (n < 2) return 1; if (is_power_of_2(n)) return order_base_2(n) + 1; return order_base_2(n); } /** * bits_per - calculate the number of bits required for the argument * @n: parameter * * This is constant-capable and can be used for compile time * initializations, e.g bitfields. * * The first few values calculated by this routine: * bf(0) = 1 * bf(1) = 1 * bf(2) = 2 * bf(3) = 2 * bf(4) = 3 * ... and so on. */ #define bits_per(n) \ ( \ __builtin_constant_p(n) ? ( \ ((n) == 0 || (n) == 1) \ ? 1 : ilog2(n) + 1 \ ) : \ __bits_per(n) \ ) #endif /* _LINUX_LOG2_H */ ================================================ FILE: t/tree/include/linux/logic_pio.h ================================================ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. * Author: Gabriele Paoloni * Author: Zhichang Yuan */ #ifndef __LINUX_LOGIC_PIO_H #define __LINUX_LOGIC_PIO_H #include enum { LOGIC_PIO_INDIRECT, /* Indirect IO flag */ LOGIC_PIO_CPU_MMIO, /* Memory-mapped IO flag */ }; struct logic_pio_hwaddr { struct list_head list; struct fwnode_handle *fwnode; resource_size_t hw_start; resource_size_t io_start; resource_size_t size; /* range size populated */ unsigned long flags; void *hostdata; const struct logic_pio_host_ops *ops; }; struct logic_pio_host_ops { u32 (*in)(void *hostdata, unsigned long addr, size_t dwidth); void (*out)(void *hostdata, unsigned long addr, u32 val, size_t dwidth); u32 (*ins)(void *hostdata, unsigned long addr, void *buffer, size_t dwidth, unsigned int count); void (*outs)(void *hostdata, unsigned long addr, const void *buffer, size_t dwidth, unsigned int count); }; #ifdef CONFIG_INDIRECT_PIO u8 logic_inb(unsigned long addr); void logic_outb(u8 value, unsigned long addr); void logic_outw(u16 value, unsigned long addr); void logic_outl(u32 value, unsigned long addr); u16 logic_inw(unsigned long addr); u32 logic_inl(unsigned long addr); void logic_outb(u8 value, unsigned long addr); void logic_outw(u16 value, unsigned long addr); void logic_outl(u32 value, unsigned long addr); void logic_insb(unsigned long addr, void *buffer, unsigned int count); void logic_insl(unsigned long addr, void *buffer, unsigned int count); void logic_insw(unsigned long addr, void *buffer, unsigned int count); void logic_outsb(unsigned long addr, const void *buffer, unsigned int count); void logic_outsw(unsigned long addr, const void *buffer, unsigned int count); void logic_outsl(unsigned long addr, const void *buffer, unsigned int count); #ifndef inb #define inb logic_inb #endif #ifndef inw #define inw logic_inw #endif #ifndef inl #define inl logic_inl #endif #ifndef outb #define outb logic_outb #endif #ifndef outw #define outw logic_outw #endif #ifndef outl #define outl logic_outl #endif #ifndef insb #define insb logic_insb #endif #ifndef insw #define insw logic_insw #endif #ifndef insl #define insl logic_insl #endif #ifndef outsb #define outsb logic_outsb #endif #ifndef outsw #define outsw logic_outsw #endif #ifndef outsl #define outsl logic_outsl #endif /* * We reserve 0x4000 bytes for Indirect IO as so far this library is only * used by the HiSilicon LPC Host. If needed, we can reserve a wider IO * area by redefining the macro below. */ #define PIO_INDIRECT_SIZE 0x4000 #define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE) #else #define MMIO_UPPER_LIMIT IO_SPACE_LIMIT #endif /* CONFIG_INDIRECT_PIO */ struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode); unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, resource_size_t hw_addr, resource_size_t size); int logic_pio_register_range(struct logic_pio_hwaddr *newrange); void logic_pio_unregister_range(struct logic_pio_hwaddr *range); resource_size_t logic_pio_to_hwaddr(unsigned long pio); unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr); #endif /* __LINUX_LOGIC_PIO_H */ ================================================ FILE: t/tree/include/linux/memblock.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _LINUX_MEMBLOCK_H #define _LINUX_MEMBLOCK_H #ifdef __KERNEL__ /* * Logical memory blocks. * * Copyright (C) 2001 Peter Bergner, IBM Corp. */ #include #include #include extern unsigned long max_low_pfn; extern unsigned long min_low_pfn; /* * highest page */ extern unsigned long max_pfn; /* * highest possible page */ extern unsigned long long max_possible_pfn; /** * enum memblock_flags - definition of memory region attributes * @MEMBLOCK_NONE: no special request * @MEMBLOCK_HOTPLUG: hotpluggable region * @MEMBLOCK_MIRROR: mirrored region * @MEMBLOCK_NOMAP: don't add to kernel direct mapping */ enum memblock_flags { MEMBLOCK_NONE = 0x0, /* No special request */ MEMBLOCK_HOTPLUG = 0x1, /* hotpluggable region */ MEMBLOCK_MIRROR = 0x2, /* mirrored region */ MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */ }; /** * struct memblock_region - represents a memory region * @base: physical address of the region * @size: size of the region * @flags: memory region attributes * @nid: NUMA node id */ struct memblock_region { phys_addr_t base; phys_addr_t size; enum memblock_flags flags; #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP int nid; #endif }; /** * struct memblock_type - collection of memory regions of certain type * @cnt: number of regions * @max: size of the allocated array * @total_size: size of all regions * @regions: array of regions * @name: the memory type symbolic name */ struct memblock_type { unsigned long cnt; unsigned long max; phys_addr_t total_size; struct memblock_region *regions; char *name; }; /** * struct memblock - memblock allocator metadata * @bottom_up: is bottom up direction? * @current_limit: physical address of the current allocation limit * @memory: usabe memory regions * @reserved: reserved memory regions * @physmem: all physical memory */ struct memblock { bool bottom_up; /* is bottom up direction? */ phys_addr_t current_limit; struct memblock_type memory; struct memblock_type reserved; #ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP struct memblock_type physmem; #endif }; extern struct memblock memblock; extern int memblock_debug; #ifndef CONFIG_ARCH_KEEP_MEMBLOCK #define __init_memblock __meminit #define __initdata_memblock __meminitdata void memblock_discard(void); #else #define __init_memblock #define __initdata_memblock static inline void memblock_discard(void) {} #endif #define memblock_dbg(fmt, ...) \ if (memblock_debug) printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align); void memblock_allow_resize(void); int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid); int memblock_add(phys_addr_t base, phys_addr_t size); int memblock_remove(phys_addr_t base, phys_addr_t size); int memblock_free(phys_addr_t base, phys_addr_t size); int memblock_reserve(phys_addr_t base, phys_addr_t size); #ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP int memblock_physmem_add(phys_addr_t base, phys_addr_t size); #endif void memblock_trim_memory(phys_addr_t align); bool memblock_overlaps_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size); int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size); int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size); int memblock_mark_mirror(phys_addr_t base, phys_addr_t size); int memblock_mark_nomap(phys_addr_t base, phys_addr_t size); int memblock_clear_nomap(phys_addr_t base, phys_addr_t size); unsigned long memblock_free_all(void); void reset_node_managed_pages(pg_data_t *pgdat); void reset_all_zones_managed_pages(void); /* Low level functions */ void __next_mem_range(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_a, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid); void __next_mem_range_rev(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_a, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid); void __next_reserved_mem_region(u64 *idx, phys_addr_t *out_start, phys_addr_t *out_end); void __memblock_free_late(phys_addr_t base, phys_addr_t size); /** * for_each_mem_range - iterate through memblock areas from type_a and not * included in type_b. Or just type_a if type_b is NULL. * @i: u64 used as loop variable * @type_a: ptr to memblock_type to iterate * @type_b: ptr to memblock_type which excludes from the iteration * @nid: node selector, %NUMA_NO_NODE for all nodes * @flags: pick from blocks based on memory attributes * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * @p_nid: ptr to int for nid of the range, can be %NULL */ #define for_each_mem_range(i, type_a, type_b, nid, flags, \ p_start, p_end, p_nid) \ for (i = 0, __next_mem_range(&i, nid, flags, type_a, type_b, \ p_start, p_end, p_nid); \ i != (u64)ULLONG_MAX; \ __next_mem_range(&i, nid, flags, type_a, type_b, \ p_start, p_end, p_nid)) /** * for_each_mem_range_rev - reverse iterate through memblock areas from * type_a and not included in type_b. Or just type_a if type_b is NULL. * @i: u64 used as loop variable * @type_a: ptr to memblock_type to iterate * @type_b: ptr to memblock_type which excludes from the iteration * @nid: node selector, %NUMA_NO_NODE for all nodes * @flags: pick from blocks based on memory attributes * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * @p_nid: ptr to int for nid of the range, can be %NULL */ #define for_each_mem_range_rev(i, type_a, type_b, nid, flags, \ p_start, p_end, p_nid) \ for (i = (u64)ULLONG_MAX, \ __next_mem_range_rev(&i, nid, flags, type_a, type_b,\ p_start, p_end, p_nid); \ i != (u64)ULLONG_MAX; \ __next_mem_range_rev(&i, nid, flags, type_a, type_b, \ p_start, p_end, p_nid)) /** * for_each_reserved_mem_region - iterate over all reserved memblock areas * @i: u64 used as loop variable * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * * Walks over reserved areas of memblock. Available as soon as memblock * is initialized. */ #define for_each_reserved_mem_region(i, p_start, p_end) \ for (i = 0UL, __next_reserved_mem_region(&i, p_start, p_end); \ i != (u64)ULLONG_MAX; \ __next_reserved_mem_region(&i, p_start, p_end)) static inline bool memblock_is_hotpluggable(struct memblock_region *m) { return m->flags & MEMBLOCK_HOTPLUG; } static inline bool memblock_is_mirror(struct memblock_region *m) { return m->flags & MEMBLOCK_MIRROR; } static inline bool memblock_is_nomap(struct memblock_region *m) { return m->flags & MEMBLOCK_NOMAP; } #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn, unsigned long *end_pfn); void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn, unsigned long *out_end_pfn, int *out_nid); /** * for_each_mem_pfn_range - early memory pfn range iterator * @i: an integer used as loop variable * @nid: node selector, %MAX_NUMNODES for all nodes * @p_start: ptr to ulong for start pfn of the range, can be %NULL * @p_end: ptr to ulong for end pfn of the range, can be %NULL * @p_nid: ptr to int for nid of the range, can be %NULL * * Walks over configured memory ranges. */ #define for_each_mem_pfn_range(i, nid, p_start, p_end, p_nid) \ for (i = -1, __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid); \ i >= 0; __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid)) #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT void __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone, unsigned long *out_spfn, unsigned long *out_epfn); /** * for_each_free_mem_range_in_zone - iterate through zone specific free * memblock areas * @i: u64 used as loop variable * @zone: zone in which all of the memory blocks reside * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * * Walks over free (memory && !reserved) areas of memblock in a specific * zone. Available once memblock and an empty zone is initialized. The main * assumption is that the zone start, end, and pgdat have been associated. * This way we can use the zone to determine NUMA node, and if a given part * of the memblock is valid for the zone. */ #define for_each_free_mem_pfn_range_in_zone(i, zone, p_start, p_end) \ for (i = 0, \ __next_mem_pfn_range_in_zone(&i, zone, p_start, p_end); \ i != U64_MAX; \ __next_mem_pfn_range_in_zone(&i, zone, p_start, p_end)) /** * for_each_free_mem_range_in_zone_from - iterate through zone specific * free memblock areas from a given point * @i: u64 used as loop variable * @zone: zone in which all of the memory blocks reside * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * * Walks over free (memory && !reserved) areas of memblock in a specific * zone, continuing from current position. Available as soon as memblock is * initialized. */ #define for_each_free_mem_pfn_range_in_zone_from(i, zone, p_start, p_end) \ for (; i != U64_MAX; \ __next_mem_pfn_range_in_zone(&i, zone, p_start, p_end)) #endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ /** * for_each_free_mem_range - iterate through free memblock areas * @i: u64 used as loop variable * @nid: node selector, %NUMA_NO_NODE for all nodes * @flags: pick from blocks based on memory attributes * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * @p_nid: ptr to int for nid of the range, can be %NULL * * Walks over free (memory && !reserved) areas of memblock. Available as * soon as memblock is initialized. */ #define for_each_free_mem_range(i, nid, flags, p_start, p_end, p_nid) \ for_each_mem_range(i, &memblock.memory, &memblock.reserved, \ nid, flags, p_start, p_end, p_nid) /** * for_each_free_mem_range_reverse - rev-iterate through free memblock areas * @i: u64 used as loop variable * @nid: node selector, %NUMA_NO_NODE for all nodes * @flags: pick from blocks based on memory attributes * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL * @p_nid: ptr to int for nid of the range, can be %NULL * * Walks over free (memory && !reserved) areas of memblock in reverse * order. Available as soon as memblock is initialized. */ #define for_each_free_mem_range_reverse(i, nid, flags, p_start, p_end, \ p_nid) \ for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, \ nid, flags, p_start, p_end, p_nid) #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP int memblock_set_node(phys_addr_t base, phys_addr_t size, struct memblock_type *type, int nid); static inline void memblock_set_region_node(struct memblock_region *r, int nid) { r->nid = nid; } static inline int memblock_get_region_node(const struct memblock_region *r) { return r->nid; } #else static inline void memblock_set_region_node(struct memblock_region *r, int nid) { } static inline int memblock_get_region_node(const struct memblock_region *r) { return 0; } #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ /* Flags for memblock allocation APIs */ #define MEMBLOCK_ALLOC_ANYWHERE (~(phys_addr_t)0) #define MEMBLOCK_ALLOC_ACCESSIBLE 0 #define MEMBLOCK_ALLOC_KASAN 1 /* We are using top down, so it is safe to use 0 here */ #define MEMBLOCK_LOW_LIMIT 0 #ifndef ARCH_LOW_ADDRESS_LIMIT #define ARCH_LOW_ADDRESS_LIMIT 0xffffffffUL #endif phys_addr_t memblock_phys_alloc_range(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end); phys_addr_t memblock_phys_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid); static inline phys_addr_t memblock_phys_alloc(phys_addr_t size, phys_addr_t align) { return memblock_phys_alloc_range(size, align, 0, MEMBLOCK_ALLOC_ACCESSIBLE); } void *memblock_alloc_exact_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid); void *memblock_alloc_try_nid_raw(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid); void *memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr, phys_addr_t max_addr, int nid); static inline void * __init memblock_alloc(phys_addr_t size, phys_addr_t align) { return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE); } static inline void * __init memblock_alloc_raw(phys_addr_t size, phys_addr_t align) { return memblock_alloc_try_nid_raw(size, align, MEMBLOCK_LOW_LIMIT, MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE); } static inline void * __init memblock_alloc_from(phys_addr_t size, phys_addr_t align, phys_addr_t min_addr) { return memblock_alloc_try_nid(size, align, min_addr, MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE); } static inline void * __init memblock_alloc_low(phys_addr_t size, phys_addr_t align) { return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE); } static inline void * __init memblock_alloc_node(phys_addr_t size, phys_addr_t align, int nid) { return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, MEMBLOCK_ALLOC_ACCESSIBLE, nid); } static inline void __init memblock_free_early(phys_addr_t base, phys_addr_t size) { memblock_free(base, size); } static inline void __init memblock_free_early_nid(phys_addr_t base, phys_addr_t size, int nid) { memblock_free(base, size); } static inline void __init memblock_free_late(phys_addr_t base, phys_addr_t size) { __memblock_free_late(base, size); } /* * Set the allocation direction to bottom-up or top-down. */ static inline void __init memblock_set_bottom_up(bool enable) { memblock.bottom_up = enable; } /* * Check if the allocation direction is bottom-up or not. * if this is true, that said, memblock will allocate memory * in bottom-up direction. */ static inline bool memblock_bottom_up(void) { return memblock.bottom_up; } phys_addr_t memblock_phys_mem_size(void); phys_addr_t memblock_reserved_size(void); phys_addr_t memblock_mem_size(unsigned long limit_pfn); phys_addr_t memblock_start_of_DRAM(void); phys_addr_t memblock_end_of_DRAM(void); void memblock_enforce_memory_limit(phys_addr_t memory_limit); void memblock_cap_memory_range(phys_addr_t base, phys_addr_t size); void memblock_mem_limit_remove_map(phys_addr_t limit); bool memblock_is_memory(phys_addr_t addr); bool memblock_is_map_memory(phys_addr_t addr); bool memblock_is_region_memory(phys_addr_t base, phys_addr_t size); bool memblock_is_reserved(phys_addr_t addr); bool memblock_is_region_reserved(phys_addr_t base, phys_addr_t size); extern void __memblock_dump_all(void); static inline void memblock_dump_all(void) { if (memblock_debug) __memblock_dump_all(); } /** * memblock_set_current_limit - Set the current allocation limit to allow * limiting allocations to what is currently * accessible during boot * @limit: New limit value (physical address) */ void memblock_set_current_limit(phys_addr_t limit); phys_addr_t memblock_get_current_limit(void); /* * pfn conversion functions * * While the memory MEMBLOCKs should always be page aligned, the reserved * MEMBLOCKs may not be. This accessor attempt to provide a very clear * idea of what they return for such non aligned MEMBLOCKs. */ /** * memblock_region_memory_base_pfn - get the lowest pfn of the memory region * @reg: memblock_region structure * * Return: the lowest pfn intersecting with the memory region */ static inline unsigned long memblock_region_memory_base_pfn(const struct memblock_region *reg) { return PFN_UP(reg->base); } /** * memblock_region_memory_end_pfn - get the end pfn of the memory region * @reg: memblock_region structure * * Return: the end_pfn of the reserved region */ static inline unsigned long memblock_region_memory_end_pfn(const struct memblock_region *reg) { return PFN_DOWN(reg->base + reg->size); } /** * memblock_region_reserved_base_pfn - get the lowest pfn of the reserved region * @reg: memblock_region structure * * Return: the lowest pfn intersecting with the reserved region */ static inline unsigned long memblock_region_reserved_base_pfn(const struct memblock_region *reg) { return PFN_DOWN(reg->base); } /** * memblock_region_reserved_end_pfn - get the end pfn of the reserved region * @reg: memblock_region structure * * Return: the end_pfn of the reserved region */ static inline unsigned long memblock_region_reserved_end_pfn(const struct memblock_region *reg) { return PFN_UP(reg->base + reg->size); } #define for_each_memblock(memblock_type, region) \ for (region = memblock.memblock_type.regions; \ region < (memblock.memblock_type.regions + memblock.memblock_type.cnt); \ region++) #define for_each_memblock_type(i, memblock_type, rgn) \ for (i = 0, rgn = &memblock_type->regions[0]; \ i < memblock_type->cnt; \ i++, rgn = &memblock_type->regions[i]) extern void *alloc_large_system_hash(const char *tablename, unsigned long bucketsize, unsigned long numentries, int scale, int flags, unsigned int *_hash_shift, unsigned int *_hash_mask, unsigned long low_limit, unsigned long high_limit); #define HASH_EARLY 0x00000001 /* Allocating during early boot? */ #define HASH_SMALL 0x00000002 /* sub-page allocation allowed, min * shift passed via *_hash_shift */ #define HASH_ZERO 0x00000004 /* Zero allocated hash table */ /* Only NUMA needs hash distribution. 64bit NUMA architectures have * sufficient vmalloc space. */ #ifdef CONFIG_NUMA #define HASHDIST_DEFAULT IS_ENABLED(CONFIG_64BIT) extern int hashdist; /* Distribute hashes across NUMA nodes? */ #else #define hashdist (0) #endif #ifdef CONFIG_MEMTEST extern void early_memtest(phys_addr_t start, phys_addr_t end); #else static inline void early_memtest(phys_addr_t start, phys_addr_t end) { } #endif #endif /* __KERNEL__ */ #endif /* _LINUX_MEMBLOCK_H */ ================================================ FILE: t/tree/include/linux/of.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ #ifndef _LINUX_OF_H #define _LINUX_OF_H /* * Definitions for talking to the Open Firmware PROM on * Power Macintosh and other computers. * * Copyright (C) 1996-2005 Paul Mackerras. * * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. * Updates for SPARC64 by David S. Miller * Derived from PowerPC and Sparc prom.h files by Stephen Rothwell, IBM Corp. */ #include #include #include #include #include #include #include #include #include #include #include #include typedef u32 phandle; typedef u32 ihandle; struct property { char *name; int length; void *value; struct property *next; #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) unsigned long _flags; #endif #if defined(CONFIG_OF_PROMTREE) unsigned int unique_id; #endif #if defined(CONFIG_OF_KOBJ) struct bin_attribute attr; #endif }; #if defined(CONFIG_SPARC) struct of_irq_controller; #endif struct device_node { const char *name; phandle phandle; const char *full_name; struct fwnode_handle fwnode; struct property *properties; struct property *deadprops; /* removed properties */ struct device_node *parent; struct device_node *child; struct device_node *sibling; #if defined(CONFIG_OF_KOBJ) struct kobject kobj; #endif unsigned long _flags; void *data; #if defined(CONFIG_SPARC) unsigned int unique_id; struct of_irq_controller *irq_trans; #endif }; #define MAX_PHANDLE_ARGS 16 struct of_phandle_args { struct device_node *np; int args_count; uint32_t args[MAX_PHANDLE_ARGS]; }; struct of_phandle_iterator { /* Common iterator information */ const char *cells_name; int cell_count; const struct device_node *parent; /* List size information */ const __be32 *list_end; const __be32 *phandle_end; /* Current position state */ const __be32 *cur; uint32_t cur_count; phandle phandle; struct device_node *node; }; struct of_reconfig_data { struct device_node *dn; struct property *prop; struct property *old_prop; }; /* initialize a node */ extern struct kobj_type of_node_ktype; extern const struct fwnode_operations of_fwnode_ops; static inline void of_node_init(struct device_node *node) { #if defined(CONFIG_OF_KOBJ) kobject_init(&node->kobj, &of_node_ktype); #endif node->fwnode.ops = &of_fwnode_ops; } #if defined(CONFIG_OF_KOBJ) #define of_node_kobj(n) (&(n)->kobj) #else #define of_node_kobj(n) NULL #endif #ifdef CONFIG_OF_DYNAMIC extern struct device_node *of_node_get(struct device_node *node); extern void of_node_put(struct device_node *node); #else /* CONFIG_OF_DYNAMIC */ /* Dummy ref counting routines - to be implemented later */ static inline struct device_node *of_node_get(struct device_node *node) { return node; } static inline void of_node_put(struct device_node *node) { } #endif /* !CONFIG_OF_DYNAMIC */ /* Pointer for first entry in chain of all nodes. */ extern struct device_node *of_root; extern struct device_node *of_chosen; extern struct device_node *of_aliases; extern struct device_node *of_stdout; extern raw_spinlock_t devtree_lock; /* * struct device_node flag descriptions * (need to be visible even when !CONFIG_OF) */ #define OF_DYNAMIC 1 /* (and properties) allocated via kmalloc */ #define OF_DETACHED 2 /* detached from the device tree */ #define OF_POPULATED 3 /* device already created */ #define OF_POPULATED_BUS 4 /* platform bus created for children */ #define OF_OVERLAY 5 /* allocated for an overlay */ #define OF_OVERLAY_FREE_CSET 6 /* in overlay cset being freed */ #define OF_BAD_ADDR ((u64)-1) #ifdef CONFIG_OF void of_core_init(void); static inline bool is_of_node(const struct fwnode_handle *fwnode) { return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &of_fwnode_ops; } #define to_of_node(__fwnode) \ ({ \ typeof(__fwnode) __to_of_node_fwnode = (__fwnode); \ \ is_of_node(__to_of_node_fwnode) ? \ container_of(__to_of_node_fwnode, \ struct device_node, fwnode) : \ NULL; \ }) #define of_fwnode_handle(node) \ ({ \ typeof(node) __of_fwnode_handle_node = (node); \ \ __of_fwnode_handle_node ? \ &__of_fwnode_handle_node->fwnode : NULL; \ }) static inline bool of_have_populated_dt(void) { return of_root != NULL; } static inline bool of_node_is_root(const struct device_node *node) { return node && (node->parent == NULL); } static inline int of_node_check_flag(struct device_node *n, unsigned long flag) { return test_bit(flag, &n->_flags); } static inline int of_node_test_and_set_flag(struct device_node *n, unsigned long flag) { return test_and_set_bit(flag, &n->_flags); } static inline void of_node_set_flag(struct device_node *n, unsigned long flag) { set_bit(flag, &n->_flags); } static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) { clear_bit(flag, &n->_flags); } #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) static inline int of_property_check_flag(struct property *p, unsigned long flag) { return test_bit(flag, &p->_flags); } static inline void of_property_set_flag(struct property *p, unsigned long flag) { set_bit(flag, &p->_flags); } static inline void of_property_clear_flag(struct property *p, unsigned long flag) { clear_bit(flag, &p->_flags); } #endif extern struct device_node *__of_find_all_nodes(struct device_node *prev); extern struct device_node *of_find_all_nodes(struct device_node *prev); /* * OF address retrieval & translation */ /* Helper to read a big number; size is in cells (not bytes) */ static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; for (; size--; cell++) r = (r << 32) | be32_to_cpu(*cell); return r; } /* Like of_read_number, but we want an unsigned long result */ static inline unsigned long of_read_ulong(const __be32 *cell, int size) { /* toss away upper bits if unsigned long is smaller than u64 */ return of_read_number(cell, size); } #if defined(CONFIG_SPARC) #include #endif #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) extern bool of_node_name_eq(const struct device_node *np, const char *name); extern bool of_node_name_prefix(const struct device_node *np, const char *prefix); static inline const char *of_node_full_name(const struct device_node *np) { return np ? np->full_name : ""; } #define for_each_of_allnodes_from(from, dn) \ for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn)) #define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn) extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name); extern struct device_node *of_find_node_by_type(struct device_node *from, const char *type); extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat); extern struct device_node *of_find_matching_node_and_match( struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match); extern struct device_node *of_find_node_opts_by_path(const char *path, const char **opts); static inline struct device_node *of_find_node_by_path(const char *path) { return of_find_node_opts_by_path(path, NULL); } extern struct device_node *of_find_node_by_phandle(phandle handle); extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); extern struct device_node *of_get_next_available_child( const struct device_node *node, struct device_node *prev); extern struct device_node *of_get_compatible_child(const struct device_node *parent, const char *compatible); extern struct device_node *of_get_child_by_name(const struct device_node *node, const char *name); /* cache lookup */ extern struct device_node *of_find_next_cache_node(const struct device_node *); extern int of_find_last_cache_level(unsigned int cpu); extern struct device_node *of_find_node_with_property( struct device_node *from, const char *prop_name); extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); extern int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size); extern int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value); extern int of_property_read_u64_index(const struct device_node *np, const char *propname, u32 index, u64 *out_value); extern int of_property_read_variable_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz_min, size_t sz_max); extern int of_property_read_variable_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz_min, size_t sz_max); extern int of_property_read_variable_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz_min, size_t sz_max); extern int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value); extern int of_property_read_variable_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz_min, size_t sz_max); extern int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string); extern int of_property_match_string(const struct device_node *np, const char *propname, const char *string); extern int of_property_read_string_helper(const struct device_node *np, const char *propname, const char **out_strs, size_t sz, int index); extern int of_device_is_compatible(const struct device_node *device, const char *); extern int of_device_compatible_match(struct device_node *device, const char *const *compat); extern bool of_device_is_available(const struct device_node *device); extern bool of_device_is_big_endian(const struct device_node *device); extern const void *of_get_property(const struct device_node *node, const char *name, int *lenp); extern struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); extern struct device_node *of_get_next_cpu_node(struct device_node *prev); #define for_each_property_of_node(dn, pp) \ for (pp = dn->properties; pp != NULL; pp = pp->next) extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( const struct of_device_id *matches, const struct device_node *node); extern int of_modalias_node(struct device_node *node, char *modalias, int len); extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args); extern struct device_node *of_parse_phandle(const struct device_node *np, const char *phandle_name, int index); extern int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int index, struct of_phandle_args *out_args); extern int of_parse_phandle_with_args_map(const struct device_node *np, const char *list_name, const char *stem_name, int index, struct of_phandle_args *out_args); extern int of_parse_phandle_with_fixed_args(const struct device_node *np, const char *list_name, int cells_count, int index, struct of_phandle_args *out_args); extern int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name); /* phandle iterator functions */ extern int of_phandle_iterator_init(struct of_phandle_iterator *it, const struct device_node *np, const char *list_name, const char *cells_name, int cell_count); extern int of_phandle_iterator_next(struct of_phandle_iterator *it); extern int of_phandle_iterator_args(struct of_phandle_iterator *it, uint32_t *args, int size); extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); extern int of_alias_get_highest_id(const char *stem); extern int of_alias_get_alias_list(const struct of_device_id *matches, const char *stem, unsigned long *bitmap, unsigned int nbits); extern int of_machine_is_compatible(const char *compat); extern int of_add_property(struct device_node *np, struct property *prop); extern int of_remove_property(struct device_node *np, struct property *prop); extern int of_update_property(struct device_node *np, struct property *newprop); /* For updating the device tree at runtime */ #define OF_RECONFIG_ATTACH_NODE 0x0001 #define OF_RECONFIG_DETACH_NODE 0x0002 #define OF_RECONFIG_ADD_PROPERTY 0x0003 #define OF_RECONFIG_REMOVE_PROPERTY 0x0004 #define OF_RECONFIG_UPDATE_PROPERTY 0x0005 extern int of_attach_node(struct device_node *); extern int of_detach_node(struct device_node *); #define of_match_ptr(_ptr) (_ptr) /** * of_property_read_u8_array - Find and read an array of u8 from a property. * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 8-bit value(s) from * it. Returns 0 on success, -EINVAL if the property does not exist, * -ENODATA if property does not have a value, and -EOVERFLOW if the * property data isn't large enough. * * dts entry of array should be like: * property = /bits/ 8 <0x50 0x60 0x70>; * * The out_values is modified only if a valid u8 value can be decoded. */ static inline int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) { int ret = of_property_read_variable_u8_array(np, propname, out_values, sz, 0); if (ret >= 0) return 0; else return ret; } /** * of_property_read_u16_array - Find and read an array of u16 from a property. * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 16-bit value(s) from * it. Returns 0 on success, -EINVAL if the property does not exist, * -ENODATA if property does not have a value, and -EOVERFLOW if the * property data isn't large enough. * * dts entry of array should be like: * property = /bits/ 16 <0x5000 0x6000 0x7000>; * * The out_values is modified only if a valid u16 value can be decoded. */ static inline int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz) { int ret = of_property_read_variable_u16_array(np, propname, out_values, sz, 0); if (ret >= 0) return 0; else return ret; } /** * of_property_read_u32_array - Find and read an array of 32 bit integers * from a property. * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 32-bit value(s) from * it. Returns 0 on success, -EINVAL if the property does not exist, * -ENODATA if property does not have a value, and -EOVERFLOW if the * property data isn't large enough. * * The out_values is modified only if a valid u32 value can be decoded. */ static inline int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz) { int ret = of_property_read_variable_u32_array(np, propname, out_values, sz, 0); if (ret >= 0) return 0; else return ret; } /** * of_property_read_u64_array - Find and read an array of 64 bit integers * from a property. * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 64-bit value(s) from * it. Returns 0 on success, -EINVAL if the property does not exist, * -ENODATA if property does not have a value, and -EOVERFLOW if the * property data isn't large enough. * * The out_values is modified only if a valid u64 value can be decoded. */ static inline int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz) { int ret = of_property_read_variable_u64_array(np, propname, out_values, sz, 0); if (ret >= 0) return 0; else return ret; } /* * struct property *prop; * const __be32 *p; * u32 u; * * of_property_for_each_u32(np, "propname", prop, p, u) * printk("U32 value: %x\n", u); */ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, u32 *pu); /* * struct property *prop; * const char *s; * * of_property_for_each_string(np, "propname", prop, s) * printk("String value: %s\n", s); */ const char *of_prop_next_string(struct property *prop, const char *cur); bool of_console_check(struct device_node *dn, char *name, int index); extern int of_cpu_node_to_id(struct device_node *np); int of_map_rid(struct device_node *np, u32 rid, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out); #else /* CONFIG_OF */ static inline void of_core_init(void) { } static inline bool is_of_node(const struct fwnode_handle *fwnode) { return false; } static inline struct device_node *to_of_node(const struct fwnode_handle *fwnode) { return NULL; } static inline bool of_node_name_eq(const struct device_node *np, const char *name) { return false; } static inline bool of_node_name_prefix(const struct device_node *np, const char *prefix) { return false; } static inline const char* of_node_full_name(const struct device_node *np) { return ""; } static inline struct device_node *of_find_node_by_name(struct device_node *from, const char *name) { return NULL; } static inline struct device_node *of_find_node_by_type(struct device_node *from, const char *type) { return NULL; } static inline struct device_node *of_find_matching_node_and_match( struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match) { return NULL; } static inline struct device_node *of_find_node_by_path(const char *path) { return NULL; } static inline struct device_node *of_find_node_opts_by_path(const char *path, const char **opts) { return NULL; } static inline struct device_node *of_find_node_by_phandle(phandle handle) { return NULL; } static inline struct device_node *of_get_parent(const struct device_node *node) { return NULL; } static inline struct device_node *of_get_next_child( const struct device_node *node, struct device_node *prev) { return NULL; } static inline struct device_node *of_get_next_available_child( const struct device_node *node, struct device_node *prev) { return NULL; } static inline struct device_node *of_find_node_with_property( struct device_node *from, const char *prop_name) { return NULL; } #define of_fwnode_handle(node) NULL static inline bool of_have_populated_dt(void) { return false; } static inline struct device_node *of_get_compatible_child(const struct device_node *parent, const char *compatible) { return NULL; } static inline struct device_node *of_get_child_by_name( const struct device_node *node, const char *name) { return NULL; } static inline int of_device_is_compatible(const struct device_node *device, const char *name) { return 0; } static inline int of_device_compatible_match(struct device_node *device, const char *const *compat) { return 0; } static inline bool of_device_is_available(const struct device_node *device) { return false; } static inline bool of_device_is_big_endian(const struct device_node *device) { return false; } static inline struct property *of_find_property(const struct device_node *np, const char *name, int *lenp) { return NULL; } static inline struct device_node *of_find_compatible_node( struct device_node *from, const char *type, const char *compat) { return NULL; } static inline int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size) { return -ENOSYS; } static inline int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) { return -ENOSYS; } static inline int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz) { return -ENOSYS; } static inline int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz) { return -ENOSYS; } static inline int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz) { return -ENOSYS; } static inline int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value) { return -ENOSYS; } static inline int of_property_read_u64_index(const struct device_node *np, const char *propname, u32 index, u64 *out_value) { return -ENOSYS; } static inline const void *of_get_property(const struct device_node *node, const char *name, int *lenp) { return NULL; } static inline struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) { return NULL; } static inline struct device_node *of_get_next_cpu_node(struct device_node *prev) { return NULL; } static inline int of_n_addr_cells(struct device_node *np) { return 0; } static inline int of_n_size_cells(struct device_node *np) { return 0; } static inline int of_property_read_variable_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz_min, size_t sz_max) { return -ENOSYS; } static inline int of_property_read_variable_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz_min, size_t sz_max) { return -ENOSYS; } static inline int of_property_read_variable_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz_min, size_t sz_max) { return -ENOSYS; } static inline int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value) { return -ENOSYS; } static inline int of_property_read_variable_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz_min, size_t sz_max) { return -ENOSYS; } static inline int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string) { return -ENOSYS; } static inline int of_property_match_string(const struct device_node *np, const char *propname, const char *string) { return -ENOSYS; } static inline int of_property_read_string_helper(const struct device_node *np, const char *propname, const char **out_strs, size_t sz, int index) { return -ENOSYS; } static inline struct device_node *of_parse_phandle(const struct device_node *np, const char *phandle_name, int index) { return NULL; } static inline int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int index, struct of_phandle_args *out_args) { return -ENOSYS; } static inline int of_parse_phandle_with_args_map(const struct device_node *np, const char *list_name, const char *stem_name, int index, struct of_phandle_args *out_args) { return -ENOSYS; } static inline int of_parse_phandle_with_fixed_args(const struct device_node *np, const char *list_name, int cells_count, int index, struct of_phandle_args *out_args) { return -ENOSYS; } static inline int of_count_phandle_with_args(struct device_node *np, const char *list_name, const char *cells_name) { return -ENOSYS; } static inline int of_phandle_iterator_init(struct of_phandle_iterator *it, const struct device_node *np, const char *list_name, const char *cells_name, int cell_count) { return -ENOSYS; } static inline int of_phandle_iterator_next(struct of_phandle_iterator *it) { return -ENOSYS; } static inline int of_phandle_iterator_args(struct of_phandle_iterator *it, uint32_t *args, int size) { return 0; } static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; } static inline int of_alias_get_highest_id(const char *stem) { return -ENOSYS; } static inline int of_alias_get_alias_list(const struct of_device_id *matches, const char *stem, unsigned long *bitmap, unsigned int nbits) { return -ENOSYS; } static inline int of_machine_is_compatible(const char *compat) { return 0; } static inline bool of_console_check(const struct device_node *dn, const char *name, int index) { return false; } static inline const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, u32 *pu) { return NULL; } static inline const char *of_prop_next_string(struct property *prop, const char *cur) { return NULL; } static inline int of_node_check_flag(struct device_node *n, unsigned long flag) { return 0; } static inline int of_node_test_and_set_flag(struct device_node *n, unsigned long flag) { return 0; } static inline void of_node_set_flag(struct device_node *n, unsigned long flag) { } static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) { } static inline int of_property_check_flag(struct property *p, unsigned long flag) { return 0; } static inline void of_property_set_flag(struct property *p, unsigned long flag) { } static inline void of_property_clear_flag(struct property *p, unsigned long flag) { } static inline int of_cpu_node_to_id(struct device_node *np) { return -ENODEV; } static inline int of_map_rid(struct device_node *np, u32 rid, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out) { return -EINVAL; } #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL #endif /* CONFIG_OF */ /* Default string compare functions, Allow arch asm/prom.h to override */ #if !defined(of_compat_cmp) #define of_compat_cmp(s1, s2, l) strcasecmp((s1), (s2)) #define of_prop_cmp(s1, s2) strcmp((s1), (s2)) #define of_node_cmp(s1, s2) strcasecmp((s1), (s2)) #endif static inline int of_prop_val_eq(struct property *p1, struct property *p2) { return p1->length == p2->length && !memcmp(p1->value, p2->value, (size_t)p1->length); } #if defined(CONFIG_OF) && defined(CONFIG_NUMA) extern int of_node_to_nid(struct device_node *np); #else static inline int of_node_to_nid(struct device_node *device) { return NUMA_NO_NODE; } #endif #ifdef CONFIG_OF_NUMA extern int of_numa_init(void); #else static inline int of_numa_init(void) { return -ENOSYS; } #endif static inline struct device_node *of_find_matching_node( struct device_node *from, const struct of_device_id *matches) { return of_find_matching_node_and_match(from, matches, NULL); } static inline const char *of_node_get_device_type(const struct device_node *np) { return of_get_property(np, "device_type", NULL); } static inline bool of_node_is_type(const struct device_node *np, const char *type) { const char *match = of_node_get_device_type(np); return np && match && type && !strcmp(match, type); } /** * of_property_count_u8_elems - Count the number of u8 elements in a property * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * * Search for a property in a device node and count the number of u8 elements * in it. Returns number of elements on sucess, -EINVAL if the property does * not exist or its length does not match a multiple of u8 and -ENODATA if the * property does not have a value. */ static inline int of_property_count_u8_elems(const struct device_node *np, const char *propname) { return of_property_count_elems_of_size(np, propname, sizeof(u8)); } /** * of_property_count_u16_elems - Count the number of u16 elements in a property * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * * Search for a property in a device node and count the number of u16 elements * in it. Returns number of elements on sucess, -EINVAL if the property does * not exist or its length does not match a multiple of u16 and -ENODATA if the * property does not have a value. */ static inline int of_property_count_u16_elems(const struct device_node *np, const char *propname) { return of_property_count_elems_of_size(np, propname, sizeof(u16)); } /** * of_property_count_u32_elems - Count the number of u32 elements in a property * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * * Search for a property in a device node and count the number of u32 elements * in it. Returns number of elements on sucess, -EINVAL if the property does * not exist or its length does not match a multiple of u32 and -ENODATA if the * property does not have a value. */ static inline int of_property_count_u32_elems(const struct device_node *np, const char *propname) { return of_property_count_elems_of_size(np, propname, sizeof(u32)); } /** * of_property_count_u64_elems - Count the number of u64 elements in a property * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * * Search for a property in a device node and count the number of u64 elements * in it. Returns number of elements on sucess, -EINVAL if the property does * not exist or its length does not match a multiple of u64 and -ENODATA if the * property does not have a value. */ static inline int of_property_count_u64_elems(const struct device_node *np, const char *propname) { return of_property_count_elems_of_size(np, propname, sizeof(u64)); } /** * of_property_read_string_array() - Read an array of strings from a multiple * strings property. * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * @out_strs: output array of string pointers. * @sz: number of array elements to read. * * Search for a property in a device tree node and retrieve a list of * terminated string values (pointer to data, not a copy) in that property. * * If @out_strs is NULL, the number of strings in the property is returned. */ static inline int of_property_read_string_array(const struct device_node *np, const char *propname, const char **out_strs, size_t sz) { return of_property_read_string_helper(np, propname, out_strs, sz, 0); } /** * of_property_count_strings() - Find and return the number of strings from a * multiple strings property. * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * * Search for a property in a device tree node and retrieve the number of null * terminated string contain in it. Returns the number of strings on * success, -EINVAL if the property does not exist, -ENODATA if property * does not have a value, and -EILSEQ if the string is not null-terminated * within the length of the property data. */ static inline int of_property_count_strings(const struct device_node *np, const char *propname) { return of_property_read_string_helper(np, propname, NULL, 0, 0); } /** * of_property_read_string_index() - Find and read a string from a multiple * strings property. * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * @index: index of the string in the list of strings * @out_string: pointer to null terminated return string, modified only if * return value is 0. * * Search for a property in a device tree node and retrieve a null * terminated string value (pointer to data, not a copy) in the list of strings * contained in that property. * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if * property does not have a value, and -EILSEQ if the string is not * null-terminated within the length of the property data. * * The out_string pointer is modified only if a valid string can be decoded. */ static inline int of_property_read_string_index(const struct device_node *np, const char *propname, int index, const char **output) { int rc = of_property_read_string_helper(np, propname, output, 1, index); return rc < 0 ? rc : 0; } /** * of_property_read_bool - Find a property * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. * * Search for a property in a device node. * Returns true if the property exists false otherwise. */ static inline bool of_property_read_bool(const struct device_node *np, const char *propname) { struct property *prop = of_find_property(np, propname, NULL); return prop ? true : false; } static inline int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value) { return of_property_read_u8_array(np, propname, out_value, 1); } static inline int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value) { return of_property_read_u16_array(np, propname, out_value, 1); } static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value) { return of_property_read_u32_array(np, propname, out_value, 1); } static inline int of_property_read_s32(const struct device_node *np, const char *propname, s32 *out_value) { return of_property_read_u32(np, propname, (u32*) out_value); } #define of_for_each_phandle(it, err, np, ln, cn, cc) \ for (of_phandle_iterator_init((it), (np), (ln), (cn), (cc)), \ err = of_phandle_iterator_next(it); \ err == 0; \ err = of_phandle_iterator_next(it)) #define of_property_for_each_u32(np, propname, prop, p, u) \ for (prop = of_find_property(np, propname, NULL), \ p = of_prop_next_u32(prop, NULL, &u); \ p; \ p = of_prop_next_u32(prop, p, &u)) #define of_property_for_each_string(np, propname, prop, s) \ for (prop = of_find_property(np, propname, NULL), \ s = of_prop_next_string(prop, NULL); \ s; \ s = of_prop_next_string(prop, s)) #define for_each_node_by_name(dn, name) \ for (dn = of_find_node_by_name(NULL, name); dn; \ dn = of_find_node_by_name(dn, name)) #define for_each_node_by_type(dn, type) \ for (dn = of_find_node_by_type(NULL, type); dn; \ dn = of_find_node_by_type(dn, type)) #define for_each_compatible_node(dn, type, compatible) \ for (dn = of_find_compatible_node(NULL, type, compatible); dn; \ dn = of_find_compatible_node(dn, type, compatible)) #define for_each_matching_node(dn, matches) \ for (dn = of_find_matching_node(NULL, matches); dn; \ dn = of_find_matching_node(dn, matches)) #define for_each_matching_node_and_match(dn, matches, match) \ for (dn = of_find_matching_node_and_match(NULL, matches, match); \ dn; dn = of_find_matching_node_and_match(dn, matches, match)) #define for_each_child_of_node(parent, child) \ for (child = of_get_next_child(parent, NULL); child != NULL; \ child = of_get_next_child(parent, child)) #define for_each_available_child_of_node(parent, child) \ for (child = of_get_next_available_child(parent, NULL); child != NULL; \ child = of_get_next_available_child(parent, child)) #define for_each_of_cpu_node(cpu) \ for (cpu = of_get_next_cpu_node(NULL); cpu != NULL; \ cpu = of_get_next_cpu_node(cpu)) #define for_each_node_with_property(dn, prop_name) \ for (dn = of_find_node_with_property(NULL, prop_name); dn; \ dn = of_find_node_with_property(dn, prop_name)) static inline int of_get_child_count(const struct device_node *np) { struct device_node *child; int num = 0; for_each_child_of_node(np, child) num++; return num; } static inline int of_get_available_child_count(const struct device_node *np) { struct device_node *child; int num = 0; for_each_available_child_of_node(np, child) num++; return num; } #if defined(CONFIG_OF) && !defined(MODULE) #define _OF_DECLARE(table, name, compat, fn, fn_type) \ static const struct of_device_id __of_table_##name \ __used __section(__##table##_of_table) \ = { .compatible = compat, \ .data = (fn == (fn_type)NULL) ? fn : fn } #else #define _OF_DECLARE(table, name, compat, fn, fn_type) \ static const struct of_device_id __of_table_##name \ __attribute__((unused)) \ = { .compatible = compat, \ .data = (fn == (fn_type)NULL) ? fn : fn } #endif typedef int (*of_init_fn_2)(struct device_node *, struct device_node *); typedef int (*of_init_fn_1_ret)(struct device_node *); typedef void (*of_init_fn_1)(struct device_node *); #define OF_DECLARE_1(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_1) #define OF_DECLARE_1_RET(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_1_ret) #define OF_DECLARE_2(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_2) /** * struct of_changeset_entry - Holds a changeset entry * * @node: list_head for the log list * @action: notifier action * @np: pointer to the device node affected * @prop: pointer to the property affected * @old_prop: hold a pointer to the original property * * Every modification of the device tree during a changeset * is held in a list of of_changeset_entry structures. * That way we can recover from a partial application, or we can * revert the changeset */ struct of_changeset_entry { struct list_head node; unsigned long action; struct device_node *np; struct property *prop; struct property *old_prop; }; /** * struct of_changeset - changeset tracker structure * * @entries: list_head for the changeset entries * * changesets are a convenient way to apply bulk changes to the * live tree. In case of an error, changes are rolled-back. * changesets live on after initial application, and if not * destroyed after use, they can be reverted in one single call. */ struct of_changeset { struct list_head entries; }; enum of_reconfig_change { OF_RECONFIG_NO_CHANGE = 0, OF_RECONFIG_CHANGE_ADD, OF_RECONFIG_CHANGE_REMOVE, }; #ifdef CONFIG_OF_DYNAMIC extern int of_reconfig_notifier_register(struct notifier_block *); extern int of_reconfig_notifier_unregister(struct notifier_block *); extern int of_reconfig_notify(unsigned long, struct of_reconfig_data *rd); extern int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *arg); extern void of_changeset_init(struct of_changeset *ocs); extern void of_changeset_destroy(struct of_changeset *ocs); extern int of_changeset_apply(struct of_changeset *ocs); extern int of_changeset_revert(struct of_changeset *ocs); extern int of_changeset_action(struct of_changeset *ocs, unsigned long action, struct device_node *np, struct property *prop); static inline int of_changeset_attach_node(struct of_changeset *ocs, struct device_node *np) { return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL); } static inline int of_changeset_detach_node(struct of_changeset *ocs, struct device_node *np) { return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL); } static inline int of_changeset_add_property(struct of_changeset *ocs, struct device_node *np, struct property *prop) { return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop); } static inline int of_changeset_remove_property(struct of_changeset *ocs, struct device_node *np, struct property *prop) { return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop); } static inline int of_changeset_update_property(struct of_changeset *ocs, struct device_node *np, struct property *prop) { return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop); } #else /* CONFIG_OF_DYNAMIC */ static inline int of_reconfig_notifier_register(struct notifier_block *nb) { return -EINVAL; } static inline int of_reconfig_notifier_unregister(struct notifier_block *nb) { return -EINVAL; } static inline int of_reconfig_notify(unsigned long action, struct of_reconfig_data *arg) { return -EINVAL; } static inline int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *arg) { return -EINVAL; } #endif /* CONFIG_OF_DYNAMIC */ /** * of_device_is_system_power_controller - Tells if system-power-controller is found for device_node * @np: Pointer to the given device_node * * return true if present false otherwise */ static inline bool of_device_is_system_power_controller(const struct device_node *np) { return of_property_read_bool(np, "system-power-controller"); } /** * Overlay support */ enum of_overlay_notify_action { OF_OVERLAY_PRE_APPLY = 0, OF_OVERLAY_POST_APPLY, OF_OVERLAY_PRE_REMOVE, OF_OVERLAY_POST_REMOVE, }; struct of_overlay_notify_data { struct device_node *overlay; struct device_node *target; }; #ifdef CONFIG_OF_OVERLAY int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, int *ovcs_id); int of_overlay_remove(int *ovcs_id); int of_overlay_remove_all(void); int of_overlay_notifier_register(struct notifier_block *nb); int of_overlay_notifier_unregister(struct notifier_block *nb); #else static inline int of_overlay_fdt_apply(void *overlay_fdt, u32 overlay_fdt_size, int *ovcs_id) { return -ENOTSUPP; } static inline int of_overlay_remove(int *ovcs_id) { return -ENOTSUPP; } static inline int of_overlay_remove_all(void) { return -ENOTSUPP; } static inline int of_overlay_notifier_register(struct notifier_block *nb) { return 0; } static inline int of_overlay_notifier_unregister(struct notifier_block *nb) { return 0; } #endif #endif /* _LINUX_OF_H */ ================================================ FILE: t/tree/include/linux/of_platform.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ #ifndef _LINUX_OF_PLATFORM_H #define _LINUX_OF_PLATFORM_H /* * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. * */ #include #include #include #include #include /** * struct of_dev_auxdata - lookup table entry for device names & platform_data * @compatible: compatible value of node to match against node * @phys_addr: Start address of registers to match against node * @name: Name to assign for matching nodes * @platform_data: platform_data to assign for matching nodes * * This lookup table allows the caller of of_platform_populate() to override * the names of devices when creating devices from the device tree. The table * should be terminated with an empty entry. It also allows the platform_data * pointer to be set. * * The reason for this functionality is that some Linux infrastructure uses * the device name to look up a specific device, but the Linux-specific names * are not encoded into the device tree, so the kernel needs to provide specific * values. * * Note: Using an auxdata lookup table should be considered a last resort when * converting a platform to use the DT. Normally the automatically generated * device name will not matter, and drivers should obtain data from the device * node instead of from an anonymous platform_data pointer. */ struct of_dev_auxdata { char *compatible; resource_size_t phys_addr; char *name; void *platform_data; }; /* Macro to simplify populating a lookup table */ #define OF_DEV_AUXDATA(_compat,_phys,_name,_pdata) \ { .compatible = _compat, .phys_addr = _phys, .name = _name, \ .platform_data = _pdata } extern const struct of_device_id of_default_bus_match_table[]; /* Platform drivers register/unregister */ extern struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent); #ifdef CONFIG_OF extern struct platform_device *of_find_device_by_node(struct device_node *np); #else static inline struct platform_device *of_find_device_by_node(struct device_node *np) { return NULL; } #endif /* Platform devices and busses creation */ extern struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, struct device *parent); extern int of_platform_device_destroy(struct device *dev, void *data); extern int of_platform_bus_probe(struct device_node *root, const struct of_device_id *matches, struct device *parent); #ifdef CONFIG_OF_ADDRESS extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent); extern int of_platform_default_populate(struct device_node *root, const struct of_dev_auxdata *lookup, struct device *parent); extern void of_platform_depopulate(struct device *parent); extern int devm_of_platform_populate(struct device *dev); extern void devm_of_platform_depopulate(struct device *dev); #else static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent) { return -ENODEV; } static inline int of_platform_default_populate(struct device_node *root, const struct of_dev_auxdata *lookup, struct device *parent) { return -ENODEV; } static inline void of_platform_depopulate(struct device *parent) { } static inline int devm_of_platform_populate(struct device *dev) { return -ENODEV; } static inline void devm_of_platform_depopulate(struct device *dev) { } #endif #if defined(CONFIG_OF_DYNAMIC) && defined(CONFIG_OF_ADDRESS) extern void of_platform_register_reconfig_notifier(void); #else static inline void of_platform_register_reconfig_notifier(void) { } #endif #endif /* _LINUX_OF_PLATFORM_H */ ================================================ FILE: t/tree/include/linux/plist.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Descending-priority-sorted double-linked list * * (C) 2002-2003 Intel Corp * Inaky Perez-Gonzalez . * * 2001-2005 (c) MontaVista Software, Inc. * Daniel Walker * * (C) 2005 Thomas Gleixner * * Simplifications of the original code by * Oleg Nesterov * * Based on simple lists (include/linux/list.h). * * This is a priority-sorted list of nodes; each node has a * priority from INT_MIN (highest) to INT_MAX (lowest). * * Addition is O(K), removal is O(1), change of priority of a node is * O(K) and K is the number of RT priority levels used in the system. * (1 <= K <= 99) * * This list is really a list of lists: * * - The tier 1 list is the prio_list, different priority nodes. * * - The tier 2 list is the node_list, serialized nodes. * * Simple ASCII art explanation: * * pl:prio_list (only for plist_node) * nl:node_list * HEAD| NODE(S) * | * ||------------------------------------| * ||->|pl|<->|pl|<--------------->|pl|<-| * | |10| |21| |21| |21| |40| (prio) * | | | | | | | | | | | * | | | | | | | | | | | * |->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-| * |-------------------------------------------| * * The nodes on the prio_list list are sorted by priority to simplify * the insertion of new nodes. There are no nodes with duplicate * priorites on the list. * * The nodes on the node_list are ordered by priority and can contain * entries which have the same priority. Those entries are ordered * FIFO * * Addition means: look for the prio_list node in the prio_list * for the priority of the node and insert it before the node_list * entry of the next prio_list node. If it is the first node of * that priority, add it to the prio_list in the right position and * insert it into the serialized node_list list * * Removal means remove it from the node_list and remove it from * the prio_list if the node_list list_head is non empty. In case * of removal from the prio_list it must be checked whether other * entries of the same priority are on the list or not. If there * is another entry of the same priority then this entry has to * replace the removed entry on the prio_list. If the entry which * is removed is the only entry of this priority then a simple * remove from both list is sufficient. * * INT_MIN is the highest priority, 0 is the medium highest, INT_MAX * is lowest priority. * * No locking is done, up to the caller. */ #ifndef _LINUX_PLIST_H_ #define _LINUX_PLIST_H_ #include #include struct plist_head { struct list_head node_list; }; struct plist_node { int prio; struct list_head prio_list; struct list_head node_list; }; /** * PLIST_HEAD_INIT - static struct plist_head initializer * @head: struct plist_head variable name */ #define PLIST_HEAD_INIT(head) \ { \ .node_list = LIST_HEAD_INIT((head).node_list) \ } /** * PLIST_HEAD - declare and init plist_head * @head: name for struct plist_head variable */ #define PLIST_HEAD(head) \ struct plist_head head = PLIST_HEAD_INIT(head) /** * PLIST_NODE_INIT - static struct plist_node initializer * @node: struct plist_node variable name * @__prio: initial node priority */ #define PLIST_NODE_INIT(node, __prio) \ { \ .prio = (__prio), \ .prio_list = LIST_HEAD_INIT((node).prio_list), \ .node_list = LIST_HEAD_INIT((node).node_list), \ } /** * plist_head_init - dynamic struct plist_head initializer * @head: &struct plist_head pointer */ static inline void plist_head_init(struct plist_head *head) { INIT_LIST_HEAD(&head->node_list); } /** * plist_node_init - Dynamic struct plist_node initializer * @node: &struct plist_node pointer * @prio: initial node priority */ static inline void plist_node_init(struct plist_node *node, int prio) { node->prio = prio; INIT_LIST_HEAD(&node->prio_list); INIT_LIST_HEAD(&node->node_list); } extern void plist_add(struct plist_node *node, struct plist_head *head); extern void plist_del(struct plist_node *node, struct plist_head *head); extern void plist_requeue(struct plist_node *node, struct plist_head *head); /** * plist_for_each - iterate over the plist * @pos: the type * to use as a loop counter * @head: the head for your list */ #define plist_for_each(pos, head) \ list_for_each_entry(pos, &(head)->node_list, node_list) /** * plist_for_each_continue - continue iteration over the plist * @pos: the type * to use as a loop cursor * @head: the head for your list * * Continue to iterate over plist, continuing after the current position. */ #define plist_for_each_continue(pos, head) \ list_for_each_entry_continue(pos, &(head)->node_list, node_list) /** * plist_for_each_safe - iterate safely over a plist of given type * @pos: the type * to use as a loop counter * @n: another type * to use as temporary storage * @head: the head for your list * * Iterate over a plist of given type, safe against removal of list entry. */ #define plist_for_each_safe(pos, n, head) \ list_for_each_entry_safe(pos, n, &(head)->node_list, node_list) /** * plist_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter * @head: the head for your list * @mem: the name of the list_head within the struct */ #define plist_for_each_entry(pos, head, mem) \ list_for_each_entry(pos, &(head)->node_list, mem.node_list) /** * plist_for_each_entry_continue - continue iteration over list of given type * @pos: the type * to use as a loop cursor * @head: the head for your list * @m: the name of the list_head within the struct * * Continue to iterate over list of given type, continuing after * the current position. */ #define plist_for_each_entry_continue(pos, head, m) \ list_for_each_entry_continue(pos, &(head)->node_list, m.node_list) /** * plist_for_each_entry_safe - iterate safely over list of given type * @pos: the type * to use as a loop counter * @n: another type * to use as temporary storage * @head: the head for your list * @m: the name of the list_head within the struct * * Iterate over list of given type, safe against removal of list entry. */ #define plist_for_each_entry_safe(pos, n, head, m) \ list_for_each_entry_safe(pos, n, &(head)->node_list, m.node_list) /** * plist_head_empty - return !0 if a plist_head is empty * @head: &struct plist_head pointer */ static inline int plist_head_empty(const struct plist_head *head) { return list_empty(&head->node_list); } /** * plist_node_empty - return !0 if plist_node is not on a list * @node: &struct plist_node pointer */ static inline int plist_node_empty(const struct plist_node *node) { return list_empty(&node->node_list); } /* All functions below assume the plist_head is not empty. */ /** * plist_first_entry - get the struct for the first entry * @head: the &struct plist_head pointer * @type: the type of the struct this is embedded in * @member: the name of the list_head within the struct */ #ifdef CONFIG_DEBUG_PLIST # define plist_first_entry(head, type, member) \ ({ \ WARN_ON(plist_head_empty(head)); \ container_of(plist_first(head), type, member); \ }) #else # define plist_first_entry(head, type, member) \ container_of(plist_first(head), type, member) #endif /** * plist_last_entry - get the struct for the last entry * @head: the &struct plist_head pointer * @type: the type of the struct this is embedded in * @member: the name of the list_head within the struct */ #ifdef CONFIG_DEBUG_PLIST # define plist_last_entry(head, type, member) \ ({ \ WARN_ON(plist_head_empty(head)); \ container_of(plist_last(head), type, member); \ }) #else # define plist_last_entry(head, type, member) \ container_of(plist_last(head), type, member) #endif /** * plist_next - get the next entry in list * @pos: the type * to cursor */ #define plist_next(pos) \ list_next_entry(pos, node_list) /** * plist_prev - get the prev entry in list * @pos: the type * to cursor */ #define plist_prev(pos) \ list_prev_entry(pos, node_list) /** * plist_first - return the first node (and thus, highest priority) * @head: the &struct plist_head pointer * * Assumes the plist is _not_ empty. */ static inline struct plist_node *plist_first(const struct plist_head *head) { return list_entry(head->node_list.next, struct plist_node, node_list); } /** * plist_last - return the last node (and thus, lowest priority) * @head: the &struct plist_head pointer * * Assumes the plist is _not_ empty. */ static inline struct plist_node *plist_last(const struct plist_head *head) { return list_entry(head->node_list.prev, struct plist_node, node_list); } #endif ================================================ FILE: t/tree/include/linux/pm.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * pm.h - Power management interface * * Copyright (C) 2000 Andrew Henroid */ #ifndef _LINUX_PM_H #define _LINUX_PM_H #include #include #include #include #include #include #include /* * Callbacks for platform drivers to implement. */ extern void (*pm_power_off)(void); extern void (*pm_power_off_prepare)(void); struct device; /* we have a circular dep with device.h */ #ifdef CONFIG_VT_CONSOLE_SLEEP extern void pm_vt_switch_required(struct device *dev, bool required); extern void pm_vt_switch_unregister(struct device *dev); #else static inline void pm_vt_switch_required(struct device *dev, bool required) { } static inline void pm_vt_switch_unregister(struct device *dev) { } #endif /* CONFIG_VT_CONSOLE_SLEEP */ /* * Device power management */ struct device; #ifdef CONFIG_PM extern const char power_group_name[]; /* = "power" */ #else #define power_group_name NULL #endif typedef struct pm_message { int event; } pm_message_t; /** * struct dev_pm_ops - device PM callbacks. * * @prepare: The principal role of this callback is to prevent new children of * the device from being registered after it has returned (the driver's * subsystem and generally the rest of the kernel is supposed to prevent * new calls to the probe method from being made too once @prepare() has * succeeded). If @prepare() detects a situation it cannot handle (e.g. * registration of a child already in progress), it may return -EAGAIN, so * that the PM core can execute it once again (e.g. after a new child has * been registered) to recover from the race condition. * This method is executed for all kinds of suspend transitions and is * followed by one of the suspend callbacks: @suspend(), @freeze(), or * @poweroff(). If the transition is a suspend to memory or standby (that * is, not related to hibernation), the return value of @prepare() may be * used to indicate to the PM core to leave the device in runtime suspend * if applicable. Namely, if @prepare() returns a positive number, the PM * core will understand that as a declaration that the device appears to be * runtime-suspended and it may be left in that state during the entire * transition and during the subsequent resume if all of its descendants * are left in runtime suspend too. If that happens, @complete() will be * executed directly after @prepare() and it must ensure the proper * functioning of the device after the system resume. * The PM core executes subsystem-level @prepare() for all devices before * starting to invoke suspend callbacks for any of them, so generally * devices may be assumed to be functional or to respond to runtime resume * requests while @prepare() is being executed. However, device drivers * may NOT assume anything about the availability of user space at that * time and it is NOT valid to request firmware from within @prepare() * (it's too late to do that). It also is NOT valid to allocate * substantial amounts of memory from @prepare() in the GFP_KERNEL mode. * [To work around these limitations, drivers may register suspend and * hibernation notifiers to be executed before the freezing of tasks.] * * @complete: Undo the changes made by @prepare(). This method is executed for * all kinds of resume transitions, following one of the resume callbacks: * @resume(), @thaw(), @restore(). Also called if the state transition * fails before the driver's suspend callback: @suspend(), @freeze() or * @poweroff(), can be executed (e.g. if the suspend callback fails for one * of the other devices that the PM core has unsuccessfully attempted to * suspend earlier). * The PM core executes subsystem-level @complete() after it has executed * the appropriate resume callbacks for all devices. If the corresponding * @prepare() at the beginning of the suspend transition returned a * positive number and the device was left in runtime suspend (without * executing any suspend and resume callbacks for it), @complete() will be * the only callback executed for the device during resume. In that case, * @complete() must be prepared to do whatever is necessary to ensure the * proper functioning of the device after the system resume. To this end, * @complete() can check the power.direct_complete flag of the device to * learn whether (unset) or not (set) the previous suspend and resume * callbacks have been executed for it. * * @suspend: Executed before putting the system into a sleep state in which the * contents of main memory are preserved. The exact action to perform * depends on the device's subsystem (PM domain, device type, class or bus * type), but generally the device must be quiescent after subsystem-level * @suspend() has returned, so that it doesn't do any I/O or DMA. * Subsystem-level @suspend() is executed for all devices after invoking * subsystem-level @prepare() for all of them. * * @suspend_late: Continue operations started by @suspend(). For a number of * devices @suspend_late() may point to the same callback routine as the * runtime suspend callback. * * @resume: Executed after waking the system up from a sleep state in which the * contents of main memory were preserved. The exact action to perform * depends on the device's subsystem, but generally the driver is expected * to start working again, responding to hardware events and software * requests (the device itself may be left in a low-power state, waiting * for a runtime resume to occur). The state of the device at the time its * driver's @resume() callback is run depends on the platform and subsystem * the device belongs to. On most platforms, there are no restrictions on * availability of resources like clocks during @resume(). * Subsystem-level @resume() is executed for all devices after invoking * subsystem-level @resume_noirq() for all of them. * * @resume_early: Prepare to execute @resume(). For a number of devices * @resume_early() may point to the same callback routine as the runtime * resume callback. * * @freeze: Hibernation-specific, executed before creating a hibernation image. * Analogous to @suspend(), but it should not enable the device to signal * wakeup events or change its power state. The majority of subsystems * (with the notable exception of the PCI bus type) expect the driver-level * @freeze() to save the device settings in memory to be used by @restore() * during the subsequent resume from hibernation. * Subsystem-level @freeze() is executed for all devices after invoking * subsystem-level @prepare() for all of them. * * @freeze_late: Continue operations started by @freeze(). Analogous to * @suspend_late(), but it should not enable the device to signal wakeup * events or change its power state. * * @thaw: Hibernation-specific, executed after creating a hibernation image OR * if the creation of an image has failed. Also executed after a failing * attempt to restore the contents of main memory from such an image. * Undo the changes made by the preceding @freeze(), so the device can be * operated in the same way as immediately before the call to @freeze(). * Subsystem-level @thaw() is executed for all devices after invoking * subsystem-level @thaw_noirq() for all of them. It also may be executed * directly after @freeze() in case of a transition error. * * @thaw_early: Prepare to execute @thaw(). Undo the changes made by the * preceding @freeze_late(). * * @poweroff: Hibernation-specific, executed after saving a hibernation image. * Analogous to @suspend(), but it need not save the device's settings in * memory. * Subsystem-level @poweroff() is executed for all devices after invoking * subsystem-level @prepare() for all of them. * * @poweroff_late: Continue operations started by @poweroff(). Analogous to * @suspend_late(), but it need not save the device's settings in memory. * * @restore: Hibernation-specific, executed after restoring the contents of main * memory from a hibernation image, analogous to @resume(). * * @restore_early: Prepare to execute @restore(), analogous to @resume_early(). * * @suspend_noirq: Complete the actions started by @suspend(). Carry out any * additional operations required for suspending the device that might be * racing with its driver's interrupt handler, which is guaranteed not to * run while @suspend_noirq() is being executed. * It generally is expected that the device will be in a low-power state * (appropriate for the target system sleep state) after subsystem-level * @suspend_noirq() has returned successfully. If the device can generate * system wakeup signals and is enabled to wake up the system, it should be * configured to do so at that time. However, depending on the platform * and device's subsystem, @suspend() or @suspend_late() may be allowed to * put the device into the low-power state and configure it to generate * wakeup signals, in which case it generally is not necessary to define * @suspend_noirq(). * * @resume_noirq: Prepare for the execution of @resume() by carrying out any * operations required for resuming the device that might be racing with * its driver's interrupt handler, which is guaranteed not to run while * @resume_noirq() is being executed. * * @freeze_noirq: Complete the actions started by @freeze(). Carry out any * additional operations required for freezing the device that might be * racing with its driver's interrupt handler, which is guaranteed not to * run while @freeze_noirq() is being executed. * The power state of the device should not be changed by either @freeze(), * or @freeze_late(), or @freeze_noirq() and it should not be configured to * signal system wakeup by any of these callbacks. * * @thaw_noirq: Prepare for the execution of @thaw() by carrying out any * operations required for thawing the device that might be racing with its * driver's interrupt handler, which is guaranteed not to run while * @thaw_noirq() is being executed. * * @poweroff_noirq: Complete the actions started by @poweroff(). Analogous to * @suspend_noirq(), but it need not save the device's settings in memory. * * @restore_noirq: Prepare for the execution of @restore() by carrying out any * operations required for thawing the device that might be racing with its * driver's interrupt handler, which is guaranteed not to run while * @restore_noirq() is being executed. Analogous to @resume_noirq(). * * @runtime_suspend: Prepare the device for a condition in which it won't be * able to communicate with the CPU(s) and RAM due to power management. * This need not mean that the device should be put into a low-power state. * For example, if the device is behind a link which is about to be turned * off, the device may remain at full power. If the device does go to low * power and is capable of generating runtime wakeup events, remote wakeup * (i.e., a hardware mechanism allowing the device to request a change of * its power state via an interrupt) should be enabled for it. * * @runtime_resume: Put the device into the fully active state in response to a * wakeup event generated by hardware or at the request of software. If * necessary, put the device into the full-power state and restore its * registers, so that it is fully operational. * * @runtime_idle: Device appears to be inactive and it might be put into a * low-power state if all of the necessary conditions are satisfied. * Check these conditions, and return 0 if it's appropriate to let the PM * core queue a suspend request for the device. * * Several device power state transitions are externally visible, affecting * the state of pending I/O queues and (for drivers that touch hardware) * interrupts, wakeups, DMA, and other hardware state. There may also be * internal transitions to various low-power modes which are transparent * to the rest of the driver stack (such as a driver that's ON gating off * clocks which are not in active use). * * The externally visible transitions are handled with the help of callbacks * included in this structure in such a way that, typically, two levels of * callbacks are involved. First, the PM core executes callbacks provided by PM * domains, device types, classes and bus types. They are the subsystem-level * callbacks expected to execute callbacks provided by device drivers, although * they may choose not to do that. If the driver callbacks are executed, they * have to collaborate with the subsystem-level callbacks to achieve the goals * appropriate for the given system transition, given transition phase and the * subsystem the device belongs to. * * All of the above callbacks, except for @complete(), return error codes. * However, the error codes returned by @resume(), @thaw(), @restore(), * @resume_noirq(), @thaw_noirq(), and @restore_noirq(), do not cause the PM * core to abort the resume transition during which they are returned. The * error codes returned in those cases are only printed to the system logs for * debugging purposes. Still, it is recommended that drivers only return error * codes from their resume methods in case of an unrecoverable failure (i.e. * when the device being handled refuses to resume and becomes unusable) to * allow the PM core to be modified in the future, so that it can avoid * attempting to handle devices that failed to resume and their children. * * It is allowed to unregister devices while the above callbacks are being * executed. However, a callback routine MUST NOT try to unregister the device * it was called for, although it may unregister children of that device (for * example, if it detects that a child was unplugged while the system was * asleep). * * There also are callbacks related to runtime power management of devices. * Again, as a rule these callbacks are executed by the PM core for subsystems * (PM domains, device types, classes and bus types) and the subsystem-level * callbacks are expected to invoke the driver callbacks. Moreover, the exact * actions to be performed by a device driver's callbacks generally depend on * the platform and subsystem the device belongs to. * * Refer to Documentation/power/runtime_pm.rst for more information about the * role of the @runtime_suspend(), @runtime_resume() and @runtime_idle() * callbacks in device runtime power management. */ struct dev_pm_ops { int (*prepare)(struct device *dev); void (*complete)(struct device *dev); int (*suspend)(struct device *dev); int (*resume)(struct device *dev); int (*freeze)(struct device *dev); int (*thaw)(struct device *dev); int (*poweroff)(struct device *dev); int (*restore)(struct device *dev); int (*suspend_late)(struct device *dev); int (*resume_early)(struct device *dev); int (*freeze_late)(struct device *dev); int (*thaw_early)(struct device *dev); int (*poweroff_late)(struct device *dev); int (*restore_early)(struct device *dev); int (*suspend_noirq)(struct device *dev); int (*resume_noirq)(struct device *dev); int (*freeze_noirq)(struct device *dev); int (*thaw_noirq)(struct device *dev); int (*poweroff_noirq)(struct device *dev); int (*restore_noirq)(struct device *dev); int (*runtime_suspend)(struct device *dev); int (*runtime_resume)(struct device *dev); int (*runtime_idle)(struct device *dev); }; #ifdef CONFIG_PM_SLEEP #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ .suspend = suspend_fn, \ .resume = resume_fn, \ .freeze = suspend_fn, \ .thaw = resume_fn, \ .poweroff = suspend_fn, \ .restore = resume_fn, #else #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif #ifdef CONFIG_PM_SLEEP #define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ .suspend_late = suspend_fn, \ .resume_early = resume_fn, \ .freeze_late = suspend_fn, \ .thaw_early = resume_fn, \ .poweroff_late = suspend_fn, \ .restore_early = resume_fn, #else #define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif #ifdef CONFIG_PM_SLEEP #define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ .suspend_noirq = suspend_fn, \ .resume_noirq = resume_fn, \ .freeze_noirq = suspend_fn, \ .thaw_noirq = resume_fn, \ .poweroff_noirq = suspend_fn, \ .restore_noirq = resume_fn, #else #define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif #ifdef CONFIG_PM #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ .runtime_suspend = suspend_fn, \ .runtime_resume = resume_fn, \ .runtime_idle = idle_fn, #else #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) #endif /* * Use this if you want to use the same suspend and resume callbacks for suspend * to RAM and hibernation. */ #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ const struct dev_pm_ops name = { \ SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ } /* * Use this for defining a set of PM operations to be used in all situations * (system suspend, hibernation or runtime PM). * NOTE: In general, system suspend callbacks, .suspend() and .resume(), should * be different from the corresponding runtime PM callbacks, .runtime_suspend(), * and .runtime_resume(), because .runtime_suspend() always works on an already * quiescent device, while .suspend() should assume that the device may be doing * something when it is called (it should ensure that the device will be * quiescent after it has returned). Therefore it's better to point the "late" * suspend and "early" resume callback pointers, .suspend_late() and * .resume_early(), to the same routines as .runtime_suspend() and * .runtime_resume(), respectively (and analogously for hibernation). */ #define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \ const struct dev_pm_ops name = { \ SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ } /* * PM_EVENT_ messages * * The following PM_EVENT_ messages are defined for the internal use of the PM * core, in order to provide a mechanism allowing the high level suspend and * hibernation code to convey the necessary information to the device PM core * code: * * ON No transition. * * FREEZE System is going to hibernate, call ->prepare() and ->freeze() * for all devices. * * SUSPEND System is going to suspend, call ->prepare() and ->suspend() * for all devices. * * HIBERNATE Hibernation image has been saved, call ->prepare() and * ->poweroff() for all devices. * * QUIESCE Contents of main memory are going to be restored from a (loaded) * hibernation image, call ->prepare() and ->freeze() for all * devices. * * RESUME System is resuming, call ->resume() and ->complete() for all * devices. * * THAW Hibernation image has been created, call ->thaw() and * ->complete() for all devices. * * RESTORE Contents of main memory have been restored from a hibernation * image, call ->restore() and ->complete() for all devices. * * RECOVER Creation of a hibernation image or restoration of the main * memory contents from a hibernation image has failed, call * ->thaw() and ->complete() for all devices. * * The following PM_EVENT_ messages are defined for internal use by * kernel subsystems. They are never issued by the PM core. * * USER_SUSPEND Manual selective suspend was issued by userspace. * * USER_RESUME Manual selective resume was issued by userspace. * * REMOTE_WAKEUP Remote-wakeup request was received from the device. * * AUTO_SUSPEND Automatic (device idle) runtime suspend was * initiated by the subsystem. * * AUTO_RESUME Automatic (device needed) runtime resume was * requested by a driver. */ #define PM_EVENT_INVALID (-1) #define PM_EVENT_ON 0x0000 #define PM_EVENT_FREEZE 0x0001 #define PM_EVENT_SUSPEND 0x0002 #define PM_EVENT_HIBERNATE 0x0004 #define PM_EVENT_QUIESCE 0x0008 #define PM_EVENT_RESUME 0x0010 #define PM_EVENT_THAW 0x0020 #define PM_EVENT_RESTORE 0x0040 #define PM_EVENT_RECOVER 0x0080 #define PM_EVENT_USER 0x0100 #define PM_EVENT_REMOTE 0x0200 #define PM_EVENT_AUTO 0x0400 #define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) #define PM_EVENT_USER_SUSPEND (PM_EVENT_USER | PM_EVENT_SUSPEND) #define PM_EVENT_USER_RESUME (PM_EVENT_USER | PM_EVENT_RESUME) #define PM_EVENT_REMOTE_RESUME (PM_EVENT_REMOTE | PM_EVENT_RESUME) #define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND) #define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) #define PMSG_INVALID ((struct pm_message){ .event = PM_EVENT_INVALID, }) #define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) #define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) #define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) #define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, }) #define PMSG_RESUME ((struct pm_message){ .event = PM_EVENT_RESUME, }) #define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) #define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) #define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) #define PMSG_USER_SUSPEND ((struct pm_message) \ { .event = PM_EVENT_USER_SUSPEND, }) #define PMSG_USER_RESUME ((struct pm_message) \ { .event = PM_EVENT_USER_RESUME, }) #define PMSG_REMOTE_RESUME ((struct pm_message) \ { .event = PM_EVENT_REMOTE_RESUME, }) #define PMSG_AUTO_SUSPEND ((struct pm_message) \ { .event = PM_EVENT_AUTO_SUSPEND, }) #define PMSG_AUTO_RESUME ((struct pm_message) \ { .event = PM_EVENT_AUTO_RESUME, }) #define PMSG_IS_AUTO(msg) (((msg).event & PM_EVENT_AUTO) != 0) /* * Device run-time power management status. * * These status labels are used internally by the PM core to indicate the * current status of a device with respect to the PM core operations. They do * not reflect the actual power state of the device or its status as seen by the * driver. * * RPM_ACTIVE Device is fully operational. Indicates that the device * bus type's ->runtime_resume() callback has completed * successfully. * * RPM_SUSPENDED Device bus type's ->runtime_suspend() callback has * completed successfully. The device is regarded as * suspended. * * RPM_RESUMING Device bus type's ->runtime_resume() callback is being * executed. * * RPM_SUSPENDING Device bus type's ->runtime_suspend() callback is being * executed. */ enum rpm_status { RPM_ACTIVE = 0, RPM_RESUMING, RPM_SUSPENDED, RPM_SUSPENDING, }; /* * Device run-time power management request types. * * RPM_REQ_NONE Do nothing. * * RPM_REQ_IDLE Run the device bus type's ->runtime_idle() callback * * RPM_REQ_SUSPEND Run the device bus type's ->runtime_suspend() callback * * RPM_REQ_AUTOSUSPEND Same as RPM_REQ_SUSPEND, but not until the device has * been inactive for as long as power.autosuspend_delay * * RPM_REQ_RESUME Run the device bus type's ->runtime_resume() callback */ enum rpm_request { RPM_REQ_NONE = 0, RPM_REQ_IDLE, RPM_REQ_SUSPEND, RPM_REQ_AUTOSUSPEND, RPM_REQ_RESUME, }; struct wakeup_source; struct wake_irq; struct pm_domain_data; struct pm_subsys_data { spinlock_t lock; unsigned int refcount; #ifdef CONFIG_PM_CLK struct list_head clock_list; #endif #ifdef CONFIG_PM_GENERIC_DOMAINS struct pm_domain_data *domain_data; #endif }; /* * Driver flags to control system suspend/resume behavior. * * These flags can be set by device drivers at the probe time. They need not be * cleared by the drivers as the driver core will take care of that. * * NEVER_SKIP: Do not skip all system suspend/resume callbacks for the device. * SMART_PREPARE: Check the return value of the driver's ->prepare callback. * SMART_SUSPEND: No need to resume the device from runtime suspend. * LEAVE_SUSPENDED: Avoid resuming the device during system resume if possible. * * Setting SMART_PREPARE instructs bus types and PM domains which may want * system suspend/resume callbacks to be skipped for the device to return 0 from * their ->prepare callbacks if the driver's ->prepare callback returns 0 (in * other words, the system suspend/resume callbacks can only be skipped for the * device if its driver doesn't object against that). This flag has no effect * if NEVER_SKIP is set. * * Setting SMART_SUSPEND instructs bus types and PM domains which may want to * runtime resume the device upfront during system suspend that doing so is not * necessary from the driver's perspective. It also may cause them to skip * invocations of the ->suspend_late and ->suspend_noirq callbacks provided by * the driver if they decide to leave the device in runtime suspend. * * Setting LEAVE_SUSPENDED informs the PM core and middle-layer code that the * driver prefers the device to be left in suspend after system resume. */ #define DPM_FLAG_NEVER_SKIP BIT(0) #define DPM_FLAG_SMART_PREPARE BIT(1) #define DPM_FLAG_SMART_SUSPEND BIT(2) #define DPM_FLAG_LEAVE_SUSPENDED BIT(3) struct dev_pm_info { pm_message_t power_state; unsigned int can_wakeup:1; unsigned int async_suspend:1; bool in_dpm_list:1; /* Owned by the PM core */ bool is_prepared:1; /* Owned by the PM core */ bool is_suspended:1; /* Ditto */ bool is_noirq_suspended:1; bool is_late_suspended:1; bool no_pm:1; bool early_init:1; /* Owned by the PM core */ bool direct_complete:1; /* Owned by the PM core */ u32 driver_flags; spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; struct completion completion; struct wakeup_source *wakeup; bool wakeup_path:1; bool syscore:1; bool no_pm_callbacks:1; /* Owned by the PM core */ unsigned int must_resume:1; /* Owned by the PM core */ unsigned int may_skip_resume:1; /* Set by subsystems */ #else unsigned int should_wakeup:1; #endif #ifdef CONFIG_PM struct hrtimer suspend_timer; unsigned long timer_expires; struct work_struct work; wait_queue_head_t wait_queue; struct wake_irq *wakeirq; atomic_t usage_count; atomic_t child_count; unsigned int disable_depth:3; unsigned int idle_notification:1; unsigned int request_pending:1; unsigned int deferred_resume:1; unsigned int runtime_auto:1; bool ignore_children:1; unsigned int no_callbacks:1; unsigned int irq_safe:1; unsigned int use_autosuspend:1; unsigned int timer_autosuspends:1; unsigned int memalloc_noio:1; unsigned int links_count; enum rpm_request request; enum rpm_status runtime_status; int runtime_error; int autosuspend_delay; u64 last_busy; u64 active_time; u64 suspended_time; u64 accounting_timestamp; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ void (*set_latency_tolerance)(struct device *, s32); struct dev_pm_qos *qos; }; extern int dev_pm_get_subsys_data(struct device *dev); extern void dev_pm_put_subsys_data(struct device *dev); /** * struct dev_pm_domain - power management domain representation. * * @ops: Power management operations associated with this domain. * @detach: Called when removing a device from the domain. * @activate: Called before executing probe routines for bus types and drivers. * @sync: Called after successful driver probe. * @dismiss: Called after unsuccessful driver probe and after driver removal. * * Power domains provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions instead of * subsystem-level and driver-level callbacks. */ struct dev_pm_domain { struct dev_pm_ops ops; void (*detach)(struct device *dev, bool power_off); int (*activate)(struct device *dev); void (*sync)(struct device *dev); void (*dismiss)(struct device *dev); }; /* * The PM_EVENT_ messages are also used by drivers implementing the legacy * suspend framework, based on the ->suspend() and ->resume() callbacks common * for suspend and hibernation transitions, according to the rules below. */ /* Necessary, because several drivers use PM_EVENT_PRETHAW */ #define PM_EVENT_PRETHAW PM_EVENT_QUIESCE /* * One transition is triggered by resume(), after a suspend() call; the * message is implicit: * * ON Driver starts working again, responding to hardware events * and software requests. The hardware may have gone through * a power-off reset, or it may have maintained state from the * previous suspend() which the driver will rely on while * resuming. On most platforms, there are no restrictions on * availability of resources like clocks during resume(). * * Other transitions are triggered by messages sent using suspend(). All * these transitions quiesce the driver, so that I/O queues are inactive. * That commonly entails turning off IRQs and DMA; there may be rules * about how to quiesce that are specific to the bus or the device's type. * (For example, network drivers mark the link state.) Other details may * differ according to the message: * * SUSPEND Quiesce, enter a low power device state appropriate for * the upcoming system state (such as PCI_D3hot), and enable * wakeup events as appropriate. * * HIBERNATE Enter a low power device state appropriate for the hibernation * state (eg. ACPI S4) and enable wakeup events as appropriate. * * FREEZE Quiesce operations so that a consistent image can be saved; * but do NOT otherwise enter a low power device state, and do * NOT emit system wakeup events. * * PRETHAW Quiesce as if for FREEZE; additionally, prepare for restoring * the system from a snapshot taken after an earlier FREEZE. * Some drivers will need to reset their hardware state instead * of preserving it, to ensure that it's never mistaken for the * state which that earlier snapshot had set up. * * A minimally power-aware driver treats all messages as SUSPEND, fully * reinitializes its device during resume() -- whether or not it was reset * during the suspend/resume cycle -- and can't issue wakeup events. * * More power-aware drivers may also use low power states at runtime as * well as during system sleep states like PM_SUSPEND_STANDBY. They may * be able to use wakeup events to exit from runtime low-power states, * or from system low-power states such as standby or suspend-to-RAM. */ #ifdef CONFIG_PM_SLEEP extern void device_pm_lock(void); extern void dpm_resume_start(pm_message_t state); extern void dpm_resume_end(pm_message_t state); extern void dpm_resume_noirq(pm_message_t state); extern void dpm_resume_early(pm_message_t state); extern void dpm_resume(pm_message_t state); extern void dpm_complete(pm_message_t state); extern void device_pm_unlock(void); extern int dpm_suspend_end(pm_message_t state); extern int dpm_suspend_start(pm_message_t state); extern int dpm_suspend_noirq(pm_message_t state); extern int dpm_suspend_late(pm_message_t state); extern int dpm_suspend(pm_message_t state); extern int dpm_prepare(pm_message_t state); extern void __suspend_report_result(const char *function, void *fn, int ret); #define suspend_report_result(fn, ret) \ do { \ __suspend_report_result(__func__, fn, ret); \ } while (0) extern int device_pm_wait_for_dev(struct device *sub, struct device *dev); extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)); extern int pm_generic_prepare(struct device *dev); extern int pm_generic_suspend_late(struct device *dev); extern int pm_generic_suspend_noirq(struct device *dev); extern int pm_generic_suspend(struct device *dev); extern int pm_generic_resume_early(struct device *dev); extern int pm_generic_resume_noirq(struct device *dev); extern int pm_generic_resume(struct device *dev); extern int pm_generic_freeze_noirq(struct device *dev); extern int pm_generic_freeze_late(struct device *dev); extern int pm_generic_freeze(struct device *dev); extern int pm_generic_thaw_noirq(struct device *dev); extern int pm_generic_thaw_early(struct device *dev); extern int pm_generic_thaw(struct device *dev); extern int pm_generic_restore_noirq(struct device *dev); extern int pm_generic_restore_early(struct device *dev); extern int pm_generic_restore(struct device *dev); extern int pm_generic_poweroff_noirq(struct device *dev); extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff(struct device *dev); extern void pm_generic_complete(struct device *dev); extern bool dev_pm_may_skip_resume(struct device *dev); extern bool dev_pm_smart_suspend_and_suspended(struct device *dev); #else /* !CONFIG_PM_SLEEP */ #define device_pm_lock() do {} while (0) #define device_pm_unlock() do {} while (0) static inline int dpm_suspend_start(pm_message_t state) { return 0; } #define suspend_report_result(fn, ret) do {} while (0) static inline int device_pm_wait_for_dev(struct device *a, struct device *b) { return 0; } static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) { } #define pm_generic_prepare NULL #define pm_generic_suspend_late NULL #define pm_generic_suspend_noirq NULL #define pm_generic_suspend NULL #define pm_generic_resume_early NULL #define pm_generic_resume_noirq NULL #define pm_generic_resume NULL #define pm_generic_freeze_noirq NULL #define pm_generic_freeze_late NULL #define pm_generic_freeze NULL #define pm_generic_thaw_noirq NULL #define pm_generic_thaw_early NULL #define pm_generic_thaw NULL #define pm_generic_restore_noirq NULL #define pm_generic_restore_early NULL #define pm_generic_restore NULL #define pm_generic_poweroff_noirq NULL #define pm_generic_poweroff_late NULL #define pm_generic_poweroff NULL #define pm_generic_complete NULL #endif /* !CONFIG_PM_SLEEP */ /* How to reorder dpm_list after device_move() */ enum dpm_order { DPM_ORDER_NONE, DPM_ORDER_DEV_AFTER_PARENT, DPM_ORDER_PARENT_BEFORE_DEV, DPM_ORDER_DEV_LAST, }; #endif /* _LINUX_PM_H */ ================================================ FILE: t/tree/include/linux/pm_wakeup.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * pm_wakeup.h - Power management wakeup interface * * Copyright (C) 2008 Alan Stern * Copyright (C) 2010 Rafael J. Wysocki, Novell Inc. */ #ifndef _LINUX_PM_WAKEUP_H #define _LINUX_PM_WAKEUP_H #ifndef _DEVICE_H_ # error "please don't include this file directly" #endif #include struct wake_irq; /** * struct wakeup_source - Representation of wakeup sources * * @name: Name of the wakeup source * @id: Wakeup source id * @entry: Wakeup source list entry * @lock: Wakeup source lock * @wakeirq: Optional device specific wakeirq * @timer: Wakeup timer list * @timer_expires: Wakeup timer expiration * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. * @last_time: Monotonic clock when the wakeup source's was touched last time. * @prevent_sleep_time: Total time this source has been preventing autosleep. * @event_count: Number of signaled wakeup events. * @active_count: Number of times the wakeup source was activated. * @relax_count: Number of times the wakeup source was deactivated. * @expire_count: Number of times the wakeup source's timeout has expired. * @wakeup_count: Number of times the wakeup source might abort suspend. * @dev: Struct device for sysfs statistics about the wakeup source. * @active: Status of the wakeup source. * @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time. */ struct wakeup_source { const char *name; int id; struct list_head entry; spinlock_t lock; struct wake_irq *wakeirq; struct timer_list timer; unsigned long timer_expires; ktime_t total_time; ktime_t max_time; ktime_t last_time; ktime_t start_prevent_time; ktime_t prevent_sleep_time; unsigned long event_count; unsigned long active_count; unsigned long relax_count; unsigned long expire_count; unsigned long wakeup_count; struct device *dev; bool active:1; bool autosleep_enabled:1; }; #ifdef CONFIG_PM_SLEEP /* * Changes to device_may_wakeup take effect on the next pm state change. */ static inline bool device_can_wakeup(struct device *dev) { return dev->power.can_wakeup; } static inline bool device_may_wakeup(struct device *dev) { return dev->power.can_wakeup && !!dev->power.wakeup; } static inline void device_set_wakeup_path(struct device *dev) { dev->power.wakeup_path = true; } /* drivers/base/power/wakeup.c */ extern struct wakeup_source *wakeup_source_create(const char *name); extern void wakeup_source_destroy(struct wakeup_source *ws); extern void wakeup_source_add(struct wakeup_source *ws); extern void wakeup_source_remove(struct wakeup_source *ws); extern struct wakeup_source *wakeup_source_register(struct device *dev, const char *name); extern void wakeup_source_unregister(struct wakeup_source *ws); extern int device_wakeup_enable(struct device *dev); extern int device_wakeup_disable(struct device *dev); extern void device_set_wakeup_capable(struct device *dev, bool capable); extern int device_init_wakeup(struct device *dev, bool val); extern int device_set_wakeup_enable(struct device *dev, bool enable); extern void __pm_stay_awake(struct wakeup_source *ws); extern void pm_stay_awake(struct device *dev); extern void __pm_relax(struct wakeup_source *ws); extern void pm_relax(struct device *dev); extern void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard); extern void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard); #else /* !CONFIG_PM_SLEEP */ static inline void device_set_wakeup_capable(struct device *dev, bool capable) { dev->power.can_wakeup = capable; } static inline bool device_can_wakeup(struct device *dev) { return dev->power.can_wakeup; } static inline struct wakeup_source *wakeup_source_create(const char *name) { return NULL; } static inline void wakeup_source_destroy(struct wakeup_source *ws) {} static inline void wakeup_source_add(struct wakeup_source *ws) {} static inline void wakeup_source_remove(struct wakeup_source *ws) {} static inline struct wakeup_source *wakeup_source_register(struct device *dev, const char *name) { return NULL; } static inline void wakeup_source_unregister(struct wakeup_source *ws) {} static inline int device_wakeup_enable(struct device *dev) { dev->power.should_wakeup = true; return 0; } static inline int device_wakeup_disable(struct device *dev) { dev->power.should_wakeup = false; return 0; } static inline int device_set_wakeup_enable(struct device *dev, bool enable) { dev->power.should_wakeup = enable; return 0; } static inline int device_init_wakeup(struct device *dev, bool val) { device_set_wakeup_capable(dev, val); device_set_wakeup_enable(dev, val); return 0; } static inline bool device_may_wakeup(struct device *dev) { return dev->power.can_wakeup && dev->power.should_wakeup; } static inline void device_set_wakeup_path(struct device *dev) {} static inline void __pm_stay_awake(struct wakeup_source *ws) {} static inline void pm_stay_awake(struct device *dev) {} static inline void __pm_relax(struct wakeup_source *ws) {} static inline void pm_relax(struct device *dev) {} static inline void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard) {} static inline void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard) {} #endif /* !CONFIG_PM_SLEEP */ static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) { return pm_wakeup_ws_event(ws, msec, false); } static inline void pm_wakeup_event(struct device *dev, unsigned int msec) { return pm_wakeup_dev_event(dev, msec, false); } static inline void pm_wakeup_hard_event(struct device *dev) { return pm_wakeup_dev_event(dev, 0, true); } #endif /* _LINUX_PM_WAKEUP_H */ ================================================ FILE: t/tree/include/linux/radix-tree.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2001 Momchil Velikov * Portions Copyright (C) 2001 Christoph Hellwig * Copyright (C) 2006 Nick Piggin * Copyright (C) 2012 Konstantin Khlebnikov */ #ifndef _LINUX_RADIX_TREE_H #define _LINUX_RADIX_TREE_H #include #include #include #include #include #include #include #include /* Keep unconverted code working */ #define radix_tree_root xarray #define radix_tree_node xa_node /* * The bottom two bits of the slot determine how the remaining bits in the * slot are interpreted: * * 00 - data pointer * 10 - internal entry * x1 - value entry * * The internal entry may be a pointer to the next level in the tree, a * sibling entry, or an indicator that the entry in this slot has been moved * to another location in the tree and the lookup should be restarted. While * NULL fits the 'data pointer' pattern, it means that there is no entry in * the tree for this index (no matter what level of the tree it is found at). * This means that storing a NULL entry in the tree is the same as deleting * the entry from the tree. */ #define RADIX_TREE_ENTRY_MASK 3UL #define RADIX_TREE_INTERNAL_NODE 2UL static inline bool radix_tree_is_internal_node(void *ptr) { return ((unsigned long)ptr & RADIX_TREE_ENTRY_MASK) == RADIX_TREE_INTERNAL_NODE; } /*** radix-tree API starts here ***/ #define RADIX_TREE_MAP_SHIFT XA_CHUNK_SHIFT #define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) #define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) #define RADIX_TREE_MAX_TAGS XA_MAX_MARKS #define RADIX_TREE_TAG_LONGS XA_MARK_LONGS #define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) #define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \ RADIX_TREE_MAP_SHIFT)) /* The IDR tag is stored in the low bits of xa_flags */ #define ROOT_IS_IDR ((__force gfp_t)4) /* The top bits of xa_flags are used to store the root tags */ #define ROOT_TAG_SHIFT (__GFP_BITS_SHIFT) #define RADIX_TREE_INIT(name, mask) XARRAY_INIT(name, mask) #define RADIX_TREE(name, mask) \ struct radix_tree_root name = RADIX_TREE_INIT(name, mask) #define INIT_RADIX_TREE(root, mask) xa_init_flags(root, mask) static inline bool radix_tree_empty(const struct radix_tree_root *root) { return root->xa_head == NULL; } /** * struct radix_tree_iter - radix tree iterator state * * @index: index of current slot * @next_index: one beyond the last index for this chunk * @tags: bit-mask for tag-iterating * @node: node that contains current slot * * This radix tree iterator works in terms of "chunks" of slots. A chunk is a * subinterval of slots contained within one radix tree leaf node. It is * described by a pointer to its first slot and a struct radix_tree_iter * which holds the chunk's position in the tree and its size. For tagged * iteration radix_tree_iter also holds the slots' bit-mask for one chosen * radix tree tag. */ struct radix_tree_iter { unsigned long index; unsigned long next_index; unsigned long tags; struct radix_tree_node *node; }; /** * Radix-tree synchronization * * The radix-tree API requires that users provide all synchronisation (with * specific exceptions, noted below). * * Synchronization of access to the data items being stored in the tree, and * management of their lifetimes must be completely managed by API users. * * For API usage, in general, * - any function _modifying_ the tree or tags (inserting or deleting * items, setting or clearing tags) must exclude other modifications, and * exclude any functions reading the tree. * - any function _reading_ the tree or tags (looking up items or tags, * gang lookups) must exclude modifications to the tree, but may occur * concurrently with other readers. * * The notable exceptions to this rule are the following functions: * __radix_tree_lookup * radix_tree_lookup * radix_tree_lookup_slot * radix_tree_tag_get * radix_tree_gang_lookup * radix_tree_gang_lookup_tag * radix_tree_gang_lookup_tag_slot * radix_tree_tagged * * The first 7 functions are able to be called locklessly, using RCU. The * caller must ensure calls to these functions are made within rcu_read_lock() * regions. Other readers (lock-free or otherwise) and modifications may be * running concurrently. * * It is still required that the caller manage the synchronization and lifetimes * of the items. So if RCU lock-free lookups are used, typically this would mean * that the items have their own locks, or are amenable to lock-free access; and * that the items are freed by RCU (or only freed after having been deleted from * the radix tree *and* a synchronize_rcu() grace period). * * (Note, rcu_assign_pointer and rcu_dereference are not needed to control * access to data items when inserting into or looking up from the radix tree) * * Note that the value returned by radix_tree_tag_get() may not be relied upon * if only the RCU read lock is held. Functions to set/clear tags and to * delete nodes running concurrently with it may affect its result such that * two consecutive reads in the same locked section may return different * values. If reliability is required, modification functions must also be * excluded from concurrency. * * radix_tree_tagged is able to be called without locking or RCU. */ /** * radix_tree_deref_slot - dereference a slot * @slot: slot pointer, returned by radix_tree_lookup_slot * * For use with radix_tree_lookup_slot(). Caller must hold tree at least read * locked across slot lookup and dereference. Not required if write lock is * held (ie. items cannot be concurrently inserted). * * radix_tree_deref_retry must be used to confirm validity of the pointer if * only the read lock is held. * * Return: entry stored in that slot. */ static inline void *radix_tree_deref_slot(void __rcu **slot) { return rcu_dereference(*slot); } /** * radix_tree_deref_slot_protected - dereference a slot with tree lock held * @slot: slot pointer, returned by radix_tree_lookup_slot * * Similar to radix_tree_deref_slot. The caller does not hold the RCU read * lock but it must hold the tree lock to prevent parallel updates. * * Return: entry stored in that slot. */ static inline void *radix_tree_deref_slot_protected(void __rcu **slot, spinlock_t *treelock) { return rcu_dereference_protected(*slot, lockdep_is_held(treelock)); } /** * radix_tree_deref_retry - check radix_tree_deref_slot * @arg: pointer returned by radix_tree_deref_slot * Returns: 0 if retry is not required, otherwise retry is required * * radix_tree_deref_retry must be used with radix_tree_deref_slot. */ static inline int radix_tree_deref_retry(void *arg) { return unlikely(radix_tree_is_internal_node(arg)); } /** * radix_tree_exception - radix_tree_deref_slot returned either exception? * @arg: value returned by radix_tree_deref_slot * Returns: 0 if well-aligned pointer, non-0 if either kind of exception. */ static inline int radix_tree_exception(void *arg) { return unlikely((unsigned long)arg & RADIX_TREE_ENTRY_MASK); } int radix_tree_insert(struct radix_tree_root *, unsigned long index, void *); void *__radix_tree_lookup(const struct radix_tree_root *, unsigned long index, struct radix_tree_node **nodep, void __rcu ***slotp); void *radix_tree_lookup(const struct radix_tree_root *, unsigned long); void __rcu **radix_tree_lookup_slot(const struct radix_tree_root *, unsigned long index); void __radix_tree_replace(struct radix_tree_root *, struct radix_tree_node *, void __rcu **slot, void *entry); void radix_tree_iter_replace(struct radix_tree_root *, const struct radix_tree_iter *, void __rcu **slot, void *entry); void radix_tree_replace_slot(struct radix_tree_root *, void __rcu **slot, void *entry); void radix_tree_iter_delete(struct radix_tree_root *, struct radix_tree_iter *iter, void __rcu **slot); void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(const struct radix_tree_root *, void **results, unsigned long first_index, unsigned int max_items); int radix_tree_preload(gfp_t gfp_mask); int radix_tree_maybe_preload(gfp_t gfp_mask); void radix_tree_init(void); void *radix_tree_tag_set(struct radix_tree_root *, unsigned long index, unsigned int tag); void *radix_tree_tag_clear(struct radix_tree_root *, unsigned long index, unsigned int tag); int radix_tree_tag_get(const struct radix_tree_root *, unsigned long index, unsigned int tag); void radix_tree_iter_tag_clear(struct radix_tree_root *, const struct radix_tree_iter *iter, unsigned int tag); unsigned int radix_tree_gang_lookup_tag(const struct radix_tree_root *, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag); unsigned int radix_tree_gang_lookup_tag_slot(const struct radix_tree_root *, void __rcu ***results, unsigned long first_index, unsigned int max_items, unsigned int tag); int radix_tree_tagged(const struct radix_tree_root *, unsigned int tag); static inline void radix_tree_preload_end(void) { preempt_enable(); } void __rcu **idr_get_free(struct radix_tree_root *root, struct radix_tree_iter *iter, gfp_t gfp, unsigned long max); enum { RADIX_TREE_ITER_TAG_MASK = 0x0f, /* tag index in lower nybble */ RADIX_TREE_ITER_TAGGED = 0x10, /* lookup tagged slots */ RADIX_TREE_ITER_CONTIG = 0x20, /* stop at first hole */ }; /** * radix_tree_iter_init - initialize radix tree iterator * * @iter: pointer to iterator state * @start: iteration starting index * Returns: NULL */ static __always_inline void __rcu ** radix_tree_iter_init(struct radix_tree_iter *iter, unsigned long start) { /* * Leave iter->tags uninitialized. radix_tree_next_chunk() will fill it * in the case of a successful tagged chunk lookup. If the lookup was * unsuccessful or non-tagged then nobody cares about ->tags. * * Set index to zero to bypass next_index overflow protection. * See the comment in radix_tree_next_chunk() for details. */ iter->index = 0; iter->next_index = start; return NULL; } /** * radix_tree_next_chunk - find next chunk of slots for iteration * * @root: radix tree root * @iter: iterator state * @flags: RADIX_TREE_ITER_* flags and tag index * Returns: pointer to chunk first slot, or NULL if there no more left * * This function looks up the next chunk in the radix tree starting from * @iter->next_index. It returns a pointer to the chunk's first slot. * Also it fills @iter with data about chunk: position in the tree (index), * its end (next_index), and constructs a bit mask for tagged iterating (tags). */ void __rcu **radix_tree_next_chunk(const struct radix_tree_root *, struct radix_tree_iter *iter, unsigned flags); /** * radix_tree_iter_lookup - look up an index in the radix tree * @root: radix tree root * @iter: iterator state * @index: key to look up * * If @index is present in the radix tree, this function returns the slot * containing it and updates @iter to describe the entry. If @index is not * present, it returns NULL. */ static inline void __rcu ** radix_tree_iter_lookup(const struct radix_tree_root *root, struct radix_tree_iter *iter, unsigned long index) { radix_tree_iter_init(iter, index); return radix_tree_next_chunk(root, iter, RADIX_TREE_ITER_CONTIG); } /** * radix_tree_iter_retry - retry this chunk of the iteration * @iter: iterator state * * If we iterate over a tree protected only by the RCU lock, a race * against deletion or creation may result in seeing a slot for which * radix_tree_deref_retry() returns true. If so, call this function * and continue the iteration. */ static inline __must_check void __rcu **radix_tree_iter_retry(struct radix_tree_iter *iter) { iter->next_index = iter->index; iter->tags = 0; return NULL; } static inline unsigned long __radix_tree_iter_add(struct radix_tree_iter *iter, unsigned long slots) { return iter->index + slots; } /** * radix_tree_iter_resume - resume iterating when the chunk may be invalid * @slot: pointer to current slot * @iter: iterator state * Returns: New slot pointer * * If the iterator needs to release then reacquire a lock, the chunk may * have been invalidated by an insertion or deletion. Call this function * before releasing the lock to continue the iteration from the next index. */ void __rcu **__must_check radix_tree_iter_resume(void __rcu **slot, struct radix_tree_iter *iter); /** * radix_tree_chunk_size - get current chunk size * * @iter: pointer to radix tree iterator * Returns: current chunk size */ static __always_inline long radix_tree_chunk_size(struct radix_tree_iter *iter) { return iter->next_index - iter->index; } /** * radix_tree_next_slot - find next slot in chunk * * @slot: pointer to current slot * @iter: pointer to interator state * @flags: RADIX_TREE_ITER_*, should be constant * Returns: pointer to next slot, or NULL if there no more left * * This function updates @iter->index in the case of a successful lookup. * For tagged lookup it also eats @iter->tags. * * There are several cases where 'slot' can be passed in as NULL to this * function. These cases result from the use of radix_tree_iter_resume() or * radix_tree_iter_retry(). In these cases we don't end up dereferencing * 'slot' because either: * a) we are doing tagged iteration and iter->tags has been set to 0, or * b) we are doing non-tagged iteration, and iter->index and iter->next_index * have been set up so that radix_tree_chunk_size() returns 1 or 0. */ static __always_inline void __rcu **radix_tree_next_slot(void __rcu **slot, struct radix_tree_iter *iter, unsigned flags) { if (flags & RADIX_TREE_ITER_TAGGED) { iter->tags >>= 1; if (unlikely(!iter->tags)) return NULL; if (likely(iter->tags & 1ul)) { iter->index = __radix_tree_iter_add(iter, 1); slot++; goto found; } if (!(flags & RADIX_TREE_ITER_CONTIG)) { unsigned offset = __ffs(iter->tags); iter->tags >>= offset++; iter->index = __radix_tree_iter_add(iter, offset); slot += offset; goto found; } } else { long count = radix_tree_chunk_size(iter); while (--count > 0) { slot++; iter->index = __radix_tree_iter_add(iter, 1); if (likely(*slot)) goto found; if (flags & RADIX_TREE_ITER_CONTIG) { /* forbid switching to the next chunk */ iter->next_index = 0; break; } } } return NULL; found: return slot; } /** * radix_tree_for_each_slot - iterate over non-empty slots * * @slot: the void** variable for pointer to slot * @root: the struct radix_tree_root pointer * @iter: the struct radix_tree_iter pointer * @start: iteration starting index * * @slot points to radix tree slot, @iter->index contains its index. */ #define radix_tree_for_each_slot(slot, root, iter, start) \ for (slot = radix_tree_iter_init(iter, start) ; \ slot || (slot = radix_tree_next_chunk(root, iter, 0)) ; \ slot = radix_tree_next_slot(slot, iter, 0)) /** * radix_tree_for_each_tagged - iterate over tagged slots * * @slot: the void** variable for pointer to slot * @root: the struct radix_tree_root pointer * @iter: the struct radix_tree_iter pointer * @start: iteration starting index * @tag: tag index * * @slot points to radix tree slot, @iter->index contains its index. */ #define radix_tree_for_each_tagged(slot, root, iter, start, tag) \ for (slot = radix_tree_iter_init(iter, start) ; \ slot || (slot = radix_tree_next_chunk(root, iter, \ RADIX_TREE_ITER_TAGGED | tag)) ; \ slot = radix_tree_next_slot(slot, iter, \ RADIX_TREE_ITER_TAGGED | tag)) #endif /* _LINUX_RADIX_TREE_H */ ================================================ FILE: t/tree/include/linux/rbtree.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Red Black Trees (C) 1999 Andrea Arcangeli linux/include/linux/rbtree.h To use rbtrees you'll have to implement your own insert and search cores. This will avoid us to use callbacks and to drop drammatically performances. I know it's not the cleaner way, but in C (not in C++) to get performances and genericity... See Documentation/rbtree.txt for documentation and samples. */ #ifndef _LINUX_RBTREE_H #define _LINUX_RBTREE_H #include #include #include struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); /* The alignment might seem pointless, but allegedly CRIS needs it */ struct rb_root { struct rb_node *rb_node; }; #define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) #define RB_ROOT (struct rb_root) { NULL, } #define rb_entry(ptr, type, member) container_of(ptr, type, member) #define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == NULL) /* 'empty' nodes are nodes that are known not to be inserted in an rbtree */ #define RB_EMPTY_NODE(node) \ ((node)->__rb_parent_color == (unsigned long)(node)) #define RB_CLEAR_NODE(node) \ ((node)->__rb_parent_color = (unsigned long)(node)) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(const struct rb_node *); extern struct rb_node *rb_prev(const struct rb_node *); extern struct rb_node *rb_first(const struct rb_root *); extern struct rb_node *rb_last(const struct rb_root *); /* Postorder iteration - always visit the parent after its children */ extern struct rb_node *rb_first_postorder(const struct rb_root *); extern struct rb_node *rb_next_postorder(const struct rb_node *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); extern void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, struct rb_root *root); static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, struct rb_node **rb_link) { node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } static inline void rb_link_node_rcu(struct rb_node *node, struct rb_node *parent, struct rb_node **rb_link) { node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL; rcu_assign_pointer(*rb_link, node); } #define rb_entry_safe(ptr, type, member) \ ({ typeof(ptr) ____ptr = (ptr); \ ____ptr ? rb_entry(____ptr, type, member) : NULL; \ }) /** * rbtree_postorder_for_each_entry_safe - iterate in post-order over rb_root of * given type allowing the backing memory of @pos to be invalidated * * @pos: the 'type *' to use as a loop cursor. * @n: another 'type *' to use as temporary storage * @root: 'rb_root *' of the rbtree. * @field: the name of the rb_node field within 'type'. * * rbtree_postorder_for_each_entry_safe() provides a similar guarantee as * list_for_each_entry_safe() and allows the iteration to continue independent * of changes to @pos by the body of the loop. * * Note, however, that it cannot handle other modifications that re-order the * rbtree it is iterating over. This includes calling rb_erase() on @pos, as * rb_erase() may rebalance the tree, causing us to miss some nodes. */ #define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \ for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \ pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \ typeof(*pos), field); 1; }); \ pos = n) /* * Leftmost-cached rbtrees. * * We do not cache the rightmost node based on footprint * size vs number of potential users that could benefit * from O(1) rb_last(). Just not worth it, users that want * this feature can always implement the logic explicitly. * Furthermore, users that want to cache both pointers may * find it a bit asymmetric, but that's ok. */ struct rb_root_cached { struct rb_root rb_root; struct rb_node *rb_leftmost; }; #define RB_ROOT_CACHED (struct rb_root_cached) { {NULL, }, NULL } /* Same as rb_first(), but O(1) */ #define rb_first_cached(root) (root)->rb_leftmost static inline void rb_insert_color_cached(struct rb_node *node, struct rb_root_cached *root, bool leftmost) { if (leftmost) root->rb_leftmost = node; rb_insert_color(node, &root->rb_root); } static inline void rb_erase_cached(struct rb_node *node, struct rb_root_cached *root) { if (root->rb_leftmost == node) root->rb_leftmost = rb_next(node); rb_erase(node, &root->rb_root); } static inline void rb_replace_node_cached(struct rb_node *victim, struct rb_node *new, struct rb_root_cached *root) { if (root->rb_leftmost == victim) root->rb_leftmost = new; rb_replace_node(victim, new, &root->rb_root); } #endif /* _LINUX_RBTREE_H */ ================================================ FILE: t/tree/include/linux/rcu_node_tree.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * RCU node combining tree definitions. These are used to compute * global attributes while avoiding common-case global contention. A key * property that these computations rely on is a tournament-style approach * where only one of the tasks contending a lower level in the tree need * advance to the next higher level. If properly configured, this allows * unlimited scalability while maintaining a constant level of contention * on the root node. * * This seemingly RCU-private file must be available to SRCU users * because the size of the TREE SRCU srcu_struct structure depends * on these definitions. * * Copyright IBM Corporation, 2017 * * Author: Paul E. McKenney */ #ifndef __LINUX_RCU_NODE_TREE_H #define __LINUX_RCU_NODE_TREE_H /* * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and * CONFIG_RCU_FANOUT_LEAF. * In theory, it should be possible to add more levels straightforwardly. * In practice, this did work well going from three levels to four. * Of course, your mileage may vary. */ #ifdef CONFIG_RCU_FANOUT #define RCU_FANOUT CONFIG_RCU_FANOUT #else /* #ifdef CONFIG_RCU_FANOUT */ # ifdef CONFIG_64BIT # define RCU_FANOUT 64 # else # define RCU_FANOUT 32 # endif #endif /* #else #ifdef CONFIG_RCU_FANOUT */ #ifdef CONFIG_RCU_FANOUT_LEAF #define RCU_FANOUT_LEAF CONFIG_RCU_FANOUT_LEAF #else /* #ifdef CONFIG_RCU_FANOUT_LEAF */ #define RCU_FANOUT_LEAF 16 #endif /* #else #ifdef CONFIG_RCU_FANOUT_LEAF */ #define RCU_FANOUT_1 (RCU_FANOUT_LEAF) #define RCU_FANOUT_2 (RCU_FANOUT_1 * RCU_FANOUT) #define RCU_FANOUT_3 (RCU_FANOUT_2 * RCU_FANOUT) #define RCU_FANOUT_4 (RCU_FANOUT_3 * RCU_FANOUT) #if NR_CPUS <= RCU_FANOUT_1 # define RCU_NUM_LVLS 1 # define NUM_RCU_LVL_0 1 # define NUM_RCU_NODES NUM_RCU_LVL_0 # define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0 } # define RCU_NODE_NAME_INIT { "rcu_node_0" } # define RCU_FQS_NAME_INIT { "rcu_node_fqs_0" } #elif NR_CPUS <= RCU_FANOUT_2 # define RCU_NUM_LVLS 2 # define NUM_RCU_LVL_0 1 # define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) # define NUM_RCU_NODES (NUM_RCU_LVL_0 + NUM_RCU_LVL_1) # define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0, NUM_RCU_LVL_1 } # define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1" } # define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1" } #elif NR_CPUS <= RCU_FANOUT_3 # define RCU_NUM_LVLS 3 # define NUM_RCU_LVL_0 1 # define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) # define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) # define NUM_RCU_NODES (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2) # define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0, NUM_RCU_LVL_1, NUM_RCU_LVL_2 } # define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1", "rcu_node_2" } # define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2" } #elif NR_CPUS <= RCU_FANOUT_4 # define RCU_NUM_LVLS 4 # define NUM_RCU_LVL_0 1 # define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_3) # define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) # define NUM_RCU_LVL_3 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) # define NUM_RCU_NODES (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3) # define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0, NUM_RCU_LVL_1, NUM_RCU_LVL_2, NUM_RCU_LVL_3 } # define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1", "rcu_node_2", "rcu_node_3" } # define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2", "rcu_node_fqs_3" } #else # error "CONFIG_RCU_FANOUT insufficient for NR_CPUS" #endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */ #endif /* __LINUX_RCU_NODE_TREE_H */ ================================================ FILE: t/tree/include/linux/rcu_segcblist.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * RCU segmented callback lists * * This seemingly RCU-private file must be available to SRCU users * because the size of the TREE SRCU srcu_struct structure depends * on these definitions. * * Copyright IBM Corporation, 2017 * * Authors: Paul E. McKenney */ #ifndef __INCLUDE_LINUX_RCU_SEGCBLIST_H #define __INCLUDE_LINUX_RCU_SEGCBLIST_H #include #include /* Simple unsegmented callback lists. */ struct rcu_cblist { struct rcu_head *head; struct rcu_head **tail; long len; long len_lazy; }; #define RCU_CBLIST_INITIALIZER(n) { .head = NULL, .tail = &n.head } /* Complicated segmented callback lists. ;-) */ /* * Index values for segments in rcu_segcblist structure. * * The segments are as follows: * * [head, *tails[RCU_DONE_TAIL]): * Callbacks whose grace period has elapsed, and thus can be invoked. * [*tails[RCU_DONE_TAIL], *tails[RCU_WAIT_TAIL]): * Callbacks waiting for the current GP from the current CPU's viewpoint. * [*tails[RCU_WAIT_TAIL], *tails[RCU_NEXT_READY_TAIL]): * Callbacks that arrived before the next GP started, again from * the current CPU's viewpoint. These can be handled by the next GP. * [*tails[RCU_NEXT_READY_TAIL], *tails[RCU_NEXT_TAIL]): * Callbacks that might have arrived after the next GP started. * There is some uncertainty as to when a given GP starts and * ends, but a CPU knows the exact times if it is the one starting * or ending the GP. Other CPUs know that the previous GP ends * before the next one starts. * * Note that RCU_WAIT_TAIL cannot be empty unless RCU_NEXT_READY_TAIL is also * empty. * * The ->gp_seq[] array contains the grace-period number at which the * corresponding segment of callbacks will be ready to invoke. A given * element of this array is meaningful only when the corresponding segment * is non-empty, and it is never valid for RCU_DONE_TAIL (whose callbacks * are already ready to invoke) or for RCU_NEXT_TAIL (whose callbacks have * not yet been assigned a grace-period number). */ #define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */ #define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */ #define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */ #define RCU_NEXT_TAIL 3 #define RCU_CBLIST_NSEGS 4 struct rcu_segcblist { struct rcu_head *head; struct rcu_head **tails[RCU_CBLIST_NSEGS]; unsigned long gp_seq[RCU_CBLIST_NSEGS]; #ifdef CONFIG_RCU_NOCB_CPU atomic_long_t len; #else long len; #endif long len_lazy; u8 enabled; u8 offloaded; }; #define RCU_SEGCBLIST_INITIALIZER(n) \ { \ .head = NULL, \ .tails[RCU_DONE_TAIL] = &n.head, \ .tails[RCU_WAIT_TAIL] = &n.head, \ .tails[RCU_NEXT_READY_TAIL] = &n.head, \ .tails[RCU_NEXT_TAIL] = &n.head, \ } #endif /* __INCLUDE_LINUX_RCU_SEGCBLIST_H */ ================================================ FILE: t/tree/include/linux/rcu_sync.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * RCU-based infrastructure for lightweight reader-writer locking * * Copyright (c) 2015, Red Hat, Inc. * * Author: Oleg Nesterov */ #ifndef _LINUX_RCU_SYNC_H_ #define _LINUX_RCU_SYNC_H_ #include #include /* Structure to mediate between updaters and fastpath-using readers. */ struct rcu_sync { int gp_state; int gp_count; wait_queue_head_t gp_wait; struct rcu_head cb_head; }; /** * rcu_sync_is_idle() - Are readers permitted to use their fastpaths? * @rsp: Pointer to rcu_sync structure to use for synchronization * * Returns true if readers are permitted to use their fastpaths. Must be * invoked within some flavor of RCU read-side critical section. */ static inline bool rcu_sync_is_idle(struct rcu_sync *rsp) { RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(), "suspicious rcu_sync_is_idle() usage"); return !READ_ONCE(rsp->gp_state); /* GP_IDLE */ } extern void rcu_sync_init(struct rcu_sync *); extern void rcu_sync_enter_start(struct rcu_sync *); extern void rcu_sync_enter(struct rcu_sync *); extern void rcu_sync_exit(struct rcu_sync *); extern void rcu_sync_dtor(struct rcu_sync *); #define __RCU_SYNC_INITIALIZER(name) { \ .gp_state = 0, \ .gp_count = 0, \ .gp_wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.gp_wait), \ } #define DEFINE_RCU_SYNC(name) \ struct rcu_sync name = __RCU_SYNC_INITIALIZER(name) #endif /* _LINUX_RCU_SYNC_H_ */ ================================================ FILE: t/tree/include/linux/rcupdate.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Read-Copy Update mechanism for mutual exclusion * * Copyright IBM Corporation, 2001 * * Author: Dipankar Sarma * * Based on the original work by Paul McKenney * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. * Papers: * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf * http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001) * * For detailed explanation of Read-Copy Update mechanism see - * http://lse.sourceforge.net/locking/rcupdate.html * */ #ifndef __LINUX_RCUPDATE_H #define __LINUX_RCUPDATE_H #include #include #include #include #include #include #include #include #include #define ULONG_CMP_GE(a, b) (ULONG_MAX / 2 >= (a) - (b)) #define ULONG_CMP_LT(a, b) (ULONG_MAX / 2 < (a) - (b)) #define ulong2long(a) (*(long *)(&(a))) /* Exported common interfaces */ void call_rcu(struct rcu_head *head, rcu_callback_t func); void rcu_barrier_tasks(void); void synchronize_rcu(void); #ifdef CONFIG_PREEMPT_RCU void __rcu_read_lock(void); void __rcu_read_unlock(void); /* * Defined as a macro as it is a very low level header included from * areas that don't even know about current. This gives the rcu_read_lock() * nesting depth, but makes sense only if CONFIG_PREEMPT_RCU -- in other * types of kernel builds, the rcu_read_lock() nesting depth is unknowable. */ #define rcu_preempt_depth() (current->rcu_read_lock_nesting) #else /* #ifdef CONFIG_PREEMPT_RCU */ static inline void __rcu_read_lock(void) { preempt_disable(); } static inline void __rcu_read_unlock(void) { preempt_enable(); } static inline int rcu_preempt_depth(void) { return 0; } #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ /* Internal to kernel */ void rcu_init(void); extern int rcu_scheduler_active __read_mostly; void rcu_sched_clock_irq(int user); void rcu_report_dead(unsigned int cpu); void rcutree_migrate_callbacks(int cpu); #ifdef CONFIG_RCU_STALL_COMMON void rcu_sysrq_start(void); void rcu_sysrq_end(void); #else /* #ifdef CONFIG_RCU_STALL_COMMON */ static inline void rcu_sysrq_start(void) { } static inline void rcu_sysrq_end(void) { } #endif /* #else #ifdef CONFIG_RCU_STALL_COMMON */ #ifdef CONFIG_NO_HZ_FULL void rcu_user_enter(void); void rcu_user_exit(void); #else static inline void rcu_user_enter(void) { } static inline void rcu_user_exit(void) { } #endif /* CONFIG_NO_HZ_FULL */ #ifdef CONFIG_RCU_NOCB_CPU void rcu_init_nohz(void); #else /* #ifdef CONFIG_RCU_NOCB_CPU */ static inline void rcu_init_nohz(void) { } #endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ /** * RCU_NONIDLE - Indicate idle-loop code that needs RCU readers * @a: Code that RCU needs to pay attention to. * * RCU read-side critical sections are forbidden in the inner idle loop, * that is, between the rcu_idle_enter() and the rcu_idle_exit() -- RCU * will happily ignore any such read-side critical sections. However, * things like powertop need tracepoints in the inner idle loop. * * This macro provides the way out: RCU_NONIDLE(do_something_with_RCU()) * will tell RCU that it needs to pay attention, invoke its argument * (in this example, calling the do_something_with_RCU() function), * and then tell RCU to go back to ignoring this CPU. It is permissible * to nest RCU_NONIDLE() wrappers, but not indefinitely (but the limit is * on the order of a million or so, even on 32-bit systems). It is * not legal to block within RCU_NONIDLE(), nor is it permissible to * transfer control either into or out of RCU_NONIDLE()'s statement. */ #define RCU_NONIDLE(a) \ do { \ rcu_irq_enter_irqson(); \ do { a; } while (0); \ rcu_irq_exit_irqson(); \ } while (0) /* * Note a quasi-voluntary context switch for RCU-tasks's benefit. * This is a macro rather than an inline function to avoid #include hell. */ #ifdef CONFIG_TASKS_RCU #define rcu_tasks_qs(t) \ do { \ if (READ_ONCE((t)->rcu_tasks_holdout)) \ WRITE_ONCE((t)->rcu_tasks_holdout, false); \ } while (0) #define rcu_note_voluntary_context_switch(t) rcu_tasks_qs(t) void call_rcu_tasks(struct rcu_head *head, rcu_callback_t func); void synchronize_rcu_tasks(void); void exit_tasks_rcu_start(void); void exit_tasks_rcu_finish(void); #else /* #ifdef CONFIG_TASKS_RCU */ #define rcu_tasks_qs(t) do { } while (0) #define rcu_note_voluntary_context_switch(t) do { } while (0) #define call_rcu_tasks call_rcu #define synchronize_rcu_tasks synchronize_rcu static inline void exit_tasks_rcu_start(void) { } static inline void exit_tasks_rcu_finish(void) { } #endif /* #else #ifdef CONFIG_TASKS_RCU */ /** * cond_resched_tasks_rcu_qs - Report potential quiescent states to RCU * * This macro resembles cond_resched(), except that it is defined to * report potential quiescent states to RCU-tasks even if the cond_resched() * machinery were to be shut off, as some advocate for PREEMPT kernels. */ #define cond_resched_tasks_rcu_qs() \ do { \ rcu_tasks_qs(current); \ cond_resched(); \ } while (0) /* * Infrastructure to implement the synchronize_() primitives in * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. */ #if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU) #include #elif defined(CONFIG_TINY_RCU) #include #else #error "Unknown RCU implementation specified to kernel configuration" #endif /* * The init_rcu_head_on_stack() and destroy_rcu_head_on_stack() calls * are needed for dynamic initialization and destruction of rcu_head * on the stack, and init_rcu_head()/destroy_rcu_head() are needed for * dynamic initialization and destruction of statically allocated rcu_head * structures. However, rcu_head structures allocated dynamically in the * heap don't need any initialization. */ #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD void init_rcu_head(struct rcu_head *head); void destroy_rcu_head(struct rcu_head *head); void init_rcu_head_on_stack(struct rcu_head *head); void destroy_rcu_head_on_stack(struct rcu_head *head); #else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ static inline void init_rcu_head(struct rcu_head *head) { } static inline void destroy_rcu_head(struct rcu_head *head) { } static inline void init_rcu_head_on_stack(struct rcu_head *head) { } static inline void destroy_rcu_head_on_stack(struct rcu_head *head) { } #endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) bool rcu_lockdep_current_cpu_online(void); #else /* #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ static inline bool rcu_lockdep_current_cpu_online(void) { return true; } #endif /* #else #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ #ifdef CONFIG_DEBUG_LOCK_ALLOC static inline void rcu_lock_acquire(struct lockdep_map *map) { lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_); } static inline void rcu_lock_release(struct lockdep_map *map) { lock_release(map, 1, _THIS_IP_); } extern struct lockdep_map rcu_lock_map; extern struct lockdep_map rcu_bh_lock_map; extern struct lockdep_map rcu_sched_lock_map; extern struct lockdep_map rcu_callback_map; int debug_lockdep_rcu_enabled(void); int rcu_read_lock_held(void); int rcu_read_lock_bh_held(void); int rcu_read_lock_sched_held(void); int rcu_read_lock_any_held(void); #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ # define rcu_lock_acquire(a) do { } while (0) # define rcu_lock_release(a) do { } while (0) static inline int rcu_read_lock_held(void) { return 1; } static inline int rcu_read_lock_bh_held(void) { return 1; } static inline int rcu_read_lock_sched_held(void) { return !preemptible(); } static inline int rcu_read_lock_any_held(void) { return !preemptible(); } #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ #ifdef CONFIG_PROVE_RCU /** * RCU_LOCKDEP_WARN - emit lockdep splat if specified condition is met * @c: condition to check * @s: informative message */ #define RCU_LOCKDEP_WARN(c, s) \ do { \ static bool __section(.data.unlikely) __warned; \ if (debug_lockdep_rcu_enabled() && !__warned && (c)) { \ __warned = true; \ lockdep_rcu_suspicious(__FILE__, __LINE__, s); \ } \ } while (0) #if defined(CONFIG_PROVE_RCU) && !defined(CONFIG_PREEMPT_RCU) static inline void rcu_preempt_sleep_check(void) { RCU_LOCKDEP_WARN(lock_is_held(&rcu_lock_map), "Illegal context switch in RCU read-side critical section"); } #else /* #ifdef CONFIG_PROVE_RCU */ static inline void rcu_preempt_sleep_check(void) { } #endif /* #else #ifdef CONFIG_PROVE_RCU */ #define rcu_sleep_check() \ do { \ rcu_preempt_sleep_check(); \ RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map), \ "Illegal context switch in RCU-bh read-side critical section"); \ RCU_LOCKDEP_WARN(lock_is_held(&rcu_sched_lock_map), \ "Illegal context switch in RCU-sched read-side critical section"); \ } while (0) #else /* #ifdef CONFIG_PROVE_RCU */ #define RCU_LOCKDEP_WARN(c, s) do { } while (0) #define rcu_sleep_check() do { } while (0) #endif /* #else #ifdef CONFIG_PROVE_RCU */ /* * Helper functions for rcu_dereference_check(), rcu_dereference_protected() * and rcu_assign_pointer(). Some of these could be folded into their * callers, but they are left separate in order to ease introduction of * multiple pointers markings to match different RCU implementations * (e.g., __srcu), should this make sense in the future. */ #ifdef __CHECKER__ #define rcu_check_sparse(p, space) \ ((void)(((typeof(*p) space *)p) == p)) #else /* #ifdef __CHECKER__ */ #define rcu_check_sparse(p, space) #endif /* #else #ifdef __CHECKER__ */ #define __rcu_access_pointer(p, space) \ ({ \ typeof(*p) *_________p1 = (typeof(*p) *__force)READ_ONCE(p); \ rcu_check_sparse(p, space); \ ((typeof(*p) __force __kernel *)(_________p1)); \ }) #define __rcu_dereference_check(p, c, space) \ ({ \ /* Dependency order vs. p above. */ \ typeof(*p) *________p1 = (typeof(*p) *__force)READ_ONCE(p); \ RCU_LOCKDEP_WARN(!(c), "suspicious rcu_dereference_check() usage"); \ rcu_check_sparse(p, space); \ ((typeof(*p) __force __kernel *)(________p1)); \ }) #define __rcu_dereference_protected(p, c, space) \ ({ \ RCU_LOCKDEP_WARN(!(c), "suspicious rcu_dereference_protected() usage"); \ rcu_check_sparse(p, space); \ ((typeof(*p) __force __kernel *)(p)); \ }) #define rcu_dereference_raw(p) \ ({ \ /* Dependency order vs. p above. */ \ typeof(p) ________p1 = READ_ONCE(p); \ ((typeof(*p) __force __kernel *)(________p1)); \ }) /** * RCU_INITIALIZER() - statically initialize an RCU-protected global variable * @v: The value to statically initialize with. */ #define RCU_INITIALIZER(v) (typeof(*(v)) __force __rcu *)(v) /** * rcu_assign_pointer() - assign to RCU-protected pointer * @p: pointer to assign to * @v: value to assign (publish) * * Assigns the specified value to the specified RCU-protected * pointer, ensuring that any concurrent RCU readers will see * any prior initialization. * * Inserts memory barriers on architectures that require them * (which is most of them), and also prevents the compiler from * reordering the code that initializes the structure after the pointer * assignment. More importantly, this call documents which pointers * will be dereferenced by RCU read-side code. * * In some special cases, you may use RCU_INIT_POINTER() instead * of rcu_assign_pointer(). RCU_INIT_POINTER() is a bit faster due * to the fact that it does not constrain either the CPU or the compiler. * That said, using RCU_INIT_POINTER() when you should have used * rcu_assign_pointer() is a very bad thing that results in * impossible-to-diagnose memory corruption. So please be careful. * See the RCU_INIT_POINTER() comment header for details. * * Note that rcu_assign_pointer() evaluates each of its arguments only * once, appearances notwithstanding. One of the "extra" evaluations * is in typeof() and the other visible only to sparse (__CHECKER__), * neither of which actually execute the argument. As with most cpp * macros, this execute-arguments-only-once property is important, so * please be careful when making changes to rcu_assign_pointer() and the * other macros that it invokes. */ #define rcu_assign_pointer(p, v) \ do { \ uintptr_t _r_a_p__v = (uintptr_t)(v); \ rcu_check_sparse(p, __rcu); \ \ if (__builtin_constant_p(v) && (_r_a_p__v) == (uintptr_t)NULL) \ WRITE_ONCE((p), (typeof(p))(_r_a_p__v)); \ else \ smp_store_release(&p, RCU_INITIALIZER((typeof(p))_r_a_p__v)); \ } while (0) /** * rcu_swap_protected() - swap an RCU and a regular pointer * @rcu_ptr: RCU pointer * @ptr: regular pointer * @c: the conditions under which the dereference will take place * * Perform swap(@rcu_ptr, @ptr) where @rcu_ptr is an RCU-annotated pointer and * @c is the argument that is passed to the rcu_dereference_protected() call * used to read that pointer. */ #define rcu_swap_protected(rcu_ptr, ptr, c) do { \ typeof(ptr) __tmp = rcu_dereference_protected((rcu_ptr), (c)); \ rcu_assign_pointer((rcu_ptr), (ptr)); \ (ptr) = __tmp; \ } while (0) /** * rcu_access_pointer() - fetch RCU pointer with no dereferencing * @p: The pointer to read * * Return the value of the specified RCU-protected pointer, but omit the * lockdep checks for being in an RCU read-side critical section. This is * useful when the value of this pointer is accessed, but the pointer is * not dereferenced, for example, when testing an RCU-protected pointer * against NULL. Although rcu_access_pointer() may also be used in cases * where update-side locks prevent the value of the pointer from changing, * you should instead use rcu_dereference_protected() for this use case. * * It is also permissible to use rcu_access_pointer() when read-side * access to the pointer was removed at least one grace period ago, as * is the case in the context of the RCU callback that is freeing up * the data, or after a synchronize_rcu() returns. This can be useful * when tearing down multi-linked structures after a grace period * has elapsed. */ #define rcu_access_pointer(p) __rcu_access_pointer((p), __rcu) /** * rcu_dereference_check() - rcu_dereference with debug checking * @p: The pointer to read, prior to dereferencing * @c: The conditions under which the dereference will take place * * Do an rcu_dereference(), but check that the conditions under which the * dereference will take place are correct. Typically the conditions * indicate the various locking conditions that should be held at that * point. The check should return true if the conditions are satisfied. * An implicit check for being in an RCU read-side critical section * (rcu_read_lock()) is included. * * For example: * * bar = rcu_dereference_check(foo->bar, lockdep_is_held(&foo->lock)); * * could be used to indicate to lockdep that foo->bar may only be dereferenced * if either rcu_read_lock() is held, or that the lock required to replace * the bar struct at foo->bar is held. * * Note that the list of conditions may also include indications of when a lock * need not be held, for example during initialisation or destruction of the * target struct: * * bar = rcu_dereference_check(foo->bar, lockdep_is_held(&foo->lock) || * atomic_read(&foo->usage) == 0); * * Inserts memory barriers on architectures that require them * (currently only the Alpha), prevents the compiler from refetching * (and from merging fetches), and, more importantly, documents exactly * which pointers are protected by RCU and checks that the pointer is * annotated as __rcu. */ #define rcu_dereference_check(p, c) \ __rcu_dereference_check((p), (c) || rcu_read_lock_held(), __rcu) /** * rcu_dereference_bh_check() - rcu_dereference_bh with debug checking * @p: The pointer to read, prior to dereferencing * @c: The conditions under which the dereference will take place * * This is the RCU-bh counterpart to rcu_dereference_check(). */ #define rcu_dereference_bh_check(p, c) \ __rcu_dereference_check((p), (c) || rcu_read_lock_bh_held(), __rcu) /** * rcu_dereference_sched_check() - rcu_dereference_sched with debug checking * @p: The pointer to read, prior to dereferencing * @c: The conditions under which the dereference will take place * * This is the RCU-sched counterpart to rcu_dereference_check(). */ #define rcu_dereference_sched_check(p, c) \ __rcu_dereference_check((p), (c) || rcu_read_lock_sched_held(), \ __rcu) /* * The tracing infrastructure traces RCU (we want that), but unfortunately * some of the RCU checks causes tracing to lock up the system. * * The no-tracing version of rcu_dereference_raw() must not call * rcu_read_lock_held(). */ #define rcu_dereference_raw_check(p) __rcu_dereference_check((p), 1, __rcu) /** * rcu_dereference_protected() - fetch RCU pointer when updates prevented * @p: The pointer to read, prior to dereferencing * @c: The conditions under which the dereference will take place * * Return the value of the specified RCU-protected pointer, but omit * the READ_ONCE(). This is useful in cases where update-side locks * prevent the value of the pointer from changing. Please note that this * primitive does *not* prevent the compiler from repeating this reference * or combining it with other references, so it should not be used without * protection of appropriate locks. * * This function is only for update-side use. Using this function * when protected only by rcu_read_lock() will result in infrequent * but very ugly failures. */ #define rcu_dereference_protected(p, c) \ __rcu_dereference_protected((p), (c), __rcu) /** * rcu_dereference() - fetch RCU-protected pointer for dereferencing * @p: The pointer to read, prior to dereferencing * * This is a simple wrapper around rcu_dereference_check(). */ #define rcu_dereference(p) rcu_dereference_check(p, 0) /** * rcu_dereference_bh() - fetch an RCU-bh-protected pointer for dereferencing * @p: The pointer to read, prior to dereferencing * * Makes rcu_dereference_check() do the dirty work. */ #define rcu_dereference_bh(p) rcu_dereference_bh_check(p, 0) /** * rcu_dereference_sched() - fetch RCU-sched-protected pointer for dereferencing * @p: The pointer to read, prior to dereferencing * * Makes rcu_dereference_check() do the dirty work. */ #define rcu_dereference_sched(p) rcu_dereference_sched_check(p, 0) /** * rcu_pointer_handoff() - Hand off a pointer from RCU to other mechanism * @p: The pointer to hand off * * This is simply an identity function, but it documents where a pointer * is handed off from RCU to some other synchronization mechanism, for * example, reference counting or locking. In C11, it would map to * kill_dependency(). It could be used as follows:: * * rcu_read_lock(); * p = rcu_dereference(gp); * long_lived = is_long_lived(p); * if (long_lived) { * if (!atomic_inc_not_zero(p->refcnt)) * long_lived = false; * else * p = rcu_pointer_handoff(p); * } * rcu_read_unlock(); */ #define rcu_pointer_handoff(p) (p) /** * rcu_read_lock() - mark the beginning of an RCU read-side critical section * * When synchronize_rcu() is invoked on one CPU while other CPUs * are within RCU read-side critical sections, then the * synchronize_rcu() is guaranteed to block until after all the other * CPUs exit their critical sections. Similarly, if call_rcu() is invoked * on one CPU while other CPUs are within RCU read-side critical * sections, invocation of the corresponding RCU callback is deferred * until after the all the other CPUs exit their critical sections. * * Note, however, that RCU callbacks are permitted to run concurrently * with new RCU read-side critical sections. One way that this can happen * is via the following sequence of events: (1) CPU 0 enters an RCU * read-side critical section, (2) CPU 1 invokes call_rcu() to register * an RCU callback, (3) CPU 0 exits the RCU read-side critical section, * (4) CPU 2 enters a RCU read-side critical section, (5) the RCU * callback is invoked. This is legal, because the RCU read-side critical * section that was running concurrently with the call_rcu() (and which * therefore might be referencing something that the corresponding RCU * callback would free up) has completed before the corresponding * RCU callback is invoked. * * RCU read-side critical sections may be nested. Any deferred actions * will be deferred until the outermost RCU read-side critical section * completes. * * You can avoid reading and understanding the next paragraph by * following this rule: don't put anything in an rcu_read_lock() RCU * read-side critical section that would block in a !PREEMPT kernel. * But if you want the full story, read on! * * In non-preemptible RCU implementations (TREE_RCU and TINY_RCU), * it is illegal to block while in an RCU read-side critical section. * In preemptible RCU implementations (PREEMPT_RCU) in CONFIG_PREEMPTION * kernel builds, RCU read-side critical sections may be preempted, * but explicit blocking is illegal. Finally, in preemptible RCU * implementations in real-time (with -rt patchset) kernel builds, RCU * read-side critical sections may be preempted and they may also block, but * only when acquiring spinlocks that are subject to priority inheritance. */ static __always_inline void rcu_read_lock(void) { __rcu_read_lock(); __acquire(RCU); rcu_lock_acquire(&rcu_lock_map); RCU_LOCKDEP_WARN(!rcu_is_watching(), "rcu_read_lock() used illegally while idle"); } /* * So where is rcu_write_lock()? It does not exist, as there is no * way for writers to lock out RCU readers. This is a feature, not * a bug -- this property is what provides RCU's performance benefits. * Of course, writers must coordinate with each other. The normal * spinlock primitives work well for this, but any other technique may be * used as well. RCU does not care how the writers keep out of each * others' way, as long as they do so. */ /** * rcu_read_unlock() - marks the end of an RCU read-side critical section. * * In most situations, rcu_read_unlock() is immune from deadlock. * However, in kernels built with CONFIG_RCU_BOOST, rcu_read_unlock() * is responsible for deboosting, which it does via rt_mutex_unlock(). * Unfortunately, this function acquires the scheduler's runqueue and * priority-inheritance spinlocks. This means that deadlock could result * if the caller of rcu_read_unlock() already holds one of these locks or * any lock that is ever acquired while holding them. * * That said, RCU readers are never priority boosted unless they were * preempted. Therefore, one way to avoid deadlock is to make sure * that preemption never happens within any RCU read-side critical * section whose outermost rcu_read_unlock() is called with one of * rt_mutex_unlock()'s locks held. Such preemption can be avoided in * a number of ways, for example, by invoking preempt_disable() before * critical section's outermost rcu_read_lock(). * * Given that the set of locks acquired by rt_mutex_unlock() might change * at any time, a somewhat more future-proofed approach is to make sure * that that preemption never happens within any RCU read-side critical * section whose outermost rcu_read_unlock() is called with irqs disabled. * This approach relies on the fact that rt_mutex_unlock() currently only * acquires irq-disabled locks. * * The second of these two approaches is best in most situations, * however, the first approach can also be useful, at least to those * developers willing to keep abreast of the set of locks acquired by * rt_mutex_unlock(). * * See rcu_read_lock() for more information. */ static inline void rcu_read_unlock(void) { RCU_LOCKDEP_WARN(!rcu_is_watching(), "rcu_read_unlock() used illegally while idle"); __release(RCU); __rcu_read_unlock(); rcu_lock_release(&rcu_lock_map); /* Keep acq info for rls diags. */ } /** * rcu_read_lock_bh() - mark the beginning of an RCU-bh critical section * * This is equivalent of rcu_read_lock(), but also disables softirqs. * Note that anything else that disables softirqs can also serve as * an RCU read-side critical section. * * Note that rcu_read_lock_bh() and the matching rcu_read_unlock_bh() * must occur in the same context, for example, it is illegal to invoke * rcu_read_unlock_bh() from one task if the matching rcu_read_lock_bh() * was invoked from some other task. */ static inline void rcu_read_lock_bh(void) { local_bh_disable(); __acquire(RCU_BH); rcu_lock_acquire(&rcu_bh_lock_map); RCU_LOCKDEP_WARN(!rcu_is_watching(), "rcu_read_lock_bh() used illegally while idle"); } /* * rcu_read_unlock_bh - marks the end of a softirq-only RCU critical section * * See rcu_read_lock_bh() for more information. */ static inline void rcu_read_unlock_bh(void) { RCU_LOCKDEP_WARN(!rcu_is_watching(), "rcu_read_unlock_bh() used illegally while idle"); rcu_lock_release(&rcu_bh_lock_map); __release(RCU_BH); local_bh_enable(); } /** * rcu_read_lock_sched() - mark the beginning of a RCU-sched critical section * * This is equivalent of rcu_read_lock(), but disables preemption. * Read-side critical sections can also be introduced by anything else * that disables preemption, including local_irq_disable() and friends. * * Note that rcu_read_lock_sched() and the matching rcu_read_unlock_sched() * must occur in the same context, for example, it is illegal to invoke * rcu_read_unlock_sched() from process context if the matching * rcu_read_lock_sched() was invoked from an NMI handler. */ static inline void rcu_read_lock_sched(void) { preempt_disable(); __acquire(RCU_SCHED); rcu_lock_acquire(&rcu_sched_lock_map); RCU_LOCKDEP_WARN(!rcu_is_watching(), "rcu_read_lock_sched() used illegally while idle"); } /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ static inline notrace void rcu_read_lock_sched_notrace(void) { preempt_disable_notrace(); __acquire(RCU_SCHED); } /* * rcu_read_unlock_sched - marks the end of a RCU-classic critical section * * See rcu_read_lock_sched for more information. */ static inline void rcu_read_unlock_sched(void) { RCU_LOCKDEP_WARN(!rcu_is_watching(), "rcu_read_unlock_sched() used illegally while idle"); rcu_lock_release(&rcu_sched_lock_map); __release(RCU_SCHED); preempt_enable(); } /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ static inline notrace void rcu_read_unlock_sched_notrace(void) { __release(RCU_SCHED); preempt_enable_notrace(); } /** * RCU_INIT_POINTER() - initialize an RCU protected pointer * @p: The pointer to be initialized. * @v: The value to initialized the pointer to. * * Initialize an RCU-protected pointer in special cases where readers * do not need ordering constraints on the CPU or the compiler. These * special cases are: * * 1. This use of RCU_INIT_POINTER() is NULLing out the pointer *or* * 2. The caller has taken whatever steps are required to prevent * RCU readers from concurrently accessing this pointer *or* * 3. The referenced data structure has already been exposed to * readers either at compile time or via rcu_assign_pointer() *and* * * a. You have not made *any* reader-visible changes to * this structure since then *or* * b. It is OK for readers accessing this structure from its * new location to see the old state of the structure. (For * example, the changes were to statistical counters or to * other state where exact synchronization is not required.) * * Failure to follow these rules governing use of RCU_INIT_POINTER() will * result in impossible-to-diagnose memory corruption. As in the structures * will look OK in crash dumps, but any concurrent RCU readers might * see pre-initialized values of the referenced data structure. So * please be very careful how you use RCU_INIT_POINTER()!!! * * If you are creating an RCU-protected linked structure that is accessed * by a single external-to-structure RCU-protected pointer, then you may * use RCU_INIT_POINTER() to initialize the internal RCU-protected * pointers, but you must use rcu_assign_pointer() to initialize the * external-to-structure pointer *after* you have completely initialized * the reader-accessible portions of the linked structure. * * Note that unlike rcu_assign_pointer(), RCU_INIT_POINTER() provides no * ordering guarantees for either the CPU or the compiler. */ #define RCU_INIT_POINTER(p, v) \ do { \ rcu_check_sparse(p, __rcu); \ WRITE_ONCE(p, RCU_INITIALIZER(v)); \ } while (0) /** * RCU_POINTER_INITIALIZER() - statically initialize an RCU protected pointer * @p: The pointer to be initialized. * @v: The value to initialized the pointer to. * * GCC-style initialization for an RCU-protected pointer in a structure field. */ #define RCU_POINTER_INITIALIZER(p, v) \ .p = RCU_INITIALIZER(v) /* * Does the specified offset indicate that the corresponding rcu_head * structure can be handled by kfree_rcu()? */ #define __is_kfree_rcu_offset(offset) ((offset) < 4096) /* * Helper macro for kfree_rcu() to prevent argument-expansion eyestrain. */ #define __kfree_rcu(head, offset) \ do { \ BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); \ kfree_call_rcu(head, (rcu_callback_t)(unsigned long)(offset)); \ } while (0) /** * kfree_rcu() - kfree an object after a grace period. * @ptr: pointer to kfree * @rhf: the name of the struct rcu_head within the type of @ptr. * * Many rcu callbacks functions just call kfree() on the base structure. * These functions are trivial, but their size adds up, and furthermore * when they are used in a kernel module, that module must invoke the * high-latency rcu_barrier() function at module-unload time. * * The kfree_rcu() function handles this issue. Rather than encoding a * function address in the embedded rcu_head structure, kfree_rcu() instead * encodes the offset of the rcu_head structure within the base structure. * Because the functions are not allowed in the low-order 4096 bytes of * kernel virtual memory, offsets up to 4095 bytes can be accommodated. * If the offset is larger than 4095 bytes, a compile-time error will * be generated in __kfree_rcu(). If this error is triggered, you can * either fall back to use of call_rcu() or rearrange the structure to * position the rcu_head structure into the first 4096 bytes. * * Note that the allowable offset might decrease in the future, for example, * to allow something like kmem_cache_free_rcu(). * * The BUILD_BUG_ON check must not involve any function calls, hence the * checks are done in macros here. */ #define kfree_rcu(ptr, rhf) \ do { \ typeof (ptr) ___p = (ptr); \ \ if (___p) \ __kfree_rcu(&((___p)->rhf), offsetof(typeof(*(ptr)), rhf)); \ } while (0) /* * Place this after a lock-acquisition primitive to guarantee that * an UNLOCK+LOCK pair acts as a full barrier. This guarantee applies * if the UNLOCK and LOCK are executed by the same CPU or if the * UNLOCK and LOCK operate on the same lock variable. */ #ifdef CONFIG_ARCH_WEAK_RELEASE_ACQUIRE #define smp_mb__after_unlock_lock() smp_mb() /* Full ordering for lock. */ #else /* #ifdef CONFIG_ARCH_WEAK_RELEASE_ACQUIRE */ #define smp_mb__after_unlock_lock() do { } while (0) #endif /* #else #ifdef CONFIG_ARCH_WEAK_RELEASE_ACQUIRE */ /* Has the specified rcu_head structure been handed to call_rcu()? */ /** * rcu_head_init - Initialize rcu_head for rcu_head_after_call_rcu() * @rhp: The rcu_head structure to initialize. * * If you intend to invoke rcu_head_after_call_rcu() to test whether a * given rcu_head structure has already been passed to call_rcu(), then * you must also invoke this rcu_head_init() function on it just after * allocating that structure. Calls to this function must not race with * calls to call_rcu(), rcu_head_after_call_rcu(), or callback invocation. */ static inline void rcu_head_init(struct rcu_head *rhp) { rhp->func = (rcu_callback_t)~0L; } /** * rcu_head_after_call_rcu - Has this rcu_head been passed to call_rcu()? * @rhp: The rcu_head structure to test. * @f: The function passed to call_rcu() along with @rhp. * * Returns @true if the @rhp has been passed to call_rcu() with @func, * and @false otherwise. Emits a warning in any other case, including * the case where @rhp has already been invoked after a grace period. * Calls to this function must not race with callback invocation. One way * to avoid such races is to enclose the call to rcu_head_after_call_rcu() * in an RCU read-side critical section that includes a read-side fetch * of the pointer to the structure containing @rhp. */ static inline bool rcu_head_after_call_rcu(struct rcu_head *rhp, rcu_callback_t f) { rcu_callback_t func = READ_ONCE(rhp->func); if (func == f) return true; WARN_ON_ONCE(func != (rcu_callback_t)~0L); return false; } #endif /* __LINUX_RCUPDATE_H */ ================================================ FILE: t/tree/include/linux/rcutree.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Read-Copy Update mechanism for mutual exclusion (tree-based version) * * Copyright IBM Corporation, 2008 * * Author: Dipankar Sarma * Paul E. McKenney Hierarchical algorithm * * Based on the original work by Paul McKenney * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. * * For detailed explanation of Read-Copy Update mechanism see - * Documentation/RCU */ #ifndef __LINUX_RCUTREE_H #define __LINUX_RCUTREE_H void rcu_softirq_qs(void); void rcu_note_context_switch(bool preempt); int rcu_needs_cpu(u64 basem, u64 *nextevt); void rcu_cpu_stall_reset(void); /* * Note a virtualization-based context switch. This is simply a * wrapper around rcu_note_context_switch(), which allows TINY_RCU * to save a few bytes. The caller must have disabled interrupts. */ static inline void rcu_virt_note_context_switch(int cpu) { rcu_note_context_switch(false); } void synchronize_rcu_expedited(void); void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func); void rcu_barrier(void); bool rcu_eqs_special_set(int cpu); unsigned long get_state_synchronize_rcu(void); void cond_synchronize_rcu(unsigned long oldstate); void rcu_idle_enter(void); void rcu_idle_exit(void); void rcu_irq_enter(void); void rcu_irq_exit(void); void rcu_irq_enter_irqson(void); void rcu_irq_exit_irqson(void); void exit_rcu(void); void rcu_scheduler_starting(void); extern int rcu_scheduler_active __read_mostly; void rcu_end_inkernel_boot(void); bool rcu_is_watching(void); #ifndef CONFIG_PREEMPTION void rcu_all_qs(void); #endif /* RCUtree hotplug events */ int rcutree_prepare_cpu(unsigned int cpu); int rcutree_online_cpu(unsigned int cpu); int rcutree_offline_cpu(unsigned int cpu); int rcutree_dead_cpu(unsigned int cpu); int rcutree_dying_cpu(unsigned int cpu); void rcu_cpu_starting(unsigned int cpu); #endif /* __LINUX_RCUTREE_H */ ================================================ FILE: t/tree/include/linux/srcu.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Sleepable Read-Copy Update mechanism for mutual exclusion * * Copyright (C) IBM Corporation, 2006 * Copyright (C) Fujitsu, 2012 * * Author: Paul McKenney * Lai Jiangshan * * For detailed explanation of Read-Copy Update mechanism see - * Documentation/RCU/ *.txt * */ #ifndef _LINUX_SRCU_H #define _LINUX_SRCU_H #include #include #include #include struct srcu_struct; #ifdef CONFIG_DEBUG_LOCK_ALLOC int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key); #define init_srcu_struct(ssp) \ ({ \ static struct lock_class_key __srcu_key; \ \ __init_srcu_struct((ssp), #ssp, &__srcu_key); \ }) #define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name }, #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ int init_srcu_struct(struct srcu_struct *ssp); #define __SRCU_DEP_MAP_INIT(srcu_name) #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ #ifdef CONFIG_TINY_SRCU #include #elif defined(CONFIG_TREE_SRCU) #include #elif defined(CONFIG_SRCU) #error "Unknown SRCU implementation specified to kernel configuration" #else /* Dummy definition for things like notifiers. Actual use gets link error. */ struct srcu_struct { }; #endif void call_srcu(struct srcu_struct *ssp, struct rcu_head *head, void (*func)(struct rcu_head *head)); void cleanup_srcu_struct(struct srcu_struct *ssp); int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp); void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp); void synchronize_srcu(struct srcu_struct *ssp); #ifdef CONFIG_DEBUG_LOCK_ALLOC /** * srcu_read_lock_held - might we be in SRCU read-side critical section? * @ssp: The srcu_struct structure to check * * If CONFIG_DEBUG_LOCK_ALLOC is selected, returns nonzero iff in an SRCU * read-side critical section. In absence of CONFIG_DEBUG_LOCK_ALLOC, * this assumes we are in an SRCU read-side critical section unless it can * prove otherwise. * * Checks debug_lockdep_rcu_enabled() to prevent false positives during boot * and while lockdep is disabled. * * Note that SRCU is based on its own statemachine and it doesn't * relies on normal RCU, it can be called from the CPU which * is in the idle loop from an RCU point of view or offline. */ static inline int srcu_read_lock_held(const struct srcu_struct *ssp) { if (!debug_lockdep_rcu_enabled()) return 1; return lock_is_held(&ssp->dep_map); } #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ static inline int srcu_read_lock_held(const struct srcu_struct *ssp) { return 1; } #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ /** * srcu_dereference_check - fetch SRCU-protected pointer for later dereferencing * @p: the pointer to fetch and protect for later dereferencing * @ssp: pointer to the srcu_struct, which is used to check that we * really are in an SRCU read-side critical section. * @c: condition to check for update-side use * * If PROVE_RCU is enabled, invoking this outside of an RCU read-side * critical section will result in an RCU-lockdep splat, unless @c evaluates * to 1. The @c argument will normally be a logical expression containing * lockdep_is_held() calls. */ #define srcu_dereference_check(p, ssp, c) \ __rcu_dereference_check((p), (c) || srcu_read_lock_held(ssp), __rcu) /** * srcu_dereference - fetch SRCU-protected pointer for later dereferencing * @p: the pointer to fetch and protect for later dereferencing * @ssp: pointer to the srcu_struct, which is used to check that we * really are in an SRCU read-side critical section. * * Makes rcu_dereference_check() do the dirty work. If PROVE_RCU * is enabled, invoking this outside of an RCU read-side critical * section will result in an RCU-lockdep splat. */ #define srcu_dereference(p, ssp) srcu_dereference_check((p), (ssp), 0) /** * srcu_dereference_notrace - no tracing and no lockdep calls from here * @p: the pointer to fetch and protect for later dereferencing * @ssp: pointer to the srcu_struct, which is used to check that we * really are in an SRCU read-side critical section. */ #define srcu_dereference_notrace(p, ssp) srcu_dereference_check((p), (ssp), 1) /** * srcu_read_lock - register a new reader for an SRCU-protected structure. * @ssp: srcu_struct in which to register the new reader. * * Enter an SRCU read-side critical section. Note that SRCU read-side * critical sections may be nested. However, it is illegal to * call anything that waits on an SRCU grace period for the same * srcu_struct, whether directly or indirectly. Please note that * one way to indirectly wait on an SRCU grace period is to acquire * a mutex that is held elsewhere while calling synchronize_srcu() or * synchronize_srcu_expedited(). * * Note that srcu_read_lock() and the matching srcu_read_unlock() must * occur in the same context, for example, it is illegal to invoke * srcu_read_unlock() in an irq handler if the matching srcu_read_lock() * was invoked in process context. */ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp) { int retval; retval = __srcu_read_lock(ssp); rcu_lock_acquire(&(ssp)->dep_map); return retval; } /* Used by tracing, cannot be traced and cannot invoke lockdep. */ static inline notrace int srcu_read_lock_notrace(struct srcu_struct *ssp) __acquires(ssp) { int retval; retval = __srcu_read_lock(ssp); return retval; } /** * srcu_read_unlock - unregister a old reader from an SRCU-protected structure. * @ssp: srcu_struct in which to unregister the old reader. * @idx: return value from corresponding srcu_read_lock(). * * Exit an SRCU read-side critical section. */ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp) { WARN_ON_ONCE(idx & ~0x1); rcu_lock_release(&(ssp)->dep_map); __srcu_read_unlock(ssp, idx); } /* Used by tracing, cannot be traced and cannot call lockdep. */ static inline notrace void srcu_read_unlock_notrace(struct srcu_struct *ssp, int idx) __releases(ssp) { __srcu_read_unlock(ssp, idx); } /** * smp_mb__after_srcu_read_unlock - ensure full ordering after srcu_read_unlock * * Converts the preceding srcu_read_unlock into a two-way memory barrier. * * Call this after srcu_read_unlock, to guarantee that all memory operations * that occur after smp_mb__after_srcu_read_unlock will appear to happen after * the preceding srcu_read_unlock. */ static inline void smp_mb__after_srcu_read_unlock(void) { /* __srcu_read_unlock has smp_mb() internally so nothing to do here. */ } #endif ================================================ FILE: t/tree/include/linux/srcutree.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Sleepable Read-Copy Update mechanism for mutual exclusion, * tree variant. * * Copyright (C) IBM Corporation, 2017 * * Author: Paul McKenney */ #ifndef _LINUX_SRCU_TREE_H #define _LINUX_SRCU_TREE_H #include #include struct srcu_node; struct srcu_struct; /* * Per-CPU structure feeding into leaf srcu_node, similar in function * to rcu_node. */ struct srcu_data { /* Read-side state. */ unsigned long srcu_lock_count[2]; /* Locks per CPU. */ unsigned long srcu_unlock_count[2]; /* Unlocks per CPU. */ /* Update-side state. */ spinlock_t __private lock ____cacheline_internodealigned_in_smp; struct rcu_segcblist srcu_cblist; /* List of callbacks.*/ unsigned long srcu_gp_seq_needed; /* Furthest future GP needed. */ unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */ bool srcu_cblist_invoking; /* Invoking these CBs? */ struct timer_list delay_work; /* Delay for CB invoking */ struct work_struct work; /* Context for CB invoking. */ struct rcu_head srcu_barrier_head; /* For srcu_barrier() use. */ struct srcu_node *mynode; /* Leaf srcu_node. */ unsigned long grpmask; /* Mask for leaf srcu_node */ /* ->srcu_data_have_cbs[]. */ int cpu; struct srcu_struct *ssp; }; /* * Node in SRCU combining tree, similar in function to rcu_data. */ struct srcu_node { spinlock_t __private lock; unsigned long srcu_have_cbs[4]; /* GP seq for children */ /* having CBs, but only */ /* is > ->srcu_gq_seq. */ unsigned long srcu_data_have_cbs[4]; /* Which srcu_data structs */ /* have CBs for given GP? */ unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */ struct srcu_node *srcu_parent; /* Next up in tree. */ int grplo; /* Least CPU for node. */ int grphi; /* Biggest CPU for node. */ }; /* * Per-SRCU-domain structure, similar in function to rcu_state. */ struct srcu_struct { struct srcu_node node[NUM_RCU_NODES]; /* Combining tree. */ struct srcu_node *level[RCU_NUM_LVLS + 1]; /* First node at each level. */ struct mutex srcu_cb_mutex; /* Serialize CB preparation. */ spinlock_t __private lock; /* Protect counters */ struct mutex srcu_gp_mutex; /* Serialize GP work. */ unsigned int srcu_idx; /* Current rdr array element. */ unsigned long srcu_gp_seq; /* Grace-period seq #. */ unsigned long srcu_gp_seq_needed; /* Latest gp_seq needed. */ unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */ unsigned long srcu_last_gp_end; /* Last GP end timestamp (ns) */ struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */ unsigned long srcu_barrier_seq; /* srcu_barrier seq #. */ struct mutex srcu_barrier_mutex; /* Serialize barrier ops. */ struct completion srcu_barrier_completion; /* Awaken barrier rq at end. */ atomic_t srcu_barrier_cpu_cnt; /* # CPUs not yet posting a */ /* callback for the barrier */ /* operation. */ struct delayed_work work; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ }; /* Values for state variable (bottom bits of ->srcu_gp_seq). */ #define SRCU_STATE_IDLE 0 #define SRCU_STATE_SCAN1 1 #define SRCU_STATE_SCAN2 2 #define __SRCU_STRUCT_INIT(name, pcpu_name) \ { \ .sda = &pcpu_name, \ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .srcu_gp_seq_needed = -1UL, \ .work = __DELAYED_WORK_INITIALIZER(name.work, NULL, 0), \ __SRCU_DEP_MAP_INIT(name) \ } /* * Define and initialize a srcu struct at build time. * Do -not- call init_srcu_struct() nor cleanup_srcu_struct() on it. * * Note that although DEFINE_STATIC_SRCU() hides the name from other * files, the per-CPU variable rules nevertheless require that the * chosen name be globally unique. These rules also prohibit use of * DEFINE_STATIC_SRCU() within a function. If these rules are too * restrictive, declare the srcu_struct manually. For example, in * each file: * * static struct srcu_struct my_srcu; * * Then, before the first use of each my_srcu, manually initialize it: * * init_srcu_struct(&my_srcu); * * See include/linux/percpu-defs.h for the rules on per-CPU variables. */ #ifdef MODULE # define __DEFINE_SRCU(name, is_static) \ is_static struct srcu_struct name; \ struct srcu_struct * const __srcu_struct_##name \ __section("___srcu_struct_ptrs") = &name #else # define __DEFINE_SRCU(name, is_static) \ static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data); \ is_static struct srcu_struct name = \ __SRCU_STRUCT_INIT(name, name##_srcu_data) #endif #define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */) #define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static) void synchronize_srcu_expedited(struct srcu_struct *ssp); void srcu_barrier(struct srcu_struct *ssp); void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf); #endif ================================================ FILE: t/tree/include/linux/stackdepot.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * A generic stack depot implementation * * Author: Alexander Potapenko * Copyright (C) 2016 Google, Inc. * * Based on code by Dmitry Chernenkov. */ #ifndef _LINUX_STACKDEPOT_H #define _LINUX_STACKDEPOT_H typedef u32 depot_stack_handle_t; depot_stack_handle_t stack_depot_save(unsigned long *entries, unsigned int nr_entries, gfp_t gfp_flags); unsigned int stack_depot_fetch(depot_stack_handle_t handle, unsigned long **entries); #endif ================================================ FILE: t/tree/include/linux/uprobes.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _LINUX_UPROBES_H #define _LINUX_UPROBES_H /* * User-space Probes (UProbes) * * Copyright (C) IBM Corporation, 2008-2012 * Authors: * Srikar Dronamraju * Jim Keniston * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra */ #include #include #include #include struct vm_area_struct; struct mm_struct; struct inode; struct notifier_block; struct page; #define UPROBE_HANDLER_REMOVE 1 #define UPROBE_HANDLER_MASK 1 #define MAX_URETPROBE_DEPTH 64 enum uprobe_filter_ctx { UPROBE_FILTER_REGISTER, UPROBE_FILTER_UNREGISTER, UPROBE_FILTER_MMAP, }; struct uprobe_consumer { int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); int (*ret_handler)(struct uprobe_consumer *self, unsigned long func, struct pt_regs *regs); bool (*filter)(struct uprobe_consumer *self, enum uprobe_filter_ctx ctx, struct mm_struct *mm); struct uprobe_consumer *next; }; #ifdef CONFIG_UPROBES #include enum uprobe_task_state { UTASK_RUNNING, UTASK_SSTEP, UTASK_SSTEP_ACK, UTASK_SSTEP_TRAPPED, }; /* * uprobe_task: Metadata of a task while it singlesteps. */ struct uprobe_task { enum uprobe_task_state state; union { struct { struct arch_uprobe_task autask; unsigned long vaddr; }; struct { struct callback_head dup_xol_work; unsigned long dup_xol_addr; }; }; struct uprobe *active_uprobe; unsigned long xol_vaddr; struct return_instance *return_instances; unsigned int depth; }; struct return_instance { struct uprobe *uprobe; unsigned long func; unsigned long stack; /* stack pointer */ unsigned long orig_ret_vaddr; /* original return address */ bool chained; /* true, if instance is nested */ struct return_instance *next; /* keep as stack */ }; enum rp_check { RP_CHECK_CALL, RP_CHECK_CHAIN_CALL, RP_CHECK_RET, }; struct xol_area; struct uprobes_state { struct xol_area *xol_area; }; extern void __init uprobes_init(void); extern int set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern bool is_swbp_insn(uprobe_opcode_t *insn); extern bool is_trap_insn(uprobe_opcode_t *insn); extern unsigned long uprobe_get_swbp_addr(struct pt_regs *regs); extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs); extern int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_register_refctr(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc); extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void uprobe_start_dup_mmap(void); extern void uprobe_end_dup_mmap(void); extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); extern void uprobe_free_utask(struct task_struct *t); extern void uprobe_copy_process(struct task_struct *t, unsigned long flags); extern int uprobe_post_sstep_notifier(struct pt_regs *regs); extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); extern void uprobe_notify_resume(struct pt_regs *regs); extern bool uprobe_deny_signal(void); extern bool arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); extern void uprobe_clear_state(struct mm_struct *mm); extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); extern bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx, struct pt_regs *regs); extern bool arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs); extern void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, void *src, unsigned long len); #else /* !CONFIG_UPROBES */ struct uprobes_state { }; static inline void uprobes_init(void) { } #define uprobe_get_trap_addr(regs) instruction_pointer(regs) static inline int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { return -ENOSYS; } static inline int uprobe_register_refctr(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc) { return -ENOSYS; } static inline int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool add) { return -ENOSYS; } static inline void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { } static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } static inline void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) { } static inline void uprobe_start_dup_mmap(void) { } static inline void uprobe_end_dup_mmap(void) { } static inline void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) { } static inline void uprobe_notify_resume(struct pt_regs *regs) { } static inline bool uprobe_deny_signal(void) { return false; } static inline void uprobe_free_utask(struct task_struct *t) { } static inline void uprobe_copy_process(struct task_struct *t, unsigned long flags) { } static inline void uprobe_clear_state(struct mm_struct *mm) { } #endif /* !CONFIG_UPROBES */ #endif /* _LINUX_UPROBES_H */ ================================================ FILE: t/tree/include/linux/xarray.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ */ #ifndef _LINUX_XARRAY_H #define _LINUX_XARRAY_H /* * eXtensible Arrays * Copyright (c) 2017 Microsoft Corporation * Author: Matthew Wilcox * * See Documentation/core-api/xarray.rst for how to use the XArray. */ #include #include #include #include #include #include #include #include /* * The bottom two bits of the entry determine how the XArray interprets * the contents: * * 00: Pointer entry * 10: Internal entry * x1: Value entry or tagged pointer * * Attempting to store internal entries in the XArray is a bug. * * Most internal entries are pointers to the next node in the tree. * The following internal entries have a special meaning: * * 0-62: Sibling entries * 256: Zero entry * 257: Retry entry * * Errors are also represented as internal entries, but use the negative * space (-4094 to -2). They're never stored in the slots array; only * returned by the normal API. */ #define BITS_PER_XA_VALUE (BITS_PER_LONG - 1) /** * xa_mk_value() - Create an XArray entry from an integer. * @v: Value to store in XArray. * * Context: Any context. * Return: An entry suitable for storing in the XArray. */ static inline void *xa_mk_value(unsigned long v) { WARN_ON((long)v < 0); return (void *)((v << 1) | 1); } /** * xa_to_value() - Get value stored in an XArray entry. * @entry: XArray entry. * * Context: Any context. * Return: The value stored in the XArray entry. */ static inline unsigned long xa_to_value(const void *entry) { return (unsigned long)entry >> 1; } /** * xa_is_value() - Determine if an entry is a value. * @entry: XArray entry. * * Context: Any context. * Return: True if the entry is a value, false if it is a pointer. */ static inline bool xa_is_value(const void *entry) { return (unsigned long)entry & 1; } /** * xa_tag_pointer() - Create an XArray entry for a tagged pointer. * @p: Plain pointer. * @tag: Tag value (0, 1 or 3). * * If the user of the XArray prefers, they can tag their pointers instead * of storing value entries. Three tags are available (0, 1 and 3). * These are distinct from the xa_mark_t as they are not replicated up * through the array and cannot be searched for. * * Context: Any context. * Return: An XArray entry. */ static inline void *xa_tag_pointer(void *p, unsigned long tag) { return (void *)((unsigned long)p | tag); } /** * xa_untag_pointer() - Turn an XArray entry into a plain pointer. * @entry: XArray entry. * * If you have stored a tagged pointer in the XArray, call this function * to get the untagged version of the pointer. * * Context: Any context. * Return: A pointer. */ static inline void *xa_untag_pointer(void *entry) { return (void *)((unsigned long)entry & ~3UL); } /** * xa_pointer_tag() - Get the tag stored in an XArray entry. * @entry: XArray entry. * * If you have stored a tagged pointer in the XArray, call this function * to get the tag of that pointer. * * Context: Any context. * Return: A tag. */ static inline unsigned int xa_pointer_tag(void *entry) { return (unsigned long)entry & 3UL; } /* * xa_mk_internal() - Create an internal entry. * @v: Value to turn into an internal entry. * * Internal entries are used for a number of purposes. Entries 0-255 are * used for sibling entries (only 0-62 are used by the current code). 256 * is used for the retry entry. 257 is used for the reserved / zero entry. * Negative internal entries are used to represent errnos. Node pointers * are also tagged as internal entries in some situations. * * Context: Any context. * Return: An XArray internal entry corresponding to this value. */ static inline void *xa_mk_internal(unsigned long v) { return (void *)((v << 2) | 2); } /* * xa_to_internal() - Extract the value from an internal entry. * @entry: XArray entry. * * Context: Any context. * Return: The value which was stored in the internal entry. */ static inline unsigned long xa_to_internal(const void *entry) { return (unsigned long)entry >> 2; } /* * xa_is_internal() - Is the entry an internal entry? * @entry: XArray entry. * * Context: Any context. * Return: %true if the entry is an internal entry. */ static inline bool xa_is_internal(const void *entry) { return ((unsigned long)entry & 3) == 2; } #define XA_ZERO_ENTRY xa_mk_internal(257) /** * xa_is_zero() - Is the entry a zero entry? * @entry: Entry retrieved from the XArray * * The normal API will return NULL as the contents of a slot containing * a zero entry. You can only see zero entries by using the advanced API. * * Return: %true if the entry is a zero entry. */ static inline bool xa_is_zero(const void *entry) { return unlikely(entry == XA_ZERO_ENTRY); } /** * xa_is_err() - Report whether an XArray operation returned an error * @entry: Result from calling an XArray function * * If an XArray operation cannot complete an operation, it will return * a special value indicating an error. This function tells you * whether an error occurred; xa_err() tells you which error occurred. * * Context: Any context. * Return: %true if the entry indicates an error. */ static inline bool xa_is_err(const void *entry) { return unlikely(xa_is_internal(entry) && entry >= xa_mk_internal(-MAX_ERRNO)); } /** * xa_err() - Turn an XArray result into an errno. * @entry: Result from calling an XArray function. * * If an XArray operation cannot complete an operation, it will return * a special pointer value which encodes an errno. This function extracts * the errno from the pointer value, or returns 0 if the pointer does not * represent an errno. * * Context: Any context. * Return: A negative errno or 0. */ static inline int xa_err(void *entry) { /* xa_to_internal() would not do sign extension. */ if (xa_is_err(entry)) return (long)entry >> 2; return 0; } /** * struct xa_limit - Represents a range of IDs. * @min: The lowest ID to allocate (inclusive). * @max: The maximum ID to allocate (inclusive). * * This structure is used either directly or via the XA_LIMIT() macro * to communicate the range of IDs that are valid for allocation. * Two common ranges are predefined for you: * * xa_limit_32b - [0 - UINT_MAX] * * xa_limit_31b - [0 - INT_MAX] */ struct xa_limit { u32 max; u32 min; }; #define XA_LIMIT(_min, _max) (struct xa_limit) { .min = _min, .max = _max } #define xa_limit_32b XA_LIMIT(0, UINT_MAX) #define xa_limit_31b XA_LIMIT(0, INT_MAX) typedef unsigned __bitwise xa_mark_t; #define XA_MARK_0 ((__force xa_mark_t)0U) #define XA_MARK_1 ((__force xa_mark_t)1U) #define XA_MARK_2 ((__force xa_mark_t)2U) #define XA_PRESENT ((__force xa_mark_t)8U) #define XA_MARK_MAX XA_MARK_2 #define XA_FREE_MARK XA_MARK_0 enum xa_lock_type { XA_LOCK_IRQ = 1, XA_LOCK_BH = 2, }; /* * Values for xa_flags. The radix tree stores its GFP flags in the xa_flags, * and we remain compatible with that. */ #define XA_FLAGS_LOCK_IRQ ((__force gfp_t)XA_LOCK_IRQ) #define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH) #define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) #define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U) #define XA_FLAGS_ALLOC_WRAPPED ((__force gfp_t)16U) #define XA_FLAGS_ACCOUNT ((__force gfp_t)32U) #define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ (__force unsigned)(mark))) /* ALLOC is for a normal 0-based alloc. ALLOC1 is for an 1-based alloc */ #define XA_FLAGS_ALLOC (XA_FLAGS_TRACK_FREE | XA_FLAGS_MARK(XA_FREE_MARK)) #define XA_FLAGS_ALLOC1 (XA_FLAGS_TRACK_FREE | XA_FLAGS_ZERO_BUSY) /** * struct xarray - The anchor of the XArray. * @xa_lock: Lock that protects the contents of the XArray. * * To use the xarray, define it statically or embed it in your data structure. * It is a very small data structure, so it does not usually make sense to * allocate it separately and keep a pointer to it in your data structure. * * You may use the xa_lock to protect your own data structures as well. */ /* * If all of the entries in the array are NULL, @xa_head is a NULL pointer. * If the only non-NULL entry in the array is at index 0, @xa_head is that * entry. If any other entry in the array is non-NULL, @xa_head points * to an @xa_node. */ struct xarray { spinlock_t xa_lock; /* private: The rest of the data structure is not to be used directly. */ gfp_t xa_flags; void __rcu * xa_head; }; #define XARRAY_INIT(name, flags) { \ .xa_lock = __SPIN_LOCK_UNLOCKED(name.xa_lock), \ .xa_flags = flags, \ .xa_head = NULL, \ } /** * DEFINE_XARRAY_FLAGS() - Define an XArray with custom flags. * @name: A string that names your XArray. * @flags: XA_FLAG values. * * This is intended for file scope definitions of XArrays. It declares * and initialises an empty XArray with the chosen name and flags. It is * equivalent to calling xa_init_flags() on the array, but it does the * initialisation at compiletime instead of runtime. */ #define DEFINE_XARRAY_FLAGS(name, flags) \ struct xarray name = XARRAY_INIT(name, flags) /** * DEFINE_XARRAY() - Define an XArray. * @name: A string that names your XArray. * * This is intended for file scope definitions of XArrays. It declares * and initialises an empty XArray with the chosen name. It is equivalent * to calling xa_init() on the array, but it does the initialisation at * compiletime instead of runtime. */ #define DEFINE_XARRAY(name) DEFINE_XARRAY_FLAGS(name, 0) /** * DEFINE_XARRAY_ALLOC() - Define an XArray which allocates IDs starting at 0. * @name: A string that names your XArray. * * This is intended for file scope definitions of allocating XArrays. * See also DEFINE_XARRAY(). */ #define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC) /** * DEFINE_XARRAY_ALLOC1() - Define an XArray which allocates IDs starting at 1. * @name: A string that names your XArray. * * This is intended for file scope definitions of allocating XArrays. * See also DEFINE_XARRAY(). */ #define DEFINE_XARRAY_ALLOC1(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC1) void *xa_load(struct xarray *, unsigned long index); void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); void *xa_erase(struct xarray *, unsigned long index); void *xa_store_range(struct xarray *, unsigned long first, unsigned long last, void *entry, gfp_t); bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t); void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); void xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); void *xa_find(struct xarray *xa, unsigned long *index, unsigned long max, xa_mark_t) __attribute__((nonnull(2))); void *xa_find_after(struct xarray *xa, unsigned long *index, unsigned long max, xa_mark_t) __attribute__((nonnull(2))); unsigned int xa_extract(struct xarray *, void **dst, unsigned long start, unsigned long max, unsigned int n, xa_mark_t); void xa_destroy(struct xarray *); /** * xa_init_flags() - Initialise an empty XArray with flags. * @xa: XArray. * @flags: XA_FLAG values. * * If you need to initialise an XArray with special flags (eg you need * to take the lock from interrupt context), use this function instead * of xa_init(). * * Context: Any context. */ static inline void xa_init_flags(struct xarray *xa, gfp_t flags) { spin_lock_init(&xa->xa_lock); xa->xa_flags = flags; xa->xa_head = NULL; } /** * xa_init() - Initialise an empty XArray. * @xa: XArray. * * An empty XArray is full of NULL entries. * * Context: Any context. */ static inline void xa_init(struct xarray *xa) { xa_init_flags(xa, 0); } /** * xa_empty() - Determine if an array has any present entries. * @xa: XArray. * * Context: Any context. * Return: %true if the array contains only NULL pointers. */ static inline bool xa_empty(const struct xarray *xa) { return xa->xa_head == NULL; } /** * xa_marked() - Inquire whether any entry in this array has a mark set * @xa: Array * @mark: Mark value * * Context: Any context. * Return: %true if any entry has this mark set. */ static inline bool xa_marked(const struct xarray *xa, xa_mark_t mark) { return xa->xa_flags & XA_FLAGS_MARK(mark); } /** * xa_for_each_start() - Iterate over a portion of an XArray. * @xa: XArray. * @index: Index of @entry. * @entry: Entry retrieved from array. * @start: First index to retrieve from array. * * During the iteration, @entry will have the value of the entry stored * in @xa at @index. You may modify @index during the iteration if you * want to skip or reprocess indices. It is safe to modify the array * during the iteration. At the end of the iteration, @entry will be set * to NULL and @index will have a value less than or equal to max. * * xa_for_each_start() is O(n.log(n)) while xas_for_each() is O(n). You have * to handle your own locking with xas_for_each(), and if you have to unlock * after each iteration, it will also end up being O(n.log(n)). * xa_for_each_start() will spin if it hits a retry entry; if you intend to * see retry entries, you should use the xas_for_each() iterator instead. * The xas_for_each() iterator will expand into more inline code than * xa_for_each_start(). * * Context: Any context. Takes and releases the RCU lock. */ #define xa_for_each_start(xa, index, entry, start) \ for (index = start, \ entry = xa_find(xa, &index, ULONG_MAX, XA_PRESENT); \ entry; \ entry = xa_find_after(xa, &index, ULONG_MAX, XA_PRESENT)) /** * xa_for_each() - Iterate over present entries in an XArray. * @xa: XArray. * @index: Index of @entry. * @entry: Entry retrieved from array. * * During the iteration, @entry will have the value of the entry stored * in @xa at @index. You may modify @index during the iteration if you want * to skip or reprocess indices. It is safe to modify the array during the * iteration. At the end of the iteration, @entry will be set to NULL and * @index will have a value less than or equal to max. * * xa_for_each() is O(n.log(n)) while xas_for_each() is O(n). You have * to handle your own locking with xas_for_each(), and if you have to unlock * after each iteration, it will also end up being O(n.log(n)). xa_for_each() * will spin if it hits a retry entry; if you intend to see retry entries, * you should use the xas_for_each() iterator instead. The xas_for_each() * iterator will expand into more inline code than xa_for_each(). * * Context: Any context. Takes and releases the RCU lock. */ #define xa_for_each(xa, index, entry) \ xa_for_each_start(xa, index, entry, 0) /** * xa_for_each_marked() - Iterate over marked entries in an XArray. * @xa: XArray. * @index: Index of @entry. * @entry: Entry retrieved from array. * @filter: Selection criterion. * * During the iteration, @entry will have the value of the entry stored * in @xa at @index. The iteration will skip all entries in the array * which do not match @filter. You may modify @index during the iteration * if you want to skip or reprocess indices. It is safe to modify the array * during the iteration. At the end of the iteration, @entry will be set to * NULL and @index will have a value less than or equal to max. * * xa_for_each_marked() is O(n.log(n)) while xas_for_each_marked() is O(n). * You have to handle your own locking with xas_for_each(), and if you have * to unlock after each iteration, it will also end up being O(n.log(n)). * xa_for_each_marked() will spin if it hits a retry entry; if you intend to * see retry entries, you should use the xas_for_each_marked() iterator * instead. The xas_for_each_marked() iterator will expand into more inline * code than xa_for_each_marked(). * * Context: Any context. Takes and releases the RCU lock. */ #define xa_for_each_marked(xa, index, entry, filter) \ for (index = 0, entry = xa_find(xa, &index, ULONG_MAX, filter); \ entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter)) #define xa_trylock(xa) spin_trylock(&(xa)->xa_lock) #define xa_lock(xa) spin_lock(&(xa)->xa_lock) #define xa_unlock(xa) spin_unlock(&(xa)->xa_lock) #define xa_lock_bh(xa) spin_lock_bh(&(xa)->xa_lock) #define xa_unlock_bh(xa) spin_unlock_bh(&(xa)->xa_lock) #define xa_lock_irq(xa) spin_lock_irq(&(xa)->xa_lock) #define xa_unlock_irq(xa) spin_unlock_irq(&(xa)->xa_lock) #define xa_lock_irqsave(xa, flags) \ spin_lock_irqsave(&(xa)->xa_lock, flags) #define xa_unlock_irqrestore(xa, flags) \ spin_unlock_irqrestore(&(xa)->xa_lock, flags) /* * Versions of the normal API which require the caller to hold the * xa_lock. If the GFP flags allow it, they will drop the lock to * allocate memory, then reacquire it afterwards. These functions * may also re-enable interrupts if the XArray flags indicate the * locking should be interrupt safe. */ void *__xa_erase(struct xarray *, unsigned long index); void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old, void *entry, gfp_t); int __must_check __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t); int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry, struct xa_limit, gfp_t); int __must_check __xa_alloc_cyclic(struct xarray *, u32 *id, void *entry, struct xa_limit, u32 *next, gfp_t); void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); /** * xa_store_bh() - Store this entry in the XArray. * @xa: XArray. * @index: Index into array. * @entry: New entry. * @gfp: Memory allocation flags. * * This function is like calling xa_store() except it disables softirqs * while holding the array lock. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. * Return: The entry which used to be at this index. */ static inline void *xa_store_bh(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) { void *curr; xa_lock_bh(xa); curr = __xa_store(xa, index, entry, gfp); xa_unlock_bh(xa); return curr; } /** * xa_store_irq() - Store this entry in the XArray. * @xa: XArray. * @index: Index into array. * @entry: New entry. * @gfp: Memory allocation flags. * * This function is like calling xa_store() except it disables interrupts * while holding the array lock. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. * Return: The entry which used to be at this index. */ static inline void *xa_store_irq(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) { void *curr; xa_lock_irq(xa); curr = __xa_store(xa, index, entry, gfp); xa_unlock_irq(xa); return curr; } /** * xa_erase_bh() - Erase this entry from the XArray. * @xa: XArray. * @index: Index of entry. * * After this function returns, loading from @index will return %NULL. * If the index is part of a multi-index entry, all indices will be erased * and none of the entries will be part of a multi-index entry. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. * Return: The entry which used to be at this index. */ static inline void *xa_erase_bh(struct xarray *xa, unsigned long index) { void *entry; xa_lock_bh(xa); entry = __xa_erase(xa, index); xa_unlock_bh(xa); return entry; } /** * xa_erase_irq() - Erase this entry from the XArray. * @xa: XArray. * @index: Index of entry. * * After this function returns, loading from @index will return %NULL. * If the index is part of a multi-index entry, all indices will be erased * and none of the entries will be part of a multi-index entry. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. * Return: The entry which used to be at this index. */ static inline void *xa_erase_irq(struct xarray *xa, unsigned long index) { void *entry; xa_lock_irq(xa); entry = __xa_erase(xa, index); xa_unlock_irq(xa); return entry; } /** * xa_cmpxchg() - Conditionally replace an entry in the XArray. * @xa: XArray. * @index: Index into array. * @old: Old value to test against. * @entry: New value to place in array. * @gfp: Memory allocation flags. * * If the entry at @index is the same as @old, replace it with @entry. * If the return value is equal to @old, then the exchange was successful. * * Context: Any context. Takes and releases the xa_lock. May sleep * if the @gfp flags permit. * Return: The old value at this index or xa_err() if an error happened. */ static inline void *xa_cmpxchg(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp) { void *curr; xa_lock(xa); curr = __xa_cmpxchg(xa, index, old, entry, gfp); xa_unlock(xa); return curr; } /** * xa_cmpxchg_bh() - Conditionally replace an entry in the XArray. * @xa: XArray. * @index: Index into array. * @old: Old value to test against. * @entry: New value to place in array. * @gfp: Memory allocation flags. * * This function is like calling xa_cmpxchg() except it disables softirqs * while holding the array lock. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. * Return: The old value at this index or xa_err() if an error happened. */ static inline void *xa_cmpxchg_bh(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp) { void *curr; xa_lock_bh(xa); curr = __xa_cmpxchg(xa, index, old, entry, gfp); xa_unlock_bh(xa); return curr; } /** * xa_cmpxchg_irq() - Conditionally replace an entry in the XArray. * @xa: XArray. * @index: Index into array. * @old: Old value to test against. * @entry: New value to place in array. * @gfp: Memory allocation flags. * * This function is like calling xa_cmpxchg() except it disables interrupts * while holding the array lock. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. * Return: The old value at this index or xa_err() if an error happened. */ static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp) { void *curr; xa_lock_irq(xa); curr = __xa_cmpxchg(xa, index, old, entry, gfp); xa_unlock_irq(xa); return curr; } /** * xa_insert() - Store this entry in the XArray unless another entry is * already present. * @xa: XArray. * @index: Index into array. * @entry: New entry. * @gfp: Memory allocation flags. * * Inserting a NULL entry will store a reserved entry (like xa_reserve()) * if no entry is present. Inserting will fail if a reserved entry is * present, even though loading from this index will return NULL. * * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ static inline int __must_check xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) { int err; xa_lock(xa); err = __xa_insert(xa, index, entry, gfp); xa_unlock(xa); return err; } /** * xa_insert_bh() - Store this entry in the XArray unless another entry is * already present. * @xa: XArray. * @index: Index into array. * @entry: New entry. * @gfp: Memory allocation flags. * * Inserting a NULL entry will store a reserved entry (like xa_reserve()) * if no entry is present. Inserting will fail if a reserved entry is * present, even though loading from this index will return NULL. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ static inline int __must_check xa_insert_bh(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) { int err; xa_lock_bh(xa); err = __xa_insert(xa, index, entry, gfp); xa_unlock_bh(xa); return err; } /** * xa_insert_irq() - Store this entry in the XArray unless another entry is * already present. * @xa: XArray. * @index: Index into array. * @entry: New entry. * @gfp: Memory allocation flags. * * Inserting a NULL entry will store a reserved entry (like xa_reserve()) * if no entry is present. Inserting will fail if a reserved entry is * present, even though loading from this index will return NULL. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ static inline int __must_check xa_insert_irq(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) { int err; xa_lock_irq(xa); err = __xa_insert(xa, index, entry, gfp); xa_unlock_irq(xa); return err; } /** * xa_alloc() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. * @entry: New entry. * @limit: Range of ID to allocate. * @gfp: Memory allocation flags. * * Finds an empty entry in @xa between @limit.min and @limit.max, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or * -EBUSY if there are no free entries in @limit. */ static inline __must_check int xa_alloc(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp) { int err; xa_lock(xa); err = __xa_alloc(xa, id, entry, limit, gfp); xa_unlock(xa); return err; } /** * xa_alloc_bh() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. * @entry: New entry. * @limit: Range of ID to allocate. * @gfp: Memory allocation flags. * * Finds an empty entry in @xa between @limit.min and @limit.max, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or * -EBUSY if there are no free entries in @limit. */ static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp) { int err; xa_lock_bh(xa); err = __xa_alloc(xa, id, entry, limit, gfp); xa_unlock_bh(xa); return err; } /** * xa_alloc_irq() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. * @entry: New entry. * @limit: Range of ID to allocate. * @gfp: Memory allocation flags. * * Finds an empty entry in @xa between @limit.min and @limit.max, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or * -EBUSY if there are no free entries in @limit. */ static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp) { int err; xa_lock_irq(xa); err = __xa_alloc(xa, id, entry, limit, gfp); xa_unlock_irq(xa); return err; } /** * xa_alloc_cyclic() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. * @entry: New entry. * @limit: Range of allocated ID. * @next: Pointer to next ID to allocate. * @gfp: Memory allocation flags. * * Finds an empty entry in @xa between @limit.min and @limit.max, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * The search for an empty entry will start at @next and will wrap * around if necessary. * * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the * allocation succeeded after wrapping, -ENOMEM if memory could not be * allocated or -EBUSY if there are no free entries in @limit. */ static inline int xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp) { int err; xa_lock(xa); err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); xa_unlock(xa); return err; } /** * xa_alloc_cyclic_bh() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. * @entry: New entry. * @limit: Range of allocated ID. * @next: Pointer to next ID to allocate. * @gfp: Memory allocation flags. * * Finds an empty entry in @xa between @limit.min and @limit.max, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * The search for an empty entry will start at @next and will wrap * around if necessary. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the * allocation succeeded after wrapping, -ENOMEM if memory could not be * allocated or -EBUSY if there are no free entries in @limit. */ static inline int xa_alloc_cyclic_bh(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp) { int err; xa_lock_bh(xa); err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); xa_unlock_bh(xa); return err; } /** * xa_alloc_cyclic_irq() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. * @entry: New entry. * @limit: Range of allocated ID. * @next: Pointer to next ID to allocate. * @gfp: Memory allocation flags. * * Finds an empty entry in @xa between @limit.min and @limit.max, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * The search for an empty entry will start at @next and will wrap * around if necessary. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the * allocation succeeded after wrapping, -ENOMEM if memory could not be * allocated or -EBUSY if there are no free entries in @limit. */ static inline int xa_alloc_cyclic_irq(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp) { int err; xa_lock_irq(xa); err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); xa_unlock_irq(xa); return err; } /** * xa_reserve() - Reserve this index in the XArray. * @xa: XArray. * @index: Index into array. * @gfp: Memory allocation flags. * * Ensures there is somewhere to store an entry at @index in the array. * If there is already something stored at @index, this function does * nothing. If there was nothing there, the entry is marked as reserved. * Loading from a reserved entry returns a %NULL pointer. * * If you do not use the entry that you have reserved, call xa_release() * or xa_erase() to free any unnecessary memory. * * Context: Any context. Takes and releases the xa_lock. * May sleep if the @gfp flags permit. * Return: 0 if the reservation succeeded or -ENOMEM if it failed. */ static inline __must_check int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp) { return xa_err(xa_cmpxchg(xa, index, NULL, XA_ZERO_ENTRY, gfp)); } /** * xa_reserve_bh() - Reserve this index in the XArray. * @xa: XArray. * @index: Index into array. * @gfp: Memory allocation flags. * * A softirq-disabling version of xa_reserve(). * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. * Return: 0 if the reservation succeeded or -ENOMEM if it failed. */ static inline __must_check int xa_reserve_bh(struct xarray *xa, unsigned long index, gfp_t gfp) { return xa_err(xa_cmpxchg_bh(xa, index, NULL, XA_ZERO_ENTRY, gfp)); } /** * xa_reserve_irq() - Reserve this index in the XArray. * @xa: XArray. * @index: Index into array. * @gfp: Memory allocation flags. * * An interrupt-disabling version of xa_reserve(). * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. * Return: 0 if the reservation succeeded or -ENOMEM if it failed. */ static inline __must_check int xa_reserve_irq(struct xarray *xa, unsigned long index, gfp_t gfp) { return xa_err(xa_cmpxchg_irq(xa, index, NULL, XA_ZERO_ENTRY, gfp)); } /** * xa_release() - Release a reserved entry. * @xa: XArray. * @index: Index of entry. * * After calling xa_reserve(), you can call this function to release the * reservation. If the entry at @index has been stored to, this function * will do nothing. */ static inline void xa_release(struct xarray *xa, unsigned long index) { xa_cmpxchg(xa, index, XA_ZERO_ENTRY, NULL, 0); } /* Everything below here is the Advanced API. Proceed with caution. */ /* * The xarray is constructed out of a set of 'chunks' of pointers. Choosing * the best chunk size requires some tradeoffs. A power of two recommends * itself so that we can walk the tree based purely on shifts and masks. * Generally, the larger the better; as the number of slots per level of the * tree increases, the less tall the tree needs to be. But that needs to be * balanced against the memory consumption of each node. On a 64-bit system, * xa_node is currently 576 bytes, and we get 7 of them per 4kB page. If we * doubled the number of slots per node, we'd get only 3 nodes per 4kB page. */ #ifndef XA_CHUNK_SHIFT #define XA_CHUNK_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) #endif #define XA_CHUNK_SIZE (1UL << XA_CHUNK_SHIFT) #define XA_CHUNK_MASK (XA_CHUNK_SIZE - 1) #define XA_MAX_MARKS 3 #define XA_MARK_LONGS DIV_ROUND_UP(XA_CHUNK_SIZE, BITS_PER_LONG) /* * @count is the count of every non-NULL element in the ->slots array * whether that is a value entry, a retry entry, a user pointer, * a sibling entry or a pointer to the next level of the tree. * @nr_values is the count of every element in ->slots which is * either a value entry or a sibling of a value entry. */ struct xa_node { unsigned char shift; /* Bits remaining in each slot */ unsigned char offset; /* Slot offset in parent */ unsigned char count; /* Total entry count */ unsigned char nr_values; /* Value entry count */ struct xa_node __rcu *parent; /* NULL at top of tree */ struct xarray *array; /* The array we belong to */ union { struct list_head private_list; /* For tree user */ struct rcu_head rcu_head; /* Used when freeing node */ }; void __rcu *slots[XA_CHUNK_SIZE]; union { unsigned long tags[XA_MAX_MARKS][XA_MARK_LONGS]; unsigned long marks[XA_MAX_MARKS][XA_MARK_LONGS]; }; }; void xa_dump(const struct xarray *); void xa_dump_node(const struct xa_node *); #ifdef XA_DEBUG #define XA_BUG_ON(xa, x) do { \ if (x) { \ xa_dump(xa); \ BUG(); \ } \ } while (0) #define XA_NODE_BUG_ON(node, x) do { \ if (x) { \ if (node) xa_dump_node(node); \ BUG(); \ } \ } while (0) #else #define XA_BUG_ON(xa, x) do { } while (0) #define XA_NODE_BUG_ON(node, x) do { } while (0) #endif /* Private */ static inline void *xa_head(const struct xarray *xa) { return rcu_dereference_check(xa->xa_head, lockdep_is_held(&xa->xa_lock)); } /* Private */ static inline void *xa_head_locked(const struct xarray *xa) { return rcu_dereference_protected(xa->xa_head, lockdep_is_held(&xa->xa_lock)); } /* Private */ static inline void *xa_entry(const struct xarray *xa, const struct xa_node *node, unsigned int offset) { XA_NODE_BUG_ON(node, offset >= XA_CHUNK_SIZE); return rcu_dereference_check(node->slots[offset], lockdep_is_held(&xa->xa_lock)); } /* Private */ static inline void *xa_entry_locked(const struct xarray *xa, const struct xa_node *node, unsigned int offset) { XA_NODE_BUG_ON(node, offset >= XA_CHUNK_SIZE); return rcu_dereference_protected(node->slots[offset], lockdep_is_held(&xa->xa_lock)); } /* Private */ static inline struct xa_node *xa_parent(const struct xarray *xa, const struct xa_node *node) { return rcu_dereference_check(node->parent, lockdep_is_held(&xa->xa_lock)); } /* Private */ static inline struct xa_node *xa_parent_locked(const struct xarray *xa, const struct xa_node *node) { return rcu_dereference_protected(node->parent, lockdep_is_held(&xa->xa_lock)); } /* Private */ static inline void *xa_mk_node(const struct xa_node *node) { return (void *)((unsigned long)node | 2); } /* Private */ static inline struct xa_node *xa_to_node(const void *entry) { return (struct xa_node *)((unsigned long)entry - 2); } /* Private */ static inline bool xa_is_node(const void *entry) { return xa_is_internal(entry) && (unsigned long)entry > 4096; } /* Private */ static inline void *xa_mk_sibling(unsigned int offset) { return xa_mk_internal(offset); } /* Private */ static inline unsigned long xa_to_sibling(const void *entry) { return xa_to_internal(entry); } /** * xa_is_sibling() - Is the entry a sibling entry? * @entry: Entry retrieved from the XArray * * Return: %true if the entry is a sibling entry. */ static inline bool xa_is_sibling(const void *entry) { return IS_ENABLED(CONFIG_XARRAY_MULTI) && xa_is_internal(entry) && (entry < xa_mk_sibling(XA_CHUNK_SIZE - 1)); } #define XA_RETRY_ENTRY xa_mk_internal(256) /** * xa_is_retry() - Is the entry a retry entry? * @entry: Entry retrieved from the XArray * * Return: %true if the entry is a retry entry. */ static inline bool xa_is_retry(const void *entry) { return unlikely(entry == XA_RETRY_ENTRY); } /** * xa_is_advanced() - Is the entry only permitted for the advanced API? * @entry: Entry to be stored in the XArray. * * Return: %true if the entry cannot be stored by the normal API. */ static inline bool xa_is_advanced(const void *entry) { return xa_is_internal(entry) && (entry <= XA_RETRY_ENTRY); } /** * typedef xa_update_node_t - A callback function from the XArray. * @node: The node which is being processed * * This function is called every time the XArray updates the count of * present and value entries in a node. It allows advanced users to * maintain the private_list in the node. * * Context: The xa_lock is held and interrupts may be disabled. * Implementations should not drop the xa_lock, nor re-enable * interrupts. */ typedef void (*xa_update_node_t)(struct xa_node *node); /* * The xa_state is opaque to its users. It contains various different pieces * of state involved in the current operation on the XArray. It should be * declared on the stack and passed between the various internal routines. * The various elements in it should not be accessed directly, but only * through the provided accessor functions. The below documentation is for * the benefit of those working on the code, not for users of the XArray. * * @xa_node usually points to the xa_node containing the slot we're operating * on (and @xa_offset is the offset in the slots array). If there is a * single entry in the array at index 0, there are no allocated xa_nodes to * point to, and so we store %NULL in @xa_node. @xa_node is set to * the value %XAS_RESTART if the xa_state is not walked to the correct * position in the tree of nodes for this operation. If an error occurs * during an operation, it is set to an %XAS_ERROR value. If we run off the * end of the allocated nodes, it is set to %XAS_BOUNDS. */ struct xa_state { struct xarray *xa; unsigned long xa_index; unsigned char xa_shift; unsigned char xa_sibs; unsigned char xa_offset; unsigned char xa_pad; /* Helps gcc generate better code */ struct xa_node *xa_node; struct xa_node *xa_alloc; xa_update_node_t xa_update; }; /* * We encode errnos in the xas->xa_node. If an error has happened, we need to * drop the lock to fix it, and once we've done so the xa_state is invalid. */ #define XA_ERROR(errno) ((struct xa_node *)(((unsigned long)errno << 2) | 2UL)) #define XAS_BOUNDS ((struct xa_node *)1UL) #define XAS_RESTART ((struct xa_node *)3UL) #define __XA_STATE(array, index, shift, sibs) { \ .xa = array, \ .xa_index = index, \ .xa_shift = shift, \ .xa_sibs = sibs, \ .xa_offset = 0, \ .xa_pad = 0, \ .xa_node = XAS_RESTART, \ .xa_alloc = NULL, \ .xa_update = NULL \ } /** * XA_STATE() - Declare an XArray operation state. * @name: Name of this operation state (usually xas). * @array: Array to operate on. * @index: Initial index of interest. * * Declare and initialise an xa_state on the stack. */ #define XA_STATE(name, array, index) \ struct xa_state name = __XA_STATE(array, index, 0, 0) /** * XA_STATE_ORDER() - Declare an XArray operation state. * @name: Name of this operation state (usually xas). * @array: Array to operate on. * @index: Initial index of interest. * @order: Order of entry. * * Declare and initialise an xa_state on the stack. This variant of * XA_STATE() allows you to specify the 'order' of the element you * want to operate on.` */ #define XA_STATE_ORDER(name, array, index, order) \ struct xa_state name = __XA_STATE(array, \ (index >> order) << order, \ order - (order % XA_CHUNK_SHIFT), \ (1U << (order % XA_CHUNK_SHIFT)) - 1) #define xas_marked(xas, mark) xa_marked((xas)->xa, (mark)) #define xas_trylock(xas) xa_trylock((xas)->xa) #define xas_lock(xas) xa_lock((xas)->xa) #define xas_unlock(xas) xa_unlock((xas)->xa) #define xas_lock_bh(xas) xa_lock_bh((xas)->xa) #define xas_unlock_bh(xas) xa_unlock_bh((xas)->xa) #define xas_lock_irq(xas) xa_lock_irq((xas)->xa) #define xas_unlock_irq(xas) xa_unlock_irq((xas)->xa) #define xas_lock_irqsave(xas, flags) \ xa_lock_irqsave((xas)->xa, flags) #define xas_unlock_irqrestore(xas, flags) \ xa_unlock_irqrestore((xas)->xa, flags) /** * xas_error() - Return an errno stored in the xa_state. * @xas: XArray operation state. * * Return: 0 if no error has been noted. A negative errno if one has. */ static inline int xas_error(const struct xa_state *xas) { return xa_err(xas->xa_node); } /** * xas_set_err() - Note an error in the xa_state. * @xas: XArray operation state. * @err: Negative error number. * * Only call this function with a negative @err; zero or positive errors * will probably not behave the way you think they should. If you want * to clear the error from an xa_state, use xas_reset(). */ static inline void xas_set_err(struct xa_state *xas, long err) { xas->xa_node = XA_ERROR(err); } /** * xas_invalid() - Is the xas in a retry or error state? * @xas: XArray operation state. * * Return: %true if the xas cannot be used for operations. */ static inline bool xas_invalid(const struct xa_state *xas) { return (unsigned long)xas->xa_node & 3; } /** * xas_valid() - Is the xas a valid cursor into the array? * @xas: XArray operation state. * * Return: %true if the xas can be used for operations. */ static inline bool xas_valid(const struct xa_state *xas) { return !xas_invalid(xas); } /** * xas_is_node() - Does the xas point to a node? * @xas: XArray operation state. * * Return: %true if the xas currently references a node. */ static inline bool xas_is_node(const struct xa_state *xas) { return xas_valid(xas) && xas->xa_node; } /* True if the pointer is something other than a node */ static inline bool xas_not_node(struct xa_node *node) { return ((unsigned long)node & 3) || !node; } /* True if the node represents RESTART or an error */ static inline bool xas_frozen(struct xa_node *node) { return (unsigned long)node & 2; } /* True if the node represents head-of-tree, RESTART or BOUNDS */ static inline bool xas_top(struct xa_node *node) { return node <= XAS_RESTART; } /** * xas_reset() - Reset an XArray operation state. * @xas: XArray operation state. * * Resets the error or walk state of the @xas so future walks of the * array will start from the root. Use this if you have dropped the * xarray lock and want to reuse the xa_state. * * Context: Any context. */ static inline void xas_reset(struct xa_state *xas) { xas->xa_node = XAS_RESTART; } /** * xas_retry() - Retry the operation if appropriate. * @xas: XArray operation state. * @entry: Entry from xarray. * * The advanced functions may sometimes return an internal entry, such as * a retry entry or a zero entry. This function sets up the @xas to restart * the walk from the head of the array if needed. * * Context: Any context. * Return: true if the operation needs to be retried. */ static inline bool xas_retry(struct xa_state *xas, const void *entry) { if (xa_is_zero(entry)) return true; if (!xa_is_retry(entry)) return false; xas_reset(xas); return true; } void *xas_load(struct xa_state *); void *xas_store(struct xa_state *, void *entry); void *xas_find(struct xa_state *, unsigned long max); void *xas_find_conflict(struct xa_state *); bool xas_get_mark(const struct xa_state *, xa_mark_t); void xas_set_mark(const struct xa_state *, xa_mark_t); void xas_clear_mark(const struct xa_state *, xa_mark_t); void *xas_find_marked(struct xa_state *, unsigned long max, xa_mark_t); void xas_init_marks(const struct xa_state *); bool xas_nomem(struct xa_state *, gfp_t); void xas_pause(struct xa_state *); void xas_create_range(struct xa_state *); /** * xas_reload() - Refetch an entry from the xarray. * @xas: XArray operation state. * * Use this function to check that a previously loaded entry still has * the same value. This is useful for the lockless pagecache lookup where * we walk the array with only the RCU lock to protect us, lock the page, * then check that the page hasn't moved since we looked it up. * * The caller guarantees that @xas is still valid. If it may be in an * error or restart state, call xas_load() instead. * * Return: The entry at this location in the xarray. */ static inline void *xas_reload(struct xa_state *xas) { struct xa_node *node = xas->xa_node; if (node) return xa_entry(xas->xa, node, xas->xa_offset); return xa_head(xas->xa); } /** * xas_set() - Set up XArray operation state for a different index. * @xas: XArray operation state. * @index: New index into the XArray. * * Move the operation state to refer to a different index. This will * have the effect of starting a walk from the top; see xas_next() * to move to an adjacent index. */ static inline void xas_set(struct xa_state *xas, unsigned long index) { xas->xa_index = index; xas->xa_node = XAS_RESTART; } /** * xas_set_order() - Set up XArray operation state for a multislot entry. * @xas: XArray operation state. * @index: Target of the operation. * @order: Entry occupies 2^@order indices. */ static inline void xas_set_order(struct xa_state *xas, unsigned long index, unsigned int order) { #ifdef CONFIG_XARRAY_MULTI xas->xa_index = order < BITS_PER_LONG ? (index >> order) << order : 0; xas->xa_shift = order - (order % XA_CHUNK_SHIFT); xas->xa_sibs = (1 << (order % XA_CHUNK_SHIFT)) - 1; xas->xa_node = XAS_RESTART; #else BUG_ON(order > 0); xas_set(xas, index); #endif } /** * xas_set_update() - Set up XArray operation state for a callback. * @xas: XArray operation state. * @update: Function to call when updating a node. * * The XArray can notify a caller after it has updated an xa_node. * This is advanced functionality and is only needed by the page cache. */ static inline void xas_set_update(struct xa_state *xas, xa_update_node_t update) { xas->xa_update = update; } /** * xas_next_entry() - Advance iterator to next present entry. * @xas: XArray operation state. * @max: Highest index to return. * * xas_next_entry() is an inline function to optimise xarray traversal for * speed. It is equivalent to calling xas_find(), and will call xas_find() * for all the hard cases. * * Return: The next present entry after the one currently referred to by @xas. */ static inline void *xas_next_entry(struct xa_state *xas, unsigned long max) { struct xa_node *node = xas->xa_node; void *entry; if (unlikely(xas_not_node(node) || node->shift || xas->xa_offset != (xas->xa_index & XA_CHUNK_MASK))) return xas_find(xas, max); do { if (unlikely(xas->xa_index >= max)) return xas_find(xas, max); if (unlikely(xas->xa_offset == XA_CHUNK_MASK)) return xas_find(xas, max); entry = xa_entry(xas->xa, node, xas->xa_offset + 1); if (unlikely(xa_is_internal(entry))) return xas_find(xas, max); xas->xa_offset++; xas->xa_index++; } while (!entry); return entry; } /* Private */ static inline unsigned int xas_find_chunk(struct xa_state *xas, bool advance, xa_mark_t mark) { unsigned long *addr = xas->xa_node->marks[(__force unsigned)mark]; unsigned int offset = xas->xa_offset; if (advance) offset++; if (XA_CHUNK_SIZE == BITS_PER_LONG) { if (offset < XA_CHUNK_SIZE) { unsigned long data = *addr & (~0UL << offset); if (data) return __ffs(data); } return XA_CHUNK_SIZE; } return find_next_bit(addr, XA_CHUNK_SIZE, offset); } /** * xas_next_marked() - Advance iterator to next marked entry. * @xas: XArray operation state. * @max: Highest index to return. * @mark: Mark to search for. * * xas_next_marked() is an inline function to optimise xarray traversal for * speed. It is equivalent to calling xas_find_marked(), and will call * xas_find_marked() for all the hard cases. * * Return: The next marked entry after the one currently referred to by @xas. */ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark) { struct xa_node *node = xas->xa_node; unsigned int offset; if (unlikely(xas_not_node(node) || node->shift)) return xas_find_marked(xas, max, mark); offset = xas_find_chunk(xas, true, mark); xas->xa_offset = offset; xas->xa_index = (xas->xa_index & ~XA_CHUNK_MASK) + offset; if (xas->xa_index > max) return NULL; if (offset == XA_CHUNK_SIZE) return xas_find_marked(xas, max, mark); return xa_entry(xas->xa, node, offset); } /* * If iterating while holding a lock, drop the lock and reschedule * every %XA_CHECK_SCHED loops. */ enum { XA_CHECK_SCHED = 4096, }; /** * xas_for_each() - Iterate over a range of an XArray. * @xas: XArray operation state. * @entry: Entry retrieved from the array. * @max: Maximum index to retrieve from array. * * The loop body will be executed for each entry present in the xarray * between the current xas position and @max. @entry will be set to * the entry retrieved from the xarray. It is safe to delete entries * from the array in the loop body. You should hold either the RCU lock * or the xa_lock while iterating. If you need to drop the lock, call * xas_pause() first. */ #define xas_for_each(xas, entry, max) \ for (entry = xas_find(xas, max); entry; \ entry = xas_next_entry(xas, max)) /** * xas_for_each_marked() - Iterate over a range of an XArray. * @xas: XArray operation state. * @entry: Entry retrieved from the array. * @max: Maximum index to retrieve from array. * @mark: Mark to search for. * * The loop body will be executed for each marked entry in the xarray * between the current xas position and @max. @entry will be set to * the entry retrieved from the xarray. It is safe to delete entries * from the array in the loop body. You should hold either the RCU lock * or the xa_lock while iterating. If you need to drop the lock, call * xas_pause() first. */ #define xas_for_each_marked(xas, entry, max, mark) \ for (entry = xas_find_marked(xas, max, mark); entry; \ entry = xas_next_marked(xas, max, mark)) /** * xas_for_each_conflict() - Iterate over a range of an XArray. * @xas: XArray operation state. * @entry: Entry retrieved from the array. * * The loop body will be executed for each entry in the XArray that lies * within the range specified by @xas. If the loop completes successfully, * any entries that lie in this range will be replaced by @entry. The caller * may break out of the loop; if they do so, the contents of the XArray will * be unchanged. The operation may fail due to an out of memory condition. * The caller may also call xa_set_err() to exit the loop while setting an * error to record the reason. */ #define xas_for_each_conflict(xas, entry) \ while ((entry = xas_find_conflict(xas))) void *__xas_next(struct xa_state *); void *__xas_prev(struct xa_state *); /** * xas_prev() - Move iterator to previous index. * @xas: XArray operation state. * * If the @xas was in an error state, it will remain in an error state * and this function will return %NULL. If the @xas has never been walked, * it will have the effect of calling xas_load(). Otherwise one will be * subtracted from the index and the state will be walked to the correct * location in the array for the next operation. * * If the iterator was referencing index 0, this function wraps * around to %ULONG_MAX. * * Return: The entry at the new index. This may be %NULL or an internal * entry. */ static inline void *xas_prev(struct xa_state *xas) { struct xa_node *node = xas->xa_node; if (unlikely(xas_not_node(node) || node->shift || xas->xa_offset == 0)) return __xas_prev(xas); xas->xa_index--; xas->xa_offset--; return xa_entry(xas->xa, node, xas->xa_offset); } /** * xas_next() - Move state to next index. * @xas: XArray operation state. * * If the @xas was in an error state, it will remain in an error state * and this function will return %NULL. If the @xas has never been walked, * it will have the effect of calling xas_load(). Otherwise one will be * added to the index and the state will be walked to the correct * location in the array for the next operation. * * If the iterator was referencing index %ULONG_MAX, this function wraps * around to 0. * * Return: The entry at the new index. This may be %NULL or an internal * entry. */ static inline void *xas_next(struct xa_state *xas) { struct xa_node *node = xas->xa_node; if (unlikely(xas_not_node(node) || node->shift || xas->xa_offset == XA_CHUNK_MASK)) return __xas_next(xas); xas->xa_index++; xas->xa_offset++; return xa_entry(xas->xa, node, xas->xa_offset); } #endif /* _LINUX_XARRAY_H */ ================================================ FILE: t/tree/include/trace/events/i2c.h ================================================ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* I2C message transfer tracepoints * * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #undef TRACE_SYSTEM #define TRACE_SYSTEM i2c #if !defined(_TRACE_I2C_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_I2C_H #include #include /* * drivers/i2c/i2c-core-base.c */ extern int i2c_transfer_trace_reg(void); extern void i2c_transfer_trace_unreg(void); /* * __i2c_transfer() write request */ TRACE_EVENT_FN(i2c_write, TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, int num), TP_ARGS(adap, msg, num), TP_STRUCT__entry( __field(int, adapter_nr ) __field(__u16, msg_nr ) __field(__u16, addr ) __field(__u16, flags ) __field(__u16, len ) __dynamic_array(__u8, buf, msg->len) ), TP_fast_assign( __entry->adapter_nr = adap->nr; __entry->msg_nr = num; __entry->addr = msg->addr; __entry->flags = msg->flags; __entry->len = msg->len; memcpy(__get_dynamic_array(buf), msg->buf, msg->len); ), TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]", __entry->adapter_nr, __entry->msg_nr, __entry->addr, __entry->flags, __entry->len, __entry->len, __get_dynamic_array(buf) ), i2c_transfer_trace_reg, i2c_transfer_trace_unreg); /* * __i2c_transfer() read request */ TRACE_EVENT_FN(i2c_read, TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, int num), TP_ARGS(adap, msg, num), TP_STRUCT__entry( __field(int, adapter_nr ) __field(__u16, msg_nr ) __field(__u16, addr ) __field(__u16, flags ) __field(__u16, len ) ), TP_fast_assign( __entry->adapter_nr = adap->nr; __entry->msg_nr = num; __entry->addr = msg->addr; __entry->flags = msg->flags; __entry->len = msg->len; ), TP_printk("i2c-%d #%u a=%03x f=%04x l=%u", __entry->adapter_nr, __entry->msg_nr, __entry->addr, __entry->flags, __entry->len ), i2c_transfer_trace_reg, i2c_transfer_trace_unreg); /* * __i2c_transfer() read reply */ TRACE_EVENT_FN(i2c_reply, TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, int num), TP_ARGS(adap, msg, num), TP_STRUCT__entry( __field(int, adapter_nr ) __field(__u16, msg_nr ) __field(__u16, addr ) __field(__u16, flags ) __field(__u16, len ) __dynamic_array(__u8, buf, msg->len) ), TP_fast_assign( __entry->adapter_nr = adap->nr; __entry->msg_nr = num; __entry->addr = msg->addr; __entry->flags = msg->flags; __entry->len = msg->len; memcpy(__get_dynamic_array(buf), msg->buf, msg->len); ), TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]", __entry->adapter_nr, __entry->msg_nr, __entry->addr, __entry->flags, __entry->len, __entry->len, __get_dynamic_array(buf) ), i2c_transfer_trace_reg, i2c_transfer_trace_unreg); /* * __i2c_transfer() result */ TRACE_EVENT_FN(i2c_result, TP_PROTO(const struct i2c_adapter *adap, int num, int ret), TP_ARGS(adap, num, ret), TP_STRUCT__entry( __field(int, adapter_nr ) __field(__u16, nr_msgs ) __field(__s16, ret ) ), TP_fast_assign( __entry->adapter_nr = adap->nr; __entry->nr_msgs = num; __entry->ret = ret; ), TP_printk("i2c-%d n=%u ret=%d", __entry->adapter_nr, __entry->nr_msgs, __entry->ret ), i2c_transfer_trace_reg, i2c_transfer_trace_unreg); #endif /* _TRACE_I2C_H */ /* This part must be outside protection */ #include ================================================ FILE: t/tree/include/uapi/linux/apm_bios.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * Include file for the interface to an APM BIOS * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au) * * 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, 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. */ #ifndef _UAPI_LINUX_APM_H #define _UAPI_LINUX_APM_H #include typedef unsigned short apm_event_t; typedef unsigned short apm_eventinfo_t; struct apm_bios_info { __u16 version; __u16 cseg; __u32 offset; __u16 cseg_16; __u16 dseg; __u16 flags; __u16 cseg_len; __u16 cseg_16_len; __u16 dseg_len; }; /* * Power states */ #define APM_STATE_READY 0x0000 #define APM_STATE_STANDBY 0x0001 #define APM_STATE_SUSPEND 0x0002 #define APM_STATE_OFF 0x0003 #define APM_STATE_BUSY 0x0004 #define APM_STATE_REJECT 0x0005 #define APM_STATE_OEM_SYS 0x0020 #define APM_STATE_OEM_DEV 0x0040 #define APM_STATE_DISABLE 0x0000 #define APM_STATE_ENABLE 0x0001 #define APM_STATE_DISENGAGE 0x0000 #define APM_STATE_ENGAGE 0x0001 /* * Events (results of Get PM Event) */ #define APM_SYS_STANDBY 0x0001 #define APM_SYS_SUSPEND 0x0002 #define APM_NORMAL_RESUME 0x0003 #define APM_CRITICAL_RESUME 0x0004 #define APM_LOW_BATTERY 0x0005 #define APM_POWER_STATUS_CHANGE 0x0006 #define APM_UPDATE_TIME 0x0007 #define APM_CRITICAL_SUSPEND 0x0008 #define APM_USER_STANDBY 0x0009 #define APM_USER_SUSPEND 0x000a #define APM_STANDBY_RESUME 0x000b #define APM_CAPABILITY_CHANGE 0x000c #define APM_USER_HIBERNATION 0x000d #define APM_HIBERNATION_RESUME 0x000e /* * Error codes */ #define APM_SUCCESS 0x00 #define APM_DISABLED 0x01 #define APM_CONNECTED 0x02 #define APM_NOT_CONNECTED 0x03 #define APM_16_CONNECTED 0x05 #define APM_16_UNSUPPORTED 0x06 #define APM_32_CONNECTED 0x07 #define APM_32_UNSUPPORTED 0x08 #define APM_BAD_DEVICE 0x09 #define APM_BAD_PARAM 0x0a #define APM_NOT_ENGAGED 0x0b #define APM_BAD_FUNCTION 0x0c #define APM_RESUME_DISABLED 0x0d #define APM_NO_ERROR 0x53 #define APM_BAD_STATE 0x60 #define APM_NO_EVENTS 0x80 #define APM_NOT_PRESENT 0x86 /* * APM Device IDs */ #define APM_DEVICE_BIOS 0x0000 #define APM_DEVICE_ALL 0x0001 #define APM_DEVICE_DISPLAY 0x0100 #define APM_DEVICE_STORAGE 0x0200 #define APM_DEVICE_PARALLEL 0x0300 #define APM_DEVICE_SERIAL 0x0400 #define APM_DEVICE_NETWORK 0x0500 #define APM_DEVICE_PCMCIA 0x0600 #define APM_DEVICE_BATTERY 0x8000 #define APM_DEVICE_OEM 0xe000 #define APM_DEVICE_OLD_ALL 0xffff #define APM_DEVICE_CLASS 0x00ff #define APM_DEVICE_MASK 0xff00 /* * Battery status */ #define APM_MAX_BATTERIES 2 /* * APM defined capability bit flags */ #define APM_CAP_GLOBAL_STANDBY 0x0001 #define APM_CAP_GLOBAL_SUSPEND 0x0002 #define APM_CAP_RESUME_STANDBY_TIMER 0x0004 /* Timer resume from standby */ #define APM_CAP_RESUME_SUSPEND_TIMER 0x0008 /* Timer resume from suspend */ #define APM_CAP_RESUME_STANDBY_RING 0x0010 /* Resume on Ring fr standby */ #define APM_CAP_RESUME_SUSPEND_RING 0x0020 /* Resume on Ring fr suspend */ #define APM_CAP_RESUME_STANDBY_PCMCIA 0x0040 /* Resume on PCMCIA Ring */ #define APM_CAP_RESUME_SUSPEND_PCMCIA 0x0080 /* Resume on PCMCIA Ring */ /* * ioctl operations */ #include #define APM_IOC_STANDBY _IO('A', 1) #define APM_IOC_SUSPEND _IO('A', 2) #endif /* _UAPI_LINUX_APM_H */ ================================================ FILE: t/tree/include/uapi/linux/eventpoll.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * include/linux/eventpoll.h ( Efficient event polling implementation ) * Copyright (C) 2001,...,2006 Davide Libenzi * * 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. * * Davide Libenzi * */ #ifndef _UAPI_LINUX_EVENTPOLL_H #define _UAPI_LINUX_EVENTPOLL_H /* For O_CLOEXEC */ #include #include /* Flags for epoll_create1. */ #define EPOLL_CLOEXEC O_CLOEXEC /* Valid opcodes to issue to sys_epoll_ctl() */ #define EPOLL_CTL_ADD 1 #define EPOLL_CTL_DEL 2 #define EPOLL_CTL_MOD 3 /* Epoll event masks */ #define EPOLLIN (__force __poll_t)0x00000001 #define EPOLLPRI (__force __poll_t)0x00000002 #define EPOLLOUT (__force __poll_t)0x00000004 #define EPOLLERR (__force __poll_t)0x00000008 #define EPOLLHUP (__force __poll_t)0x00000010 #define EPOLLNVAL (__force __poll_t)0x00000020 #define EPOLLRDNORM (__force __poll_t)0x00000040 #define EPOLLRDBAND (__force __poll_t)0x00000080 #define EPOLLWRNORM (__force __poll_t)0x00000100 #define EPOLLWRBAND (__force __poll_t)0x00000200 #define EPOLLMSG (__force __poll_t)0x00000400 #define EPOLLRDHUP (__force __poll_t)0x00002000 /* Set exclusive wakeup mode for the target file descriptor */ #define EPOLLEXCLUSIVE ((__force __poll_t)(1U << 28)) /* * Request the handling of system wakeup events so as to prevent system suspends * from happening while those events are being processed. * * Assuming neither EPOLLET nor EPOLLONESHOT is set, system suspends will not be * re-allowed until epoll_wait is called again after consuming the wakeup * event(s). * * Requires CAP_BLOCK_SUSPEND */ #define EPOLLWAKEUP ((__force __poll_t)(1U << 29)) /* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT ((__force __poll_t)(1U << 30)) /* Set the Edge Triggered behaviour for the target file descriptor */ #define EPOLLET ((__force __poll_t)(1U << 31)) /* * On x86-64 make the 64bit structure have the same alignment as the * 32bit structure. This makes 32bit emulation easier. * * UML/x86_64 needs the same packing as x86_64 */ #ifdef __x86_64__ #define EPOLL_PACKED __attribute__((packed)) #else #define EPOLL_PACKED #endif struct epoll_event { __poll_t events; __u64 data; } EPOLL_PACKED; #ifdef CONFIG_PM_SLEEP static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev) { if ((epev->events & EPOLLWAKEUP) && !capable(CAP_BLOCK_SUSPEND)) epev->events &= ~EPOLLWAKEUP; } #else static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev) { epev->events &= ~EPOLLWAKEUP; } #endif #endif /* _UAPI_LINUX_EVENTPOLL_H */ ================================================ FILE: t/tree/include/uapi/linux/i2c.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* ------------------------------------------------------------------------- */ /* */ /* i2c.h - definitions for the i2c-bus interface */ /* */ /* ------------------------------------------------------------------------- */ /* Copyright (C) 1995-2000 Simon G. Vogl 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. */ /* ------------------------------------------------------------------------- */ /* With some changes from Kyösti Mälkki and Frodo Looijaard */ #ifndef _UAPI_LINUX_I2C_H #define _UAPI_LINUX_I2C_H #include /** * struct i2c_msg - an I2C transaction segment beginning with START * @addr: Slave address, either seven or ten bits. When this is a ten * bit address, I2C_M_TEN must be set in @flags and the adapter * must support I2C_FUNC_10BIT_ADDR. * @flags: I2C_M_RD is handled by all adapters. No other flags may be * provided unless the adapter exported the relevant I2C_FUNC_* * flags through i2c_check_functionality(). * @len: Number of data bytes in @buf being read from or written to the * I2C slave address. For read transactions where I2C_M_RECV_LEN * is set, the caller guarantees that this buffer can hold up to * 32 bytes in addition to the initial length byte sent by the * slave (plus, if used, the SMBus PEC); and this value will be * incremented by the number of block data bytes received. * @buf: The buffer into which data is read, or from which it's written. * * An i2c_msg is the low level representation of one segment of an I2C * transaction. It is visible to drivers in the @i2c_transfer() procedure, * to userspace from i2c-dev, and to I2C adapter drivers through the * @i2c_adapter.@master_xfer() method. * * Except when I2C "protocol mangling" is used, all I2C adapters implement * the standard rules for I2C transactions. Each transaction begins with a * START. That is followed by the slave address, and a bit encoding read * versus write. Then follow all the data bytes, possibly including a byte * with SMBus PEC. The transfer terminates with a NAK, or when all those * bytes have been transferred and ACKed. If this is the last message in a * group, it is followed by a STOP. Otherwise it is followed by the next * @i2c_msg transaction segment, beginning with a (repeated) START. * * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then * passing certain @flags may have changed those standard protocol behaviors. * Those flags are only for use with broken/nonconforming slaves, and with * adapters which are known to support the specific mangling options they * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). */ struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_RD 0x0001 /* read data, from slave to master */ /* I2C_M_RD is guaranteed to be 0x0001! */ #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ /* makes only sense in kernelspace */ /* userspace buffers are copied anyway */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ }; /* To determine what functionality is present */ #define I2C_FUNC_I2C 0x00000001 #define I2C_FUNC_10BIT_ADDR 0x00000002 #define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */ #define I2C_FUNC_SMBUS_PEC 0x00000008 #define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */ #define I2C_FUNC_SLAVE 0x00000020 #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ #define I2C_FUNC_SMBUS_QUICK 0x00010000 #define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 #define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 #define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 #define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 #define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 #define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 #define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 #define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 #define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ #define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 #define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ I2C_FUNC_SMBUS_WRITE_BYTE) #define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ I2C_FUNC_SMBUS_WRITE_BYTE_DATA) #define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ I2C_FUNC_SMBUS_WRITE_WORD_DATA) #define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) #define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) #define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \ I2C_FUNC_SMBUS_BYTE | \ I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA | \ I2C_FUNC_SMBUS_PROC_CALL | \ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \ I2C_FUNC_SMBUS_I2C_BLOCK | \ I2C_FUNC_SMBUS_PEC) /* * Data for SMBus Messages */ #define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ union i2c_smbus_data { __u8 byte; __u16 word; __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ /* and one more for user-space compatibility */ }; /* i2c_smbus_xfer read or write markers */ #define I2C_SMBUS_READ 1 #define I2C_SMBUS_WRITE 0 /* SMBus transaction types (size parameter in the above functions) Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ #define I2C_SMBUS_QUICK 0 #define I2C_SMBUS_BYTE 1 #define I2C_SMBUS_BYTE_DATA 2 #define I2C_SMBUS_WORD_DATA 3 #define I2C_SMBUS_PROC_CALL 4 #define I2C_SMBUS_BLOCK_DATA 5 #define I2C_SMBUS_I2C_BLOCK_BROKEN 6 #define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ #define I2C_SMBUS_I2C_BLOCK_DATA 8 #endif /* _UAPI_LINUX_I2C_H */ ================================================ FILE: t/tree/include/uapi/linux/rseq.h ================================================ /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ #ifndef _UAPI_LINUX_RSEQ_H #define _UAPI_LINUX_RSEQ_H /* * linux/rseq.h * * Restartable sequences system call API * * Copyright (c) 2015-2018 Mathieu Desnoyers */ #include #include enum rseq_cpu_id_state { RSEQ_CPU_ID_UNINITIALIZED = -1, RSEQ_CPU_ID_REGISTRATION_FAILED = -2, }; enum rseq_flags { RSEQ_FLAG_UNREGISTER = (1 << 0), }; enum rseq_cs_flags_bit { RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT = 0, RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT = 1, RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, }; enum rseq_cs_flags { RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT = (1U << RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT), RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL = (1U << RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT), RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE = (1U << RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT), }; /* * struct rseq_cs is aligned on 4 * 8 bytes to ensure it is always * contained within a single cache-line. It is usually declared as * link-time constant data. */ struct rseq_cs { /* Version of this structure. */ __u32 version; /* enum rseq_cs_flags */ __u32 flags; __u64 start_ip; /* Offset from start_ip. */ __u64 post_commit_offset; __u64 abort_ip; } __attribute__((aligned(4 * sizeof(__u64)))); /* * struct rseq is aligned on 4 * 8 bytes to ensure it is always * contained within a single cache-line. * * A single struct rseq per thread is allowed. */ struct rseq { /* * Restartable sequences cpu_id_start field. Updated by the * kernel. Read by user-space with single-copy atomicity * semantics. This field should only be read by the thread which * registered this data structure. Aligned on 32-bit. Always * contains a value in the range of possible CPUs, although the * value may not be the actual current CPU (e.g. if rseq is not * initialized). This CPU number value should always be compared * against the value of the cpu_id field before performing a rseq * commit or returning a value read from a data structure indexed * using the cpu_id_start value. */ __u32 cpu_id_start; /* * Restartable sequences cpu_id field. Updated by the kernel. * Read by user-space with single-copy atomicity semantics. This * field should only be read by the thread which registered this * data structure. Aligned on 32-bit. Values * RSEQ_CPU_ID_UNINITIALIZED and RSEQ_CPU_ID_REGISTRATION_FAILED * have a special semantic: the former means "rseq uninitialized", * and latter means "rseq initialization failed". This value is * meant to be read within rseq critical sections and compared * with the cpu_id_start value previously read, before performing * the commit instruction, or read and compared with the * cpu_id_start value before returning a value loaded from a data * structure indexed using the cpu_id_start value. */ __u32 cpu_id; /* * Restartable sequences rseq_cs field. * * Contains NULL when no critical section is active for the current * thread, or holds a pointer to the currently active struct rseq_cs. * * Updated by user-space, which sets the address of the currently * active rseq_cs at the beginning of assembly instruction sequence * block, and set to NULL by the kernel when it restarts an assembly * instruction sequence block, as well as when the kernel detects that * it is preempting or delivering a signal outside of the range * targeted by the rseq_cs. Also needs to be set to NULL by user-space * before reclaiming memory that contains the targeted struct rseq_cs. * * Read and set by the kernel. Set by user-space with single-copy * atomicity semantics. This field should only be updated by the * thread which registered this data structure. Aligned on 64-bit. */ union { __u64 ptr64; #ifdef __LP64__ __u64 ptr; #else struct { #if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(__BIG_ENDIAN) __u32 padding; /* Initialized to zero. */ __u32 ptr32; #else /* LITTLE */ __u32 ptr32; __u32 padding; /* Initialized to zero. */ #endif /* ENDIAN */ } ptr; #endif } rseq_cs; /* * Restartable sequences flags field. * * This field should only be updated by the thread which * registered this data structure. Read by the kernel. * Mainly used for single-stepping through rseq critical sections * with debuggers. * * - RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT * Inhibit instruction sequence block restart on preemption * for this thread. * - RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL * Inhibit instruction sequence block restart on signal * delivery for this thread. * - RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE * Inhibit instruction sequence block restart on migration for * this thread. */ __u32 flags; } __attribute__((aligned(4 * sizeof(__u64)))); #endif /* _UAPI_LINUX_RSEQ_H */ ================================================ FILE: t/tree/issue102.c ================================================ void foo(void) { } /* This file triggers the bug described in #102. */ /** * documented_function_XYZZY */ void documented_function_XYZZY(void) { } /* t/tree/issue102.c */ /* SPDX-License-Identifier: CC0-1.0 */ ================================================ FILE: t/tree/issue131.h ================================================ /* This file triggers the bug described in #131. */ /** * struct class - structure * @member: A member * * Description. */ struct class { int member; }; void foo(struct class *klass) { klass->member = 42; } /* t/tree/issue131.c */ /* SPDX-License-Identifier: CC0-1.0 */ ================================================ FILE: t/tree/issue134.c ================================================ /* * issue134.c: tests exhibiting problems noted in * https://github.com/bootlin/elixir/issues/134#issue-624392766 * * t/tree/issue134.c * SPDX-License-Identifier: CC0-1.0 */ /** * issue134_function1: * Do something magical * and something quotidian * * @p1: Param * * Do something * * Returns whatever */ int issue134_function1(int p1) { write_test_cases(); } /** * issue134_function2: - does what it does * @param: something * * Whatever * * Whatever, paragraph 2. */ void __sched issue134_function2(struct completion *x) { take_action(); } /** * issue134_function3() - something * @param: whatever * @paramii: whatever else * * Description * * Return: %magic or * %-EIEIO if the farm bought */ int issue134_function3(struct issue134_struct1 *param, struct issue134_struct2 *paramii); ================================================ FILE: t/tree/issue150.S ================================================ /* Function memset should be located as a definition --- #150. */ #include .text ENTRY(memset) nop ENDPROC(memset) /* t/tree/issue150.S */ /* SPDX-License-Identifier: CC0-1.0 */ ================================================ FILE: t/tree/issue186-counterexamples.c ================================================ // SPDX-License-Identifier: CC0-1.0 // This file is the situation of #186, but with documentation. #ifdef SOMETHING /** * i186c_fn1 - do something */ static int i186c_fn1(struct platform_device *pdev) { } #else #define i186c_fn1 NULL #endif #ifdef SOMETHING static int i186c_fn2(struct platform_device *pdev) { } #else /** * i186c_fn2 - do something */ #define i186c_fn2 NULL #endif ================================================ FILE: t/tree/issue186.c ================================================ // SPDX-License-Identifier: CC0-1.0 // This file triggers the bug described in #186. #ifdef SOMETHING static int undocumented_function(struct platform_device *pdev) { } #else #define undocumented_function NULL #endif ================================================ FILE: t/tree/issue188.c ================================================ // SPDX-License-Identifier: CC0-1.0 // This file triggers the bug described in #188. static int undocumented_function(struct platform_device *pdev) { int foo; #define BAR (42) foo=1; return foo; } ================================================ FILE: t/tree/issue192.c ================================================ /* t/tree/issue192.c */ /* SPDX-License-Identifier: CC0-1.0 */ /* This file triggers the bug described in #192. */ /** * issue192a: - do something **/ int issue192a(const struct foo **bar, const char *bat, struct baz *quux) { return 0; } /** * issue192b() * * Allow an uppercase return type */ AMAZING_RETURN_TYPE issue192b(void *parameter) { return (AMAZING_RETURN_TYPE)31337; } ================================================ FILE: t/tree/syscall_define.c ================================================ SYSCALL_DEFINE3(init_module) { } ================================================ FILE: templates/error.html ================================================ {% extends "layout.html" %} {% block title %} Elixir error - Bootlin {% endblock %} {% block main %}

{{ error_title }}

{{ error_details|default('') }}
You might want to: {% if report_error_details is not none -%}
Error details for a bug report
{{ report_error_details }}
{% endif -%}
{% endblock %} ================================================ FILE: templates/header.html ================================================
================================================ FILE: templates/ident.html ================================================ {% extends "layout.html" %} {% block title %} {{ searched_ident|e }} identifier - {{ current_project|capitalize }} source code {{ current_tag }} - Bootlin Elixir Cross Referencer {% endblock %} {% block description -%} Elixir Cross Referencer - {{ searched_ident|e }} identifier references search for {{ current_project|capitalize }} {{ current_tag }}. {%- for section in symbol_sections -%} {%- if 'symbols' in section -%} {%- for type, symbols in (section['symbols'].items()) -%} {%- if (symbols|length) == 1 %} {{ section['title'] }} {{- ' as a '+type if type != '_unknown' else '' }} in {{ symbols[0].path }}. {%- else %} {{ section['title'] }} in {{ symbols|length }} files {{- ' as a '+type if type != '_unknown' else '' -}}: {{ symbols[0].path }}... {%- endif %} {%- endfor -%} {%- endif -%} {%- endfor -%} {%- endblock -%} {% block main %}
{% if symbol_sections|length != 0 %} {% for section in symbol_sections %} {% if 'symbols' in section %} {% for type, symbols in section['symbols'].items() %}

{{ section['title'] }} in {{ symbols|length }} files {{- ' as a '+type if type != '_unknown' else '' -}}:

{% endfor %} {% else %}

{{ section['message'] }}

{% endif %} {% endfor %} {% else %}

Unknown identifier '{{ searched_ident|e }}'

{% if symbol_exists %}
The '{{ searched_ident|e }}' symbol is defined in another version. Elixir cannot efficiently tell where.
{% endif %} {% endif %}
{% endblock %} ================================================ FILE: templates/layout.html ================================================ {% block title %} Bootlin - Elixir Cross Referencer {% endblock %}
{% include 'header.html' %} {% include 'topbar.html' %}
{% include "sidebar.html" %}
Loading...
{% block main %} {% endblock %}
================================================ FILE: templates/sidebar.html ================================================ ================================================ FILE: templates/source.html ================================================ {% extends "layout.html" %} {% block title %} {{ title_path }} {{ current_project|capitalize }} source code {{ current_tag }} - Bootlin Elixir Cross Referencer {% endblock %} {% block description -%} Elixir Cross Referencer - source code of {{ current_project|capitalize }} {{ current_tag -}} {{- '' if path|length <= 1 else ': ' + path[1:] }} {%- endblock %} {% block main %}
{{ code }}
{% endblock %} {% block footer %} Download file {% endblock %} ================================================ FILE: templates/topbar.html ================================================
================================================ FILE: templates/tree.html ================================================ {% extends "layout.html" %} {% block title %} {{ title_path }} {{ current_project|capitalize }} source code ({{ current_tag }}) - Bootlin Elixir Cross Referencer {% endblock %} {% block description -%} {%- if path == '' -%} Elixir Cross Referencer - explore {{ current_project|capitalize }} {{ current_tag }} source code in your browser. {%- else -%} Elixir Cross Referencer - source tree of {{ current_project|capitalize }} {{ current_tag -}} {{- '' if path|length <= 1 else ': ' + path[1:] }} {%- endif -%} {%- endblock -%} {% block main %}
{% if back_url is not none %} {% endif %} {% for type, name, path, url, size in dir_entries %} {% endfor %}
Parent directory
{% if type == 'symlink' %} {{ name }} -> {{ path }} {% else %} {{ name }} {% endif %} {% if size is not none %} {{ size }} bytes {% endif %}
{% endblock %} ================================================ FILE: update.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2017--2020 Mikaël Bouillot # Maxime Chretien # and contributors # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . # Throughout, an "idx" is the sequential number associated with a blob. # This is different from that blob's Git hash. from sys import argv from threading import Thread, Lock, Event, Condition import elixir.lib as lib from elixir.lib import script, scriptLines import elixir.data as data from elixir.data import PathList from find_compatible_dts import FindCompatibleDTS verbose = False dts_comp_support = int(script('dts-comp')) compatibles_parser = FindCompatibleDTS() db = data.DB(lib.getDataDir(), readonly=False, shared=True, dtscomp=dts_comp_support) # Number of cpu threads (+2 for version indexing) cpu = 10 threads_list = [] hash_file_lock = Lock() # Lock for db.hash and db.file blobs_lock = Lock() # Lock for db.blobs defs_lock = Lock() # Lock for db.defs refs_lock = Lock() # Lock for db.refs docs_lock = Lock() # Lock for db.docs comps_lock = Lock() # Lock for db.comps comps_docs_lock = Lock() # Lock for db.comps_docs tag_ready = Condition() # Waiting for new tags new_idxes = [] # (new idxes, Event idxes ready, Event defs ready, Event comps ready, Event vers ready) bindings_idxes = [] # DT bindings documentation files idx_key_mod = 1000000 defs_idxes = {} # Idents definitions stored with (idx*idx_key_mod + line) as the key. tags_done = False # True if all tags have been added to new_idxes # Progress variables [tags, finished threads] tags_defs = [0, 0] tags_defs_lock = Lock() tags_refs = [0, 0] tags_refs_lock = Lock() tags_docs = [0, 0] tags_docs_lock = Lock() tags_comps = [0, 0] tags_comps_lock = Lock() tags_comps_docs = [0, 0] tags_comps_docs_lock = Lock() class UpdateIds(Thread): def __init__(self, tag_buf): Thread.__init__(self, name="UpdateIdsElixir") self.tag_buf = tag_buf def run(self): global new_idxes, tags_done, tag_ready self.index = 0 for tag in self.tag_buf: new_idxes.append((self.update_blob_ids(tag), Event(), Event(), Event(), Event())) progress('ids: ' + tag.decode() + ': ' + str(len(new_idxes[self.index][0])) + ' new blobs', self.index+1) new_idxes[self.index][1].set() # Tell that the tag is ready self.index += 1 # Wake up waiting threads with tag_ready: tag_ready.notify_all() tags_done = True progress('ids: Thread finished', self.index) def update_blob_ids(self, tag): global hash_file_lock, blobs_lock if db.vars.exists('numBlobs'): idx = db.vars.get('numBlobs') else: idx = 0 # Get blob hashes and associated file names (without path) blobs = scriptLines('list-blobs', '-f', tag) new_idxes = [] for blob in blobs: hash, filename = blob.split(b' ',maxsplit=1) with blobs_lock: blob_exist = db.blob.exists(hash) if not blob_exist: db.blob.put(hash, idx) if not blob_exist: with hash_file_lock: db.hash.put(idx, hash) db.file.put(idx, filename) new_idxes.append(idx) if verbose: print(f"New blob #{idx} {hash}:{filename}") idx += 1 db.vars.put('numBlobs', idx) return new_idxes class UpdateVersions(Thread): def __init__(self, tag_buf): Thread.__init__(self, name="UpdateVersionsElixir") self.tag_buf = tag_buf def run(self): global new_idxes, tag_ready index = 0 while index < len(self.tag_buf): if index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() continue tag = self.tag_buf[index] new_idxes[index][1].wait() # Make sure the tag is ready self.update_versions(tag) new_idxes[index][4].set() # Tell that UpdateVersions processed the tag progress('vers: ' + tag.decode() + ' done', index+1) index += 1 progress('vers: Thread finished', index) def update_versions(self, tag): global blobs_lock # Get blob hashes and associated file paths blobs = scriptLines('list-blobs', '-p', tag) buf = [] for blob in blobs: hash, path = blob.split(b' ', maxsplit=1) with blobs_lock: idx = db.blob.get(hash) buf.append((idx, path)) buf = sorted(buf) obj = PathList() for idx, path in buf: obj.append(idx, path) # Store DT bindings documentation files to parse them later if path[:33] == b'Documentation/devicetree/bindings': bindings_idxes.append(idx) if verbose: print(f"Tag {tag}: adding #{idx} {path}") db.vers.put(tag, obj, sync=True) def generate_defs_caches(): for key in db.defs.get_keys(): value = db.defs.get(key) for family in ['C', 'K', 'D', 'M']: if (lib.compatibleFamily(value.get_families(), family) or lib.compatibleMacro(value.get_macros(), family)): db.defs_cache[family].put(key, b'') class UpdateDefs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateDefsElixir") self.index = start self.inc = inc # Equivalent to the number of defs threads def run(self): global new_idxes, tags_done, tag_ready, tags_defs, tags_defs_lock while not (tags_done and self.index >= len(new_idxes)): if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() continue new_idxes[self.index][1].wait() # Make sure the tag is ready with tags_defs_lock: tags_defs[0] += 1 self.update_definitions(new_idxes[self.index][0]) new_idxes[self.index][2].set() # Tell that UpdateDefs processed the tag self.index += self.inc with tags_defs_lock: tags_defs[1] += 1 progress('defs: Thread ' + str(tags_defs[1]) + '/' + str(self.inc) + ' finished', tags_defs[0]) def update_definitions(self, idxes): global hash_file_lock, defs_lock, tags_defs for idx in idxes: if idx % 1000 == 0: progress('defs: ' + str(idx), tags_defs[0]) with hash_file_lock: hash = db.hash.get(idx) filename = db.file.get(idx) family = lib.getFileFamily(filename) if family in [None, 'M']: continue lines = scriptLines('parse-defs', hash, filename, family) with defs_lock: for l in lines: ident, type, line = l.split(b' ') type = type.decode() line = int(line.decode()) defs_idxes[idx*idx_key_mod + line] = ident if db.defs.exists(ident): obj = db.defs.get(ident) elif lib.isIdent(ident): obj = data.DefList() else: continue obj.append(idx, type, line, family) if verbose: print(f"def {type} {ident} in #{idx} @ {line}") db.defs.put(ident, obj) generate_defs_caches() class UpdateRefs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateRefsElixir") self.index = start self.inc = inc # Equivalent to the number of refs threads def run(self): global new_idxes, tags_done, tags_refs, tags_refs_lock while not (tags_done and self.index >= len(new_idxes)): if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() continue new_idxes[self.index][1].wait() # Make sure the tag is ready new_idxes[self.index][2].wait() # Make sure UpdateDefs processed the tag with tags_refs_lock: tags_refs[0] += 1 self.update_references(new_idxes[self.index][0]) self.index += self.inc with tags_refs_lock: tags_refs[1] += 1 progress('refs: Thread ' + str(tags_refs[1]) + '/' + str(self.inc) + ' finished', tags_refs[0]) def update_references(self, idxes): global hash_file_lock, defs_lock, refs_lock, tags_refs for idx in idxes: if idx % 1000 == 0: progress('refs: ' + str(idx), tags_refs[0]) with hash_file_lock: hash = db.hash.get(idx) filename = db.file.get(idx) family = lib.getFileFamily(filename) if family == None: continue prefix = b'' # Kconfig values are saved as CONFIG_ if family == 'K': prefix = b'CONFIG_' tokens = scriptLines('tokenize-file', '-b', hash, family) even = True line_num = 1 idents = {} with defs_lock: for tok in tokens: even = not even if even: tok = prefix + tok if (db.defs.exists(tok) and not ( (idx*idx_key_mod + line_num) in defs_idxes and defs_idxes[idx*idx_key_mod + line_num] == tok ) and (family != 'M' or tok.startswith(b'CONFIG_'))): # We only index CONFIG_??? in makefiles if tok in idents: idents[tok] += ',' + str(line_num) else: idents[tok] = str(line_num) else: line_num += tok.count(b'\1') with refs_lock: for ident, lines in idents.items(): if db.refs.exists(ident): obj = db.refs.get(ident) else: obj = data.RefList() obj.append(idx, lines, family) if verbose: print(f"ref: {ident} in #{idx} @ {lines}") db.refs.put(ident, obj) class UpdateDocs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateDocsElixir") self.index = start self.inc = inc # Equivalent to the number of docs threads def run(self): global new_idxes, tags_done, tags_docs, tags_docs_lock while not (tags_done and self.index >= len(new_idxes)): if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() continue new_idxes[self.index][1].wait() # Make sure the tag is ready with tags_docs_lock: tags_docs[0] += 1 self.update_doc_comments(new_idxes[self.index][0]) self.index += self.inc with tags_docs_lock: tags_docs[1] += 1 progress('docs: Thread ' + str(tags_docs[1]) + '/' + str(self.inc) + ' finished', tags_docs[0]) def update_doc_comments(self, idxes): global hash_file_lock, docs_lock, tags_docs for idx in idxes: if idx % 1000 == 0: progress('docs: ' + str(idx), tags_docs[0]) with hash_file_lock: hash = db.hash.get(idx) filename = db.file.get(idx) family = lib.getFileFamily(filename) if family in [None, 'M']: continue lines = scriptLines('parse-docs', hash, filename) with docs_lock: for l in lines: ident, line = l.split(b' ') line = int(line.decode()) if db.docs.exists(ident): obj = db.docs.get(ident) else: obj = data.RefList() obj.append(idx, str(line), family) if verbose: print(f"doc: {ident} in #{idx} @ {line}") db.docs.put(ident, obj) class UpdateComps(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateCompsElixir") self.index = start self.inc = inc # Equivalent to the number of comps threads def run(self): global new_idxes, tags_done, tags_comps, tags_comps_lock while not (tags_done and self.index >= len(new_idxes)): if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() continue new_idxes[self.index][1].wait() # Make sure the tag is ready with tags_comps_lock: tags_comps[0] += 1 self.update_compatibles(new_idxes[self.index][0]) new_idxes[self.index][3].set() # Tell that UpdateComps processed the tag self.index += self.inc with tags_comps_lock: tags_comps[1] += 1 progress('comps: Thread ' + str(tags_comps[1]) + '/' + str(self.inc) + ' finished', tags_comps[0]) def update_compatibles(self, idxes): global hash_file_lock, comps_lock, tags_comps for idx in idxes: if idx % 1000 == 0: progress('comps: ' + str(idx), tags_comps[0]) with hash_file_lock: hash = db.hash.get(idx) filename = db.file.get(idx) family = lib.getFileFamily(filename) if family in [None, 'K', 'M']: continue lines = compatibles_parser.run(scriptLines('get-blob', hash), family) comps = {} for l in lines: ident, line = l.split(' ') if ident in comps: comps[ident] += ',' + str(line) else: comps[ident] = str(line) with comps_lock: for ident, lines in comps.items(): if db.comps.exists(ident): obj = db.comps.get(ident) else: obj = data.RefList() obj.append(idx, lines, family) if verbose: print(f"comps: {ident} in #{idx} @ {line}") db.comps.put(ident, obj) class UpdateCompsDocs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateCompsDocsElixir") self.index = start self.inc = inc # Equivalent to the number of comps_docs threads def run(self): global new_idxes, tags_done, tags_comps_docs, tags_comps_docs_lock while not (tags_done and self.index >= len(new_idxes)): if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() continue new_idxes[self.index][1].wait() # Make sure the tag is ready new_idxes[self.index][3].wait() # Make sure UpdateComps processed the tag new_idxes[self.index][4].wait() # Make sure UpdateVersions processed the tag with tags_comps_docs_lock: tags_comps_docs[0] += 1 self.update_compatibles_bindings(new_idxes[self.index][0]) self.index += self.inc with tags_comps_docs_lock: tags_comps_docs[1] += 1 progress('comps_docs: Thread ' + str(tags_comps_docs[1]) + '/' + str(self.inc) + ' finished', tags_comps_docs[0]) def update_compatibles_bindings(self, idxes): global hash_file_lock, comps_lock, comps_docs_lock, tags_comps_docs, bindings_idxes for idx in idxes: if idx % 1000 == 0: progress('comps_docs: ' + str(idx), tags_comps_docs[0]) if not idx in bindings_idxes: # Parse only bindings doc files continue with hash_file_lock: hash = db.hash.get(idx) family = 'B' lines = compatibles_parser.run(scriptLines('get-blob', hash), family) comps_docs = {} with comps_lock: for l in lines: ident, line = l.split(' ') if db.comps.exists(ident): if ident in comps_docs: comps_docs[ident] += ',' + str(line) else: comps_docs[ident] = str(line) with comps_docs_lock: for ident, lines in comps_docs.items(): if db.comps_docs.exists(ident): obj = db.comps_docs.get(ident) else: obj = data.RefList() obj.append(idx, lines, family) if verbose: print(f"comps_docs: {ident} in #{idx} @ {line}") db.comps_docs.put(ident, obj) def progress(msg, current): print('{} - {} ({:.1%})'.format(project, msg, current/num_tags)) # Main # Check number of threads arg if len(argv) >= 2 and argv[1].isdigit() : cpu = int(argv[1]) if cpu < 5 : cpu = 5 # Distribute threads among functions using the following rules : # There are more (or equal) refs threads than others # There are more (or equal) defs threads than docs or comps threads # Example : if cpu=6 : defs=1, refs=2, docs=1, comps=1, comps_docs=1 # if cpu=7 : defs=2, refs=2, docs=1, comps=1, comps_docs=1 # if cpu=8 : defs=2, refs=3, docs=1, comps=1, comps_docs=1 # if cpu=11: defs=2, refs=3, docs=2, comps=2, comps_docs=2 quo, rem = divmod(cpu, 5) num_th_refs = quo num_th_defs = quo num_th_docs = quo # If DT bindings support is enabled, use $quo threads for each of the 2 threads # Otherwise add them to the remaining threads if dts_comp_support: num_th_comps = quo num_th_comps_docs = quo else : num_th_comps = 0 num_th_comps_docs = 0 rem += 2*quo quo, rem = divmod(rem, 2) num_th_defs += quo num_th_refs += quo + rem tag_buf = [] for tag in scriptLines('list-tags'): if not db.vers.exists(tag): tag_buf.append(tag) num_tags = len(tag_buf) project = lib.currentProject() print(project + ' - found ' + str(num_tags) + ' new tags') if not num_tags: # Backward-compatibility: generate defs caches if they are empty. if db.defs_cache['C'].db.stat()['nkeys'] == 0: generate_defs_caches() exit(0) threads_list.append(UpdateIds(tag_buf)) threads_list.append(UpdateVersions(tag_buf)) # Define defs threads for i in range(num_th_defs): threads_list.append(UpdateDefs(i, num_th_defs)) # Define refs threads for i in range(num_th_refs): threads_list.append(UpdateRefs(i, num_th_refs)) # Define docs threads for i in range(num_th_docs): threads_list.append(UpdateDocs(i, num_th_docs)) # Define comps threads for i in range(num_th_comps): threads_list.append(UpdateComps(i, num_th_comps)) # Define comps_docs threads for i in range(num_th_comps_docs): threads_list.append(UpdateCompsDocs(i, num_th_comps_docs)) # Start to process tags threads_list[0].start() # Wait until the first tag is ready with tag_ready: tag_ready.wait() # Start remaining threads for i in range(1, len(threads_list)): threads_list[i].start() # Make sure all threads finished for i in range(len(threads_list)): threads_list[i].join() ================================================ FILE: utils/index ================================================ #!/bin/bash if test $# -lt 2; then echo "Usage: $0 [...]" echo "Usage: $0 --all" exit 1 fi # $1 is the project path (inside will be created data/ and repo/). # It supports being called on an existing project. project_init() { # Detect already inited projects. Avoids stderr logs. # Using `git tag -n1` because `git status` doesn't work on bare repos. if git -C $1/repo tag -n1 >/dev/null 2>/dev/null; then return; fi mkdir -p $1/data $1/repo git -C $1/repo -c init.defaultBranch=main init --bare } # $1 is the project path (parent of data/ and repo/). # $2 is the remote URL. project_add_remote() { git="git -C $1/repo -c safe.directory=$1/repo" # Do nothing if remote already exists. if $git remote | xargs -L1 -r $git remote get-url 2>/dev/null | grep -qxF "$2"; then return; fi # Remotes are called remote$i with $i = 0, 1, 2... i="$($git remote | awk ' BEGIN { n=-1; } $0 ~ /^remote[0-9]+$/ { i=substr($0, length("remote")+1); if (i>n) n=i; } END { print n+1; }')" $git remote add remote$i "$2" } # $1 is the project path (parent of data/ and repo/). project_fetch() { git="git -C $1/repo -c safe.directory=$1/repo" $git fetch --all --tags -j4 # A gc.log file implies a garbage collect failed in the past. # Also, create a hidden flag which could be useful to trigger GCs manually. if test -e $1/repo/gc.log -o "$ELIXIR_GC"; then $git gc --aggressive else # Otherwise, give Git an occasion to trigger a GC. # Porcelain commands should trigger that, but we don't use any. $git gc --auto fi } # $1 is the project path (parent of data/ and repo/). project_index() { if test -z "$ELIXIR_THREADS"; then ELIXIR_THREADS="$(nproc)" fi elixir_sources="$(dirname "$(dirname "$0")")" LXR_REPO_DIR=$1/repo LXR_DATA_DIR=$1/data \ python3 "$elixir_sources/update.py" $ELIXIR_THREADS } # $1 is the Elixir root data path. # $2 is the project name. # $... are the remote URLs. add_remotes() { dir="$1/$2" project_init "$dir" shift shift for remote do project_add_remote "$dir" "$remote" done } # Call add_remotes() if no remotes are passed as arguments. # # $1 is the Elixir root data path. # $2 is the CLI arg count. # $3 is the CLI arg for project name (can be --all). # $4 is the project name. # $... are the default remote URLs. add_default_remotes() { if test $2 -eq 2 -a \( "$3" = "--all" -o "$3" = "$4" \); then add_remotes "$1" "$4" ${@:5} fi } do_index() { if test ! "$(find $1/data -type f)"; then # If we are indexing from scratch, do it twice as the initial one # probably took a lot of time. project_fetch "$1" project_index "$1" project_fetch "$1" project_index "$1" else project_fetch "$1" project_index "$1" fi } # Add all known projects remotes. This works in two cases: # ./utils/index --all # => Add default remotes for all projects # ./utils/index musl # => Add default remote for musl add_default_remotes $1 $# $2 amazon-freertos https://github.com/aws/amazon-freertos.git add_default_remotes $1 $# $2 arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware add_default_remotes $1 $# $2 barebox https://git.pengutronix.de/git/barebox add_default_remotes $1 $# $2 busybox https://git.busybox.net/busybox add_default_remotes $1 $# $2 coreboot https://review.coreboot.org/coreboot.git add_default_remotes $1 $# $2 dpdk https://dpdk.org/git/dpdk \ https://dpdk.org/git/dpdk-stable add_default_remotes $1 $# $2 glibc https://sourceware.org/git/glibc.git add_default_remotes $1 $# $2 llvm https://github.com/llvm/llvm-project.git add_default_remotes $1 $# $2 mesa https://gitlab.freedesktop.org/mesa/mesa.git add_default_remotes $1 $# $2 musl https://git.musl-libc.org/git/musl add_default_remotes $1 $# $2 ofono https://git.kernel.org/pub/scm/network/ofono/ofono.git add_default_remotes $1 $# $2 op-tee https://github.com/OP-TEE/optee_os.git add_default_remotes $1 $# $2 qemu https://gitlab.com/qemu-project/qemu.git add_default_remotes $1 $# $2 u-boot https://source.denx.de/u-boot/u-boot.git add_default_remotes $1 $# $2 uclibc-ng https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git add_default_remotes $1 $# $2 zephyr https://github.com/zephyrproject-rtos/zephyr add_default_remotes $1 $# $2 toybox https://github.com/landley/toybox.git add_default_remotes $1 $# $2 grub https://git.savannah.gnu.org/git/grub.git add_default_remotes $1 $# $2 bluez https://git.kernel.org/pub/scm/bluetooth/bluez.git add_default_remotes $1 $# $2 linux https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git \ https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git \ https://github.com/bootlin/linux-history.git add_default_remotes $1 $# $2 xen https://xenbits.xen.org/git-http/xen.git add_default_remotes $1 $# $2 freebsd https://git.freebsd.org/src.git add_default_remotes $1 $# $2 opensbi https://github.com/riscv-software-src/opensbi add_default_remotes $1 $# $2 iproute2 https://git.kernel.org/pub/scm/network/iproute2/iproute2.git add_default_remotes $1 $# $2 vpp https://gerrit.fd.io/r/vpp # Index a single project if test "x$2" != "x--all"; then dir="$1/$2" add_remotes "$@" do_index "$dir" else # Index all projects. # Note: this is not only the default projects ones but all the ones in $1. find $1 -mindepth 1 -maxdepth 1 -type d | \ while read dir; do do_index "$dir" done fi ================================================ FILE: utils/query.py ================================================ from elixir.query import Query from elixir import lib def cmd_stats(q, **kwargs): print("Versions: ", len(q.db.vers)) print("Blobs: ", len(q.db.blob)) if len(q.db.blob) != len(q.db.hash) or len(q.db.hash) != len(q.db.file): print("Warning, number of blobs, hashes or files is not equal") print("Definitions: ", len(q.db.defs)) print("References: ", len(q.db.refs)) def cmd_versions(q, **kwargs): for major in q.get_versions().values(): for minor in major.values(): for v in minor: print(v) def cmd_ident(q, version, ident, family, **kwargs): symbol_definitions, symbol_references, symbol_doccomments, _ = q.search_ident(version, ident, family) print("Symbol Definitions:") for symbol_definition in symbol_definitions: print(symbol_definition) print("\nSymbol References:") for symbol_reference in symbol_references: print(symbol_reference) print("\nDocumented in:") for symbol_doccomment in symbol_doccomments: print(symbol_doccomment) def cmd_file(q, version, path, **kwargs): code = q.get_tokenized_file(version, path) print(code) if __name__ == "__main__": import argparse query = Query(lib.getDataDir(), lib.getRepoDir()) parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(required=True) ident_subparser = subparsers.add_parser('stats', help="Get basic database stats") ident_subparser.set_defaults(func=cmd_stats, q=query) ident_subparser = subparsers.add_parser('versions', help="Get list of versions in the project") ident_subparser.set_defaults(func=cmd_versions, q=query) ident_subparser = subparsers.add_parser('ident', help="Get definitions and references of an identifier") ident_subparser.add_argument("version", help="The version of the project", type=str, default="latest") ident_subparser.add_argument('ident', type=str, help="The name of the identifier") ident_subparser.add_argument('family', type=str, help="The file family requested") ident_subparser.set_defaults(func=cmd_ident, q=query) file_subparser = subparsers.add_parser('file', help="Get a source file") file_subparser.add_argument("version", help="The version of the project", type=str, default="latest") file_subparser.add_argument('path', type=str, help="The path of the source file") file_subparser.set_defaults(func=cmd_file, q=query) args = parser.parse_args() args.func(**vars(args)) ================================================ FILE: utils/speedtest.py ================================================ #!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # # Copyright (C) 2020 Maxime Chretien # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Elixir 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . from time import time import os import sys BOLD = '\033[1m' NORMAL = '\033[0m' ELIXIR_DIR = os.path.dirname(__file__) + '/..' sys.path = [ ELIXIR_DIR ] + sys.path #Parameters run_number = 100 verbose = False #List of test elements project = 'linux' versions = ['latest', 'v5.6.2'] idents = [ ('loopback', 'C'), ('devm_register_reboot_notifier', 'C'), ('notrace', 'C'), ('arch_local_irq_restore', 'C'), ('blk_queue_dma_alignment', 'C'), ('spinlock_t', 'C'), ('max', 'C'), ('task_struct', 'C'), ('eth_header', 'C'), ('sk_buff', 'C') ] files = [ '/block/partitions/osf.c', '/mm/kasan/quarantine.c', '/include/crypto/internal/hash.h', '/drivers/gpu/drm/gma500/gma_display.c', '/drivers/hwmon/pmbus/ltc2978.c', '/virt/lib/irqbypass.c', '/Makefile', '/fs/btrfs/tree-checker.c', '/kernel/locking/qspinlock_stat.h', '/arch/arm/boot/dts/armada-375.dtsi' ] #Test results idents_results = [] idents_max = 0 idents_min = 0 idents_average = 0 files_results = [] files_max = 0 files_min = 0 files_average = 0 def init_query(project): if 'LXR_PROJ_DIR' not in os.environ: print("ERROR : LXR_PROJ_DIR not defined !") return None; basedir = os.environ['LXR_PROJ_DIR'] datadir = basedir + '/' + project + '/data' repodir = basedir + '/' + project + '/repo' from elixir.query import Query return Query(datadir, repodir) def get_ident(query, ident, version): if version == ('latest', 'latest-rc'): rc = version == 'latest' version = query.get_latest_tag(rc=rc) return query.search_ident(version, ident[0], ident[1]) def get_file(query, path, version): if version == ('latest', 'latest-rc'): rc = version == 'latest' version = query.get_latest_tag(rc=rc) return query.get_file(version, path) #Read arguments if len(sys.argv) > 1: if sys.argv[1] == '-v': verbose = True elif sys.argv[1] == '-h': print("LXR_PROJ_DIR needs to be set before launching this script\n" + "Options :\n" + "-v Verbose mode (Show requests details)") exit() #Query init query = init_query(project) if query == None: exit() #Database test print((BOLD + "Database test for project {}" + NORMAL).format(project)) print("Each test runs {} times".format(run_number)) for version in versions: print((BOLD + "\nVersion tested : {}\n" + NORMAL).format(version)) print(BOLD + "Identifiers access test\n" + NORMAL) for i in range(run_number): for ident in idents: start_time = time() get_ident(query, ident, version) end_time = time() elapsed_time = (end_time - start_time)*1000 #convert to ms idents_results.append(elapsed_time) if verbose: print("Identifier : {}".format(ident)) print("Elapsed time : {0:.6f} ms\n".format(elapsed_time)) idents_min = min(idents_results) idents_max = max(idents_results) idents_average = sum(idents_results)/len(idents_results) print((BOLD + "Min:" + NORMAL + " {0:.6f} ms\n" + BOLD + "Max:" + NORMAL + " {1:.6f} ms\n" + BOLD + "Average:" + NORMAL + " {2:.6f} ms\n" ).format(idents_min, idents_max, idents_average)) print(BOLD + "Files access test\n" + NORMAL) for i in range(run_number): for file in files: start_time = time() get_file(query, file, version) end_time = time() elapsed_time = (end_time - start_time) * 1000 #convert to ms files_results.append(elapsed_time) if verbose: print("File : {}".format(file)) print("Elapsed time : {0:.6f} ms\n".format(elapsed_time)) files_min = min(files_results) files_max = max(files_results) files_average = sum(files_results)/len(files_results) print((BOLD + "Min:" + NORMAL + " {0:.6f} ms\n" + BOLD + "Max:" + NORMAL + " {1:.6f} ms\n" + BOLD + "Average:" + NORMAL + " {2:.6f} ms\n" ).format(files_min, files_max, files_average)) ================================================ FILE: wsgi.py ================================================ from elixir.web import application