├── .gitignore ├── AUTHORS ├── ChangeLog ├── LICENSES ├── CC0-1.0.txt ├── GPL-2.0-only.txt └── MIT.txt ├── Makefile.am ├── README ├── autogen.sh ├── callbacks.c ├── ccan ├── check_type │ └── check_type.h ├── config.h ├── container_of │ └── container_of.h ├── list │ └── list.h └── str │ ├── str.h │ └── str_debug.h ├── com.kroah.usbview.metainfo.xml ├── configure.ac ├── interface.c ├── main.c ├── sysfs.c ├── sysfs.h ├── usbtree.c ├── usbtree.h ├── usbview.8 ├── usbview.desktop ├── usbview.spdx ├── usbview_icon.svg ├── usbview_logo.xcf └── usbview_logo.xpm /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (c) 2022 Greg Kroah-Hartman 3 | *.gz 4 | *.o 5 | *~ 6 | .deps/ 7 | /aclocal.m4 8 | /autom4te.cache/ 9 | /compile 10 | /config.h 11 | /config.h.in 12 | /config.log 13 | /config.status 14 | /configure 15 | /depcomp 16 | /hicolor/ 17 | /install-sh 18 | /missing 19 | /stamp-h1 20 | /usbview 21 | /usbview_icon.xpm 22 | Makefile 23 | Makefile.in 24 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | -- AUTHORS for USBView - a USB device viewer 2 | -- SPDX-License-Identifier: GPL-2.0-only 3 | -- Copyright (c) 1999-2012, 2021-2022 by Greg Kroah-Hartman, greg@kroah.com 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; version 2 of the License. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- (See the full license text in the LICENSES directory) 15 | -- 16 | 17 | Author: 18 | Greg Kroah-Hartman 19 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregkh/usbview/592e5ed367dc09f773357a39c3141636be5de248/ChangeLog -------------------------------------------------------------------------------- /LICENSES/CC0-1.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /LICENSES/GPL-2.0-only.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 6 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. 12 | 13 | When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. 14 | 15 | To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. 16 | 17 | For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. 18 | 19 | We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. 20 | 21 | Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. 22 | 23 | Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 24 | 25 | The precise terms and conditions for copying, distribution and modification follow. 26 | 27 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 28 | 29 | 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". 30 | 31 | Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 32 | 33 | 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 34 | 35 | You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 36 | 37 | 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 38 | 39 | a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 40 | 41 | b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. 42 | 43 | c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) 44 | 45 | These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 46 | 47 | Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. 48 | 49 | In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 50 | 51 | 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: 52 | 53 | a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 54 | 55 | b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 56 | 57 | c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) 58 | 59 | The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 60 | 61 | If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 62 | 63 | 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 64 | 65 | 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 66 | 67 | 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 68 | 69 | 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. 70 | 71 | If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. 72 | 73 | It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 74 | 75 | This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 76 | 77 | 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 78 | 79 | 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 80 | 81 | Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 82 | 83 | 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 84 | 85 | NO WARRANTY 86 | 87 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 88 | 89 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 90 | 91 | END OF TERMS AND CONDITIONS 92 | 93 | How to Apply These Terms to Your New Programs 94 | 95 | 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. 96 | 97 | To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 98 | 99 | one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author 100 | 101 | 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. 102 | 103 | 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. 104 | 105 | You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. 106 | 107 | If the program is interactive, make it output a short notice like this when it starts in an interactive mode: 108 | 109 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. 110 | 111 | The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. 112 | 113 | You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: 114 | 115 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. 116 | 117 | signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice 118 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (c) 2022 Greg Kroah-Hartman 3 | # 4 | ## Process this file with automake to produce Makefile.in 5 | 6 | AM_CPPFLAGS = $(GTK_CFLAGS) 7 | usbview_LDADD = $(GTK_LIBS) 8 | 9 | bin_PROGRAMS = usbview 10 | 11 | man_MANS = usbview.8 12 | 13 | usbview_SOURCES = \ 14 | main.c \ 15 | interface.c \ 16 | callbacks.c \ 17 | usbtree.c usbtree.h \ 18 | sysfs.c sysfs.h \ 19 | ccan/check_type/check_type.h \ 20 | ccan/str/str.h \ 21 | ccan/str/str_debug.h \ 22 | ccan/config.h \ 23 | ccan/container_of/container_of.h\ 24 | ccan/list/list.h \ 25 | usbview_logo.xpm 26 | 27 | interface.o: $(icon_bitmaps_xpm) 28 | 29 | EXTRA_DIST = $(man_MANS) usbview_icon.svg usbview.desktop \ 30 | usbview_logo.xcf \ 31 | com.kroah.usbview.metainfo.xml \ 32 | LICENSES/GPL-2.0-only.txt 33 | 34 | desktopdir = $(datadir)/applications 35 | metainfodir = $(datadir)/metainfo 36 | if DESKTOP 37 | desktop_DATA = usbview.desktop 38 | metainfo_DATA = com.kroah.usbview.metainfo.xml 39 | endif 40 | 41 | icondir = $(datadir)/icons 42 | 43 | icon_bitmaps_png = \ 44 | hicolor/16x16/apps/usbview.png \ 45 | hicolor/22x22/apps/usbview.png \ 46 | hicolor/32x32/apps/usbview.png \ 47 | hicolor/48x48/apps/usbview.png \ 48 | hicolor/64x64/apps/usbview.png \ 49 | hicolor/256x256/apps/usbview.png 50 | 51 | icon_bitmaps_xpm = hicolor/64x64/apps/usbview_icon.xpm 52 | 53 | if ICONS 54 | nobase_icon_DATA = $(icon_scalable) $(icon_bitmaps_png) 55 | endif 56 | 57 | $(icon_bitmaps_png): usbview_icon.svg 58 | mkdir -p $$(dirname $@) 59 | if HAVE_CONVERT 60 | $(CONVERT) -background none -density 300x300 -geometry $$(basename $$(dirname $$(dirname $@))) -density 96x96 $< $@ 61 | else 62 | echo "error: unable to generate $@ from $<" 63 | exit 1 64 | endif 65 | 66 | $(icon_bitmaps_xpm): usbview_icon.svg 67 | mkdir -p $$(dirname $@) 68 | if HAVE_CONVERT 69 | $(CONVERT) -background none -density 300x300 -geometry $$(basename $$(dirname $$(dirname $@))) -density 96x96 $< $@ 70 | else 71 | echo "error: unable to generate $@ from $<" 72 | exit 1 73 | endif 74 | 75 | icon_scalable = hicolor/scalable/apps/usbview.svg 76 | 77 | $(icon_scalable): usbview_icon.svg 78 | mkdir -p $$(dirname $@) 79 | cp $< $@ 80 | 81 | CLEANFILES = $(icon_scalable) $(icon_bitmaps_png) $(icon_bitmaps_xpm) 82 | 83 | # gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor; gtk-update-icon-cache -f -t $(datadir)/icons/HighContrast 84 | # 85 | # install-data-hook: update-icon-cache 86 | # uninstall-hook: update-icon-cache 87 | # update-icon-cache: 88 | # @-if test -z "$(DESTDIR)"; then \ 89 | # echo "Updating Gtk icon cache."; \ 90 | # $(gtk_update_icon_cache); \ 91 | # else \ 92 | # echo "*** Icon cache not updated. After (un)install, run this:"; \ 93 | # echo "*** $(gtk_update_icon_cache)"; \ 94 | # fi 95 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | -- README for USBView - a USB device viewer 2 | -- SPDX-License-Identifier: GPL-2.0-only 3 | -- Copyright (c) 1999-2012, 2021-2022 by Greg Kroah-Hartman, greg@kroah.com 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; version 2 of the License. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- (See the full license text in the LICENSES directory) 15 | -- 16 | 17 | USBView is a small GTK application to show what the device tree of 18 | the USB bus looks like. It shows a graphical representation of the 19 | devices that are currently plugged in, showing the topology of the 20 | USB bus. It also displays information on each individual device on 21 | the bus. 22 | 23 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # Copyright (c) 2009,2010 Greg Kroah-Hartman 4 | 5 | #gtkdocize 6 | autoreconf --install --symlink 7 | 8 | MYCFLAGS="-g -Wall \ 9 | -Wmissing-declarations -Wmissing-prototypes \ 10 | -Wnested-externs -Wpointer-arith \ 11 | -Wpointer-arith -Wsign-compare -Wchar-subscripts \ 12 | -Wstrict-prototypes -Wshadow \ 13 | -Wno-stringop-truncation \ 14 | -Wformat=2 -Wtype-limits" 15 | 16 | case "$CFLAGS" in 17 | *-O[0-9]*) 18 | ;; 19 | *) 20 | MYCFLAGS="$MYCFLAGS -O2" 21 | ;; 22 | esac 23 | 24 | libdir() { 25 | echo $(cd $1/$(gcc -print-multi-os-directory); pwd) 26 | } 27 | 28 | args="--prefix=/usr \ 29 | --sysconfdir=/etc \ 30 | --sbindir=/sbin \ 31 | --libdir=$(libdir /usr/lib)" 32 | 33 | export CFLAGS="$CFLAGS $MYCFLAGS" 34 | ./configure $args $@ 35 | -------------------------------------------------------------------------------- /callbacks.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * callbacks.c for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000 by Greg Kroah-Hartman, 5 | */ 6 | #ifdef HAVE_CONFIG_H 7 | #include 8 | #endif 9 | 10 | #include 11 | #include "usbtree.h" 12 | #include "usbview_logo.xpm" /* logo */ 13 | 14 | 15 | void on_buttonClose_clicked (GtkButton *button, gpointer user_data) 16 | { 17 | gtk_main_quit(); 18 | } 19 | 20 | 21 | gboolean on_window1_delete_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) 22 | { 23 | gtk_main_quit(); 24 | 25 | return FALSE; 26 | } 27 | 28 | 29 | void on_buttonRefresh_clicked (GtkButton *button, gpointer user_data) 30 | { 31 | LoadUSBTree(1); 32 | } 33 | 34 | 35 | void on_buttonAbout_clicked (GtkButton *button, gpointer user_data) 36 | { 37 | GdkPixbuf *logo; 38 | gchar *authors[] = { "Greg Kroah-Hartman ", NULL }; 39 | 40 | logo = gdk_pixbuf_new_from_xpm_data ((const char **)usbview_logo_xpm); 41 | gtk_show_about_dialog (GTK_WINDOW (windowMain), 42 | "logo", logo, 43 | "program-name", "usbview", 44 | "version", VERSION, 45 | "comments", "Display information on USB devices", 46 | "website-label", "http://www.kroah.com/linux-usb/", 47 | "website", "http://www.kroah.com/linux-usb/", 48 | "copyright", "Copyright © 1999-2012, 2021-2022", 49 | "authors", authors, 50 | NULL); 51 | g_object_unref (logo); 52 | } 53 | 54 | 55 | gint on_timer_timeout (gpointer user_data) 56 | { 57 | LoadUSBTree(0); 58 | return 1; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ccan/check_type/check_type.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | /* CC0 (Public domain) - see LICENSE file for details */ 3 | #ifndef CCAN_CHECK_TYPE_H 4 | #define CCAN_CHECK_TYPE_H 5 | #include "../config.h" 6 | 7 | /** 8 | * check_type - issue a warning or build failure if type is not correct. 9 | * @expr: the expression whose type we should check (not evaluated). 10 | * @type: the exact type we expect the expression to be. 11 | * 12 | * This macro is usually used within other macros to try to ensure that a macro 13 | * argument is of the expected type. No type promotion of the expression is 14 | * done: an unsigned int is not the same as an int! 15 | * 16 | * check_type() always evaluates to 0. 17 | * 18 | * If your compiler does not support typeof, then the best we can do is fail 19 | * to compile if the sizes of the types are unequal (a less complete check). 20 | * 21 | * Example: 22 | * // They should always pass a 64-bit value to _set_some_value! 23 | * #define set_some_value(expr) \ 24 | * _set_some_value((check_type((expr), uint64_t), (expr))) 25 | */ 26 | 27 | /** 28 | * check_types_match - issue a warning or build failure if types are not same. 29 | * @expr1: the first expression (not evaluated). 30 | * @expr2: the second expression (not evaluated). 31 | * 32 | * This macro is usually used within other macros to try to ensure that 33 | * arguments are of identical types. No type promotion of the expressions is 34 | * done: an unsigned int is not the same as an int! 35 | * 36 | * check_types_match() always evaluates to 0. 37 | * 38 | * If your compiler does not support typeof, then the best we can do is fail 39 | * to compile if the sizes of the types are unequal (a less complete check). 40 | * 41 | * Example: 42 | * // Do subtraction to get to enclosing type, but make sure that 43 | * // pointer is of correct type for that member. 44 | * #define container_of(mbr_ptr, encl_type, mbr) \ 45 | * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ 46 | * ((encl_type *) \ 47 | * ((char *)(mbr_ptr) - offsetof(encl_type, mbr)))) 48 | */ 49 | #if HAVE_TYPEOF 50 | #define check_type(expr, type) \ 51 | ((typeof(expr) *)0 != (type *)0) 52 | 53 | #define check_types_match(expr1, expr2) \ 54 | ((typeof(expr1) *)0 != (typeof(expr2) *)0) 55 | #else 56 | #include 57 | /* Without typeof, we can only test the sizes. */ 58 | #define check_type(expr, type) \ 59 | BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) 60 | 61 | #define check_types_match(expr1, expr2) \ 62 | BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) 63 | #endif /* HAVE_TYPEOF */ 64 | 65 | #endif /* CCAN_CHECK_TYPE_H */ 66 | -------------------------------------------------------------------------------- /ccan/config.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | /* Generated by CCAN configurator */ 3 | #ifndef CCAN_CONFIG_H 4 | #define CCAN_CONFIG_H 5 | #ifndef _GNU_SOURCE 6 | #define _GNU_SOURCE /* Always use GNU extensions. */ 7 | #endif 8 | #define CCAN_COMPILER "cc" 9 | #define CCAN_CFLAGS "-g3 -ggdb -Wall -Wstrict-prototypes -Wold-style-definition -Wundef -Wmissing-prototypes -Wmissing-declarations -Wpointer-arith -Wwrite-strings -DCCAN_STR_DEBUG=1 -I." 10 | #define CCAN_OUTPUT_EXE_CFLAG "-o" 11 | 12 | #define HAVE_CCAN 1 13 | #define HAVE_32BIT_OFF_T 0 14 | #define HAVE_ALIGNOF 1 15 | #define HAVE_ASPRINTF 1 16 | #define HAVE_ATTRIBUTE_COLD 1 17 | #define HAVE_ATTRIBUTE_CONST 1 18 | #define HAVE_ATTRIBUTE_DEPRECATED 0 19 | #define HAVE_ATTRIBUTE_NONNULL 1 20 | #define HAVE_ATTRIBUTE_SENTINEL 1 21 | #define HAVE_ATTRIBUTE_PURE 1 22 | #define HAVE_ATTRIBUTE_MAY_ALIAS 1 23 | #define HAVE_ATTRIBUTE_NORETURN 1 24 | #define HAVE_ATTRIBUTE_PRINTF 1 25 | #define HAVE_ATTRIBUTE_UNUSED 1 26 | #define HAVE_ATTRIBUTE_USED 1 27 | #define HAVE_BACKTRACE 1 28 | #define HAVE_BIG_ENDIAN 0 29 | #define HAVE_BSWAP_64 1 30 | #define HAVE_BUILTIN_CHOOSE_EXPR 1 31 | #define HAVE_BUILTIN_CLZ 1 32 | #define HAVE_BUILTIN_CLZL 1 33 | #define HAVE_BUILTIN_CLZLL 1 34 | #define HAVE_BUILTIN_CTZ 1 35 | #define HAVE_BUILTIN_CTZL 1 36 | #define HAVE_BUILTIN_CTZLL 1 37 | #define HAVE_BUILTIN_CONSTANT_P 1 38 | #define HAVE_BUILTIN_EXPECT 1 39 | #define HAVE_BUILTIN_FFS 1 40 | #define HAVE_BUILTIN_FFSL 1 41 | #define HAVE_BUILTIN_FFSLL 1 42 | #define HAVE_BUILTIN_POPCOUNT 1 43 | #define HAVE_BUILTIN_POPCOUNTL 1 44 | #define HAVE_BUILTIN_POPCOUNTLL 1 45 | #define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 46 | #define HAVE_ICCARM_INTRINSICS 0 47 | #define HAVE_BYTESWAP_H 1 48 | #define HAVE_CLOCK_GETTIME 1 49 | #define HAVE_CLOCK_GETTIME_IN_LIBRT 0 50 | #define HAVE_COMPOUND_LITERALS 1 51 | #define HAVE_FCHDIR 1 52 | #define HAVE_ERR_H 1 53 | #define HAVE_FILE_OFFSET_BITS 0 54 | #define HAVE_FOR_LOOP_DECLARATION 1 55 | #define HAVE_FLEXIBLE_ARRAY_MEMBER 1 56 | #define HAVE_GETPAGESIZE 1 57 | #define HAVE_ISBLANK 1 58 | #define HAVE_LITTLE_ENDIAN 1 59 | #define HAVE_MEMMEM 1 60 | #define HAVE_MEMRCHR 1 61 | #define HAVE_MMAP 1 62 | #define HAVE_PROC_SELF_MAPS 1 63 | #define HAVE_QSORT_R_PRIVATE_LAST 1 64 | #define HAVE_STRUCT_TIMESPEC 1 65 | #define HAVE_SECTION_START_STOP 1 66 | #define HAVE_STACK_GROWS_UPWARDS 0 67 | #define HAVE_STATEMENT_EXPR 1 68 | #define HAVE_SYS_FILIO_H 0 69 | #define HAVE_SYS_TERMIOS_H 1 70 | #define HAVE_SYS_UNISTD_H 1 71 | #define HAVE_TYPEOF 1 72 | #define HAVE_UNALIGNED_ACCESS 1 73 | #define HAVE_UTIME 1 74 | #define HAVE_WARN_UNUSED_RESULT 1 75 | #define HAVE_OPENMP 1 76 | #define HAVE_VALGRIND_MEMCHECK_H 0 77 | #define HAVE_UCONTEXT 1 78 | #define HAVE_POINTER_SAFE_MAKECONTEXT 0 79 | #endif /* CCAN_CONFIG_H */ 80 | -------------------------------------------------------------------------------- /ccan/container_of/container_of.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | /* CC0 (Public domain) - see LICENSE file for details */ 3 | #ifndef CCAN_CONTAINER_OF_H 4 | #define CCAN_CONTAINER_OF_H 5 | #include 6 | 7 | #include "../config.h" 8 | //#include 9 | #include "../check_type/check_type.h" 10 | 11 | /** 12 | * container_of - get pointer to enclosing structure 13 | * @member_ptr: pointer to the structure member 14 | * @containing_type: the type this member is within 15 | * @member: the name of this member within the structure. 16 | * 17 | * Given a pointer to a member of a structure, this macro does pointer 18 | * subtraction to return the pointer to the enclosing type. 19 | * 20 | * Example: 21 | * struct foo { 22 | * int fielda, fieldb; 23 | * // ... 24 | * }; 25 | * struct info { 26 | * int some_other_field; 27 | * struct foo my_foo; 28 | * }; 29 | * 30 | * static struct info *foo_to_info(struct foo *foo) 31 | * { 32 | * return container_of(foo, struct info, my_foo); 33 | * } 34 | */ 35 | #define container_of(member_ptr, containing_type, member) \ 36 | ((containing_type *) \ 37 | ((char *)(member_ptr) \ 38 | - container_off(containing_type, member)) \ 39 | + check_types_match(*(member_ptr), ((containing_type *)0)->member)) 40 | 41 | 42 | /** 43 | * container_of_or_null - get pointer to enclosing structure, or NULL 44 | * @member_ptr: pointer to the structure member 45 | * @containing_type: the type this member is within 46 | * @member: the name of this member within the structure. 47 | * 48 | * Given a pointer to a member of a structure, this macro does pointer 49 | * subtraction to return the pointer to the enclosing type, unless it 50 | * is given NULL, in which case it also returns NULL. 51 | * 52 | * Example: 53 | * struct foo { 54 | * int fielda, fieldb; 55 | * // ... 56 | * }; 57 | * struct info { 58 | * int some_other_field; 59 | * struct foo my_foo; 60 | * }; 61 | * 62 | * static struct info *foo_to_info_allowing_null(struct foo *foo) 63 | * { 64 | * return container_of_or_null(foo, struct info, my_foo); 65 | * } 66 | */ 67 | static inline char *container_of_or_null_(void *member_ptr, size_t offset) 68 | { 69 | return member_ptr ? (char *)member_ptr - offset : NULL; 70 | } 71 | #define container_of_or_null(member_ptr, containing_type, member) \ 72 | ((containing_type *) \ 73 | container_of_or_null_(member_ptr, \ 74 | container_off(containing_type, member)) \ 75 | + check_types_match(*(member_ptr), ((containing_type *)0)->member)) 76 | 77 | /** 78 | * container_off - get offset to enclosing structure 79 | * @containing_type: the type this member is within 80 | * @member: the name of this member within the structure. 81 | * 82 | * Given a pointer to a member of a structure, this macro does 83 | * typechecking and figures out the offset to the enclosing type. 84 | * 85 | * Example: 86 | * struct foo { 87 | * int fielda, fieldb; 88 | * // ... 89 | * }; 90 | * struct info { 91 | * int some_other_field; 92 | * struct foo my_foo; 93 | * }; 94 | * 95 | * static struct info *foo_to_info(struct foo *foo) 96 | * { 97 | * size_t off = container_off(struct info, my_foo); 98 | * return (void *)((char *)foo - off); 99 | * } 100 | */ 101 | #define container_off(containing_type, member) \ 102 | offsetof(containing_type, member) 103 | 104 | /** 105 | * container_of_var - get pointer to enclosing structure using a variable 106 | * @member_ptr: pointer to the structure member 107 | * @container_var: a pointer of same type as this member's container 108 | * @member: the name of this member within the structure. 109 | * 110 | * Given a pointer to a member of a structure, this macro does pointer 111 | * subtraction to return the pointer to the enclosing type. 112 | * 113 | * Example: 114 | * static struct info *foo_to_i(struct foo *foo) 115 | * { 116 | * struct info *i = container_of_var(foo, i, my_foo); 117 | * return i; 118 | * } 119 | */ 120 | #if HAVE_TYPEOF 121 | #define container_of_var(member_ptr, container_var, member) \ 122 | container_of(member_ptr, typeof(*container_var), member) 123 | #else 124 | #define container_of_var(member_ptr, container_var, member) \ 125 | ((void *)((char *)(member_ptr) - \ 126 | container_off_var(container_var, member))) 127 | #endif 128 | 129 | /** 130 | * container_off_var - get offset of a field in enclosing structure 131 | * @container_var: a pointer to a container structure 132 | * @member: the name of a member within the structure. 133 | * 134 | * Given (any) pointer to a structure and a its member name, this 135 | * macro does pointer subtraction to return offset of member in a 136 | * structure memory layout. 137 | * 138 | */ 139 | #if HAVE_TYPEOF 140 | #define container_off_var(var, member) \ 141 | container_off(typeof(*var), member) 142 | #else 143 | #define container_off_var(var, member) \ 144 | ((const char *)&(var)->member - (const char *)(var)) 145 | #endif 146 | 147 | #endif /* CCAN_CONTAINER_OF_H */ 148 | -------------------------------------------------------------------------------- /ccan/list/list.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* Licensed under BSD-MIT - see LICENSE file for details */ 3 | #ifndef CCAN_LIST_H 4 | #define CCAN_LIST_H 5 | //#define CCAN_LIST_DEBUG 1 6 | #include 7 | #include 8 | //#include 9 | //#include 10 | //#include 11 | #include "../str/str.h" 12 | #include "../container_of/container_of.h" 13 | #include "../check_type/check_type.h" 14 | 15 | /** 16 | * struct list_node - an entry in a doubly-linked list 17 | * @next: next entry (self if empty) 18 | * @prev: previous entry (self if empty) 19 | * 20 | * This is used as an entry in a linked list. 21 | * Example: 22 | * struct child { 23 | * const char *name; 24 | * // Linked list of all us children. 25 | * struct list_node list; 26 | * }; 27 | */ 28 | struct list_node 29 | { 30 | struct list_node *next, *prev; 31 | }; 32 | 33 | /** 34 | * struct list_head - the head of a doubly-linked list 35 | * @h: the list_head (containing next and prev pointers) 36 | * 37 | * This is used as the head of a linked list. 38 | * Example: 39 | * struct parent { 40 | * const char *name; 41 | * struct list_head children; 42 | * unsigned int num_children; 43 | * }; 44 | */ 45 | struct list_head 46 | { 47 | struct list_node n; 48 | }; 49 | 50 | /** 51 | * list_check - check head of a list for consistency 52 | * @h: the list_head 53 | * @abortstr: the location to print on aborting, or NULL. 54 | * 55 | * Because list_nodes have redundant information, consistency checking between 56 | * the back and forward links can be done. This is useful as a debugging check. 57 | * If @abortstr is non-NULL, that will be printed in a diagnostic if the list 58 | * is inconsistent, and the function will abort. 59 | * 60 | * Returns the list head if the list is consistent, NULL if not (it 61 | * can never return NULL if @abortstr is set). 62 | * 63 | * See also: list_check_node() 64 | * 65 | * Example: 66 | * static void dump_parent(struct parent *p) 67 | * { 68 | * struct child *c; 69 | * 70 | * printf("%s (%u children):\n", p->name, p->num_children); 71 | * list_check(&p->children, "bad child list"); 72 | * list_for_each(&p->children, c, list) 73 | * printf(" -> %s\n", c->name); 74 | * } 75 | */ 76 | struct list_head *list_check(const struct list_head *h, const char *abortstr); 77 | 78 | /** 79 | * list_check_node - check node of a list for consistency 80 | * @n: the list_node 81 | * @abortstr: the location to print on aborting, or NULL. 82 | * 83 | * Check consistency of the list node is in (it must be in one). 84 | * 85 | * See also: list_check() 86 | * 87 | * Example: 88 | * static void dump_child(const struct child *c) 89 | * { 90 | * list_check_node(&c->list, "bad child list"); 91 | * printf("%s\n", c->name); 92 | * } 93 | */ 94 | struct list_node *list_check_node(const struct list_node *n, 95 | const char *abortstr); 96 | 97 | #define LIST_LOC __FILE__ ":" stringify(__LINE__) 98 | #ifdef CCAN_LIST_DEBUG 99 | #define list_debug(h, loc) list_check((h), loc) 100 | #define list_debug_node(n, loc) list_check_node((n), loc) 101 | #else 102 | #define list_debug(h, loc) ((void)loc, h) 103 | #define list_debug_node(n, loc) ((void)loc, n) 104 | #endif 105 | 106 | /** 107 | * LIST_HEAD_INIT - initializer for an empty list_head 108 | * @name: the name of the list. 109 | * 110 | * Explicit initializer for an empty list. 111 | * 112 | * See also: 113 | * LIST_HEAD, list_head_init() 114 | * 115 | * Example: 116 | * static struct list_head my_list = LIST_HEAD_INIT(my_list); 117 | */ 118 | #define LIST_HEAD_INIT(name) { { &(name).n, &(name).n } } 119 | 120 | /** 121 | * LIST_HEAD - define and initialize an empty list_head 122 | * @name: the name of the list. 123 | * 124 | * The LIST_HEAD macro defines a list_head and initializes it to an empty 125 | * list. It can be prepended by "static" to define a static list_head. 126 | * 127 | * See also: 128 | * LIST_HEAD_INIT, list_head_init() 129 | * 130 | * Example: 131 | * static LIST_HEAD(my_global_list); 132 | */ 133 | #define LIST_HEAD(name) \ 134 | struct list_head name = LIST_HEAD_INIT(name) 135 | 136 | /** 137 | * list_head_init - initialize a list_head 138 | * @h: the list_head to set to the empty list 139 | * 140 | * Example: 141 | * ... 142 | * struct parent *parent = malloc(sizeof(*parent)); 143 | * 144 | * list_head_init(&parent->children); 145 | * parent->num_children = 0; 146 | */ 147 | static inline void list_head_init(struct list_head *h) 148 | { 149 | h->n.next = h->n.prev = &h->n; 150 | } 151 | 152 | /** 153 | * list_node_init - initialize a list_node 154 | * @n: the list_node to link to itself. 155 | * 156 | * You don't need to use this normally! But it lets you list_del(@n) 157 | * safely. 158 | */ 159 | static inline void list_node_init(struct list_node *n) 160 | { 161 | n->next = n->prev = n; 162 | } 163 | 164 | /** 165 | * list_add_after - add an entry after an existing node in a linked list 166 | * @h: the list_head to add the node to (for debugging) 167 | * @p: the existing list_node to add the node after 168 | * @n: the new list_node to add to the list. 169 | * 170 | * The existing list_node must already be a member of the list. 171 | * The new list_node does not need to be initialized; it will be overwritten. 172 | * 173 | * Example: 174 | * struct child c1, c2, c3; 175 | * LIST_HEAD(h); 176 | * 177 | * list_add_tail(&h, &c1.list); 178 | * list_add_tail(&h, &c3.list); 179 | * list_add_after(&h, &c1.list, &c2.list); 180 | */ 181 | #define list_add_after(h, p, n) list_add_after_(h, p, n, LIST_LOC) 182 | static inline void list_add_after_(struct list_head *h, 183 | struct list_node *p, 184 | struct list_node *n, 185 | const char *abortstr) 186 | { 187 | n->next = p->next; 188 | n->prev = p; 189 | p->next->prev = n; 190 | p->next = n; 191 | (void)list_debug(h, abortstr); 192 | } 193 | 194 | /** 195 | * list_add - add an entry at the start of a linked list. 196 | * @h: the list_head to add the node to 197 | * @n: the list_node to add to the list. 198 | * 199 | * The list_node does not need to be initialized; it will be overwritten. 200 | * Example: 201 | * struct child *child = malloc(sizeof(*child)); 202 | * 203 | * child->name = "marvin"; 204 | * list_add(&parent->children, &child->list); 205 | * parent->num_children++; 206 | */ 207 | #define list_add(h, n) list_add_(h, n, LIST_LOC) 208 | static inline void list_add_(struct list_head *h, 209 | struct list_node *n, 210 | const char *abortstr) 211 | { 212 | list_add_after_(h, &h->n, n, abortstr); 213 | } 214 | 215 | /** 216 | * list_add_before - add an entry before an existing node in a linked list 217 | * @h: the list_head to add the node to (for debugging) 218 | * @p: the existing list_node to add the node before 219 | * @n: the new list_node to add to the list. 220 | * 221 | * The existing list_node must already be a member of the list. 222 | * The new list_node does not need to be initialized; it will be overwritten. 223 | * 224 | * Example: 225 | * list_head_init(&h); 226 | * list_add_tail(&h, &c1.list); 227 | * list_add_tail(&h, &c3.list); 228 | * list_add_before(&h, &c3.list, &c2.list); 229 | */ 230 | #define list_add_before(h, p, n) list_add_before_(h, p, n, LIST_LOC) 231 | static inline void list_add_before_(struct list_head *h, 232 | struct list_node *p, 233 | struct list_node *n, 234 | const char *abortstr) 235 | { 236 | n->next = p; 237 | n->prev = p->prev; 238 | p->prev->next = n; 239 | p->prev = n; 240 | (void)list_debug(h, abortstr); 241 | } 242 | 243 | /** 244 | * list_add_tail - add an entry at the end of a linked list. 245 | * @h: the list_head to add the node to 246 | * @n: the list_node to add to the list. 247 | * 248 | * The list_node does not need to be initialized; it will be overwritten. 249 | * Example: 250 | * list_add_tail(&parent->children, &child->list); 251 | * parent->num_children++; 252 | */ 253 | #define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC) 254 | static inline void list_add_tail_(struct list_head *h, 255 | struct list_node *n, 256 | const char *abortstr) 257 | { 258 | list_add_before_(h, &h->n, n, abortstr); 259 | } 260 | 261 | /** 262 | * list_empty - is a list empty? 263 | * @h: the list_head 264 | * 265 | * If the list is empty, returns true. 266 | * 267 | * Example: 268 | * assert(list_empty(&parent->children) == (parent->num_children == 0)); 269 | */ 270 | #define list_empty(h) list_empty_(h, LIST_LOC) 271 | static inline bool list_empty_(const struct list_head *h, const char* abortstr) 272 | { 273 | (void)list_debug(h, abortstr); 274 | return h->n.next == &h->n; 275 | } 276 | 277 | /** 278 | * list_empty_nodebug - is a list empty (and don't perform debug checks)? 279 | * @h: the list_head 280 | * 281 | * If the list is empty, returns true. 282 | * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it 283 | * will NOT perform debug checks. Only use this function if you REALLY 284 | * know what you're doing. 285 | * 286 | * Example: 287 | * assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0)); 288 | */ 289 | #ifndef CCAN_LIST_DEBUG 290 | #define list_empty_nodebug(h) list_empty(h) 291 | #else 292 | static inline bool list_empty_nodebug(const struct list_head *h) 293 | { 294 | return h->n.next == &h->n; 295 | } 296 | #endif 297 | 298 | /** 299 | * list_empty_nocheck - is a list empty? 300 | * @h: the list_head 301 | * 302 | * If the list is empty, returns true. This doesn't perform any 303 | * debug check for list consistency, so it can be called without 304 | * locks, racing with the list being modified. This is ok for 305 | * checks where an incorrect result is not an issue (optimized 306 | * bail out path for example). 307 | */ 308 | static inline bool list_empty_nocheck(const struct list_head *h) 309 | { 310 | return h->n.next == &h->n; 311 | } 312 | 313 | /** 314 | * list_del - delete an entry from an (unknown) linked list. 315 | * @n: the list_node to delete from the list. 316 | * 317 | * Note that this leaves @n in an undefined state; it can be added to 318 | * another list, but not deleted again. 319 | * 320 | * See also: 321 | * list_del_from(), list_del_init() 322 | * 323 | * Example: 324 | * list_del(&child->list); 325 | * parent->num_children--; 326 | */ 327 | #define list_del(n) list_del_(n, LIST_LOC) 328 | static inline void list_del_(struct list_node *n, const char* abortstr) 329 | { 330 | (void)list_debug_node(n, abortstr); 331 | n->next->prev = n->prev; 332 | n->prev->next = n->next; 333 | #ifdef CCAN_LIST_DEBUG 334 | /* Catch use-after-del. */ 335 | n->next = n->prev = NULL; 336 | #endif 337 | } 338 | 339 | /** 340 | * list_del_init - delete a node, and reset it so it can be deleted again. 341 | * @n: the list_node to be deleted. 342 | * 343 | * list_del(@n) or list_del_init() again after this will be safe, 344 | * which can be useful in some cases. 345 | * 346 | * See also: 347 | * list_del_from(), list_del() 348 | * 349 | * Example: 350 | * list_del_init(&child->list); 351 | * parent->num_children--; 352 | */ 353 | #define list_del_init(n) list_del_init_(n, LIST_LOC) 354 | static inline void list_del_init_(struct list_node *n, const char *abortstr) 355 | { 356 | list_del_(n, abortstr); 357 | list_node_init(n); 358 | } 359 | 360 | /** 361 | * list_del_from - delete an entry from a known linked list. 362 | * @h: the list_head the node is in. 363 | * @n: the list_node to delete from the list. 364 | * 365 | * This explicitly indicates which list a node is expected to be in, 366 | * which is better documentation and can catch more bugs. 367 | * 368 | * See also: list_del() 369 | * 370 | * Example: 371 | * list_del_from(&parent->children, &child->list); 372 | * parent->num_children--; 373 | */ 374 | static inline void list_del_from(struct list_head *h, struct list_node *n) 375 | { 376 | #ifdef CCAN_LIST_DEBUG 377 | { 378 | /* Thorough check: make sure it was in list! */ 379 | struct list_node *i; 380 | for (i = h->n.next; i != n; i = i->next) 381 | assert(i != &h->n); 382 | } 383 | #endif /* CCAN_LIST_DEBUG */ 384 | 385 | /* Quick test that catches a surprising number of bugs. */ 386 | assert(!list_empty(h)); 387 | list_del(n); 388 | } 389 | 390 | /** 391 | * list_swap - swap out an entry from an (unknown) linked list for a new one. 392 | * @o: the list_node to replace from the list. 393 | * @n: the list_node to insert in place of the old one. 394 | * 395 | * Note that this leaves @o in an undefined state; it can be added to 396 | * another list, but not deleted/swapped again. 397 | * 398 | * See also: 399 | * list_del() 400 | * 401 | * Example: 402 | * struct child x1, x2; 403 | * LIST_HEAD(xh); 404 | * 405 | * list_add(&xh, &x1.list); 406 | * list_swap(&x1.list, &x2.list); 407 | */ 408 | #define list_swap(o, n) list_swap_(o, n, LIST_LOC) 409 | static inline void list_swap_(struct list_node *o, 410 | struct list_node *n, 411 | const char* abortstr) 412 | { 413 | (void)list_debug_node(o, abortstr); 414 | *n = *o; 415 | n->next->prev = n; 416 | n->prev->next = n; 417 | #ifdef CCAN_LIST_DEBUG 418 | /* Catch use-after-del. */ 419 | o->next = o->prev = NULL; 420 | #endif 421 | } 422 | 423 | /** 424 | * list_entry - convert a list_node back into the structure containing it. 425 | * @n: the list_node 426 | * @type: the type of the entry 427 | * @member: the list_node member of the type 428 | * 429 | * Example: 430 | * // First list entry is children.next; convert back to child. 431 | * child = list_entry(parent->children.n.next, struct child, list); 432 | * 433 | * See Also: 434 | * list_top(), list_for_each() 435 | */ 436 | #define list_entry(n, type, member) container_of(n, type, member) 437 | 438 | /** 439 | * list_top - get the first entry in a list 440 | * @h: the list_head 441 | * @type: the type of the entry 442 | * @member: the list_node member of the type 443 | * 444 | * If the list is empty, returns NULL. 445 | * 446 | * Example: 447 | * struct child *first; 448 | * first = list_top(&parent->children, struct child, list); 449 | * if (!first) 450 | * printf("Empty list!\n"); 451 | */ 452 | #define list_top(h, type, member) \ 453 | ((type *)list_top_((h), list_off_(type, member))) 454 | 455 | static inline const void *list_top_(const struct list_head *h, size_t off) 456 | { 457 | if (list_empty(h)) 458 | return NULL; 459 | return (const char *)h->n.next - off; 460 | } 461 | 462 | /** 463 | * list_pop - remove the first entry in a list 464 | * @h: the list_head 465 | * @type: the type of the entry 466 | * @member: the list_node member of the type 467 | * 468 | * If the list is empty, returns NULL. 469 | * 470 | * Example: 471 | * struct child *one; 472 | * one = list_pop(&parent->children, struct child, list); 473 | * if (!one) 474 | * printf("Empty list!\n"); 475 | */ 476 | #define list_pop(h, type, member) \ 477 | ((type *)list_pop_((h), list_off_(type, member))) 478 | 479 | static inline const void *list_pop_(const struct list_head *h, size_t off) 480 | { 481 | struct list_node *n; 482 | 483 | if (list_empty(h)) 484 | return NULL; 485 | n = h->n.next; 486 | list_del(n); 487 | return (const char *)n - off; 488 | } 489 | 490 | /** 491 | * list_tail - get the last entry in a list 492 | * @h: the list_head 493 | * @type: the type of the entry 494 | * @member: the list_node member of the type 495 | * 496 | * If the list is empty, returns NULL. 497 | * 498 | * Example: 499 | * struct child *last; 500 | * last = list_tail(&parent->children, struct child, list); 501 | * if (!last) 502 | * printf("Empty list!\n"); 503 | */ 504 | #define list_tail(h, type, member) \ 505 | ((type *)list_tail_((h), list_off_(type, member))) 506 | 507 | static inline const void *list_tail_(const struct list_head *h, size_t off) 508 | { 509 | if (list_empty(h)) 510 | return NULL; 511 | return (const char *)h->n.prev - off; 512 | } 513 | 514 | /** 515 | * list_for_each - iterate through a list. 516 | * @h: the list_head (warning: evaluated multiple times!) 517 | * @i: the structure containing the list_node 518 | * @member: the list_node member of the structure 519 | * 520 | * This is a convenient wrapper to iterate @i over the entire list. It's 521 | * a for loop, so you can break and continue as normal. 522 | * 523 | * Example: 524 | * list_for_each(&parent->children, child, list) 525 | * printf("Name: %s\n", child->name); 526 | */ 527 | #define list_for_each(h, i, member) \ 528 | list_for_each_off(h, i, list_off_var_(i, member)) 529 | 530 | /** 531 | * list_for_each_rev - iterate through a list backwards. 532 | * @h: the list_head 533 | * @i: the structure containing the list_node 534 | * @member: the list_node member of the structure 535 | * 536 | * This is a convenient wrapper to iterate @i over the entire list. It's 537 | * a for loop, so you can break and continue as normal. 538 | * 539 | * Example: 540 | * list_for_each_rev(&parent->children, child, list) 541 | * printf("Name: %s\n", child->name); 542 | */ 543 | #define list_for_each_rev(h, i, member) \ 544 | list_for_each_rev_off(h, i, list_off_var_(i, member)) 545 | 546 | /** 547 | * list_for_each_rev_safe - iterate through a list backwards, 548 | * maybe during deletion 549 | * @h: the list_head 550 | * @i: the structure containing the list_node 551 | * @nxt: the structure containing the list_node 552 | * @member: the list_node member of the structure 553 | * 554 | * This is a convenient wrapper to iterate @i over the entire list backwards. 555 | * It's a for loop, so you can break and continue as normal. The extra 556 | * variable * @nxt is used to hold the next element, so you can delete @i 557 | * from the list. 558 | * 559 | * Example: 560 | * struct child *next; 561 | * list_for_each_rev_safe(&parent->children, child, next, list) { 562 | * printf("Name: %s\n", child->name); 563 | * } 564 | */ 565 | #define list_for_each_rev_safe(h, i, nxt, member) \ 566 | list_for_each_rev_safe_off(h, i, nxt, list_off_var_(i, member)) 567 | 568 | /** 569 | * list_for_each_safe - iterate through a list, maybe during deletion 570 | * @h: the list_head 571 | * @i: the structure containing the list_node 572 | * @nxt: the structure containing the list_node 573 | * @member: the list_node member of the structure 574 | * 575 | * This is a convenient wrapper to iterate @i over the entire list. It's 576 | * a for loop, so you can break and continue as normal. The extra variable 577 | * @nxt is used to hold the next element, so you can delete @i from the list. 578 | * 579 | * Example: 580 | * list_for_each_safe(&parent->children, child, next, list) { 581 | * list_del(&child->list); 582 | * parent->num_children--; 583 | * } 584 | */ 585 | #define list_for_each_safe(h, i, nxt, member) \ 586 | list_for_each_safe_off(h, i, nxt, list_off_var_(i, member)) 587 | 588 | /** 589 | * list_next - get the next entry in a list 590 | * @h: the list_head 591 | * @i: a pointer to an entry in the list. 592 | * @member: the list_node member of the structure 593 | * 594 | * If @i was the last entry in the list, returns NULL. 595 | * 596 | * Example: 597 | * struct child *second; 598 | * second = list_next(&parent->children, first, list); 599 | * if (!second) 600 | * printf("No second child!\n"); 601 | */ 602 | #define list_next(h, i, member) \ 603 | ((list_typeof(i))list_entry_or_null(list_debug(h, \ 604 | __FILE__ ":" stringify(__LINE__)), \ 605 | (i)->member.next, \ 606 | list_off_var_((i), member))) 607 | 608 | /** 609 | * list_prev - get the previous entry in a list 610 | * @h: the list_head 611 | * @i: a pointer to an entry in the list. 612 | * @member: the list_node member of the structure 613 | * 614 | * If @i was the first entry in the list, returns NULL. 615 | * 616 | * Example: 617 | * first = list_prev(&parent->children, second, list); 618 | * if (!first) 619 | * printf("Can't go back to first child?!\n"); 620 | */ 621 | #define list_prev(h, i, member) \ 622 | ((list_typeof(i))list_entry_or_null(list_debug(h, \ 623 | __FILE__ ":" stringify(__LINE__)), \ 624 | (i)->member.prev, \ 625 | list_off_var_((i), member))) 626 | 627 | /** 628 | * list_append_list - empty one list onto the end of another. 629 | * @to: the list to append into 630 | * @from: the list to empty. 631 | * 632 | * This takes the entire contents of @from and moves it to the end of 633 | * @to. After this @from will be empty. 634 | * 635 | * Example: 636 | * struct list_head adopter; 637 | * 638 | * list_append_list(&adopter, &parent->children); 639 | * assert(list_empty(&parent->children)); 640 | * parent->num_children = 0; 641 | */ 642 | #define list_append_list(t, f) list_append_list_(t, f, \ 643 | __FILE__ ":" stringify(__LINE__)) 644 | static inline void list_append_list_(struct list_head *to, 645 | struct list_head *from, 646 | const char *abortstr) 647 | { 648 | struct list_node *from_tail = list_debug(from, abortstr)->n.prev; 649 | struct list_node *to_tail = list_debug(to, abortstr)->n.prev; 650 | 651 | /* Sew in head and entire list. */ 652 | to->n.prev = from_tail; 653 | from_tail->next = &to->n; 654 | to_tail->next = &from->n; 655 | from->n.prev = to_tail; 656 | 657 | /* Now remove head. */ 658 | list_del(&from->n); 659 | list_head_init(from); 660 | } 661 | 662 | /** 663 | * list_prepend_list - empty one list into the start of another. 664 | * @to: the list to prepend into 665 | * @from: the list to empty. 666 | * 667 | * This takes the entire contents of @from and moves it to the start 668 | * of @to. After this @from will be empty. 669 | * 670 | * Example: 671 | * list_prepend_list(&adopter, &parent->children); 672 | * assert(list_empty(&parent->children)); 673 | * parent->num_children = 0; 674 | */ 675 | #define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC) 676 | static inline void list_prepend_list_(struct list_head *to, 677 | struct list_head *from, 678 | const char *abortstr) 679 | { 680 | struct list_node *from_tail = list_debug(from, abortstr)->n.prev; 681 | struct list_node *to_head = list_debug(to, abortstr)->n.next; 682 | 683 | /* Sew in head and entire list. */ 684 | to->n.next = &from->n; 685 | from->n.prev = &to->n; 686 | to_head->prev = from_tail; 687 | from_tail->next = to_head; 688 | 689 | /* Now remove head. */ 690 | list_del(&from->n); 691 | list_head_init(from); 692 | } 693 | 694 | /* internal macros, do not use directly */ 695 | #define list_for_each_off_dir_(h, i, off, dir) \ 696 | for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \ 697 | (off)); \ 698 | list_node_from_off_((void *)i, (off)) != &(h)->n; \ 699 | i = list_node_to_off_(list_node_from_off_((void *)i, (off))->dir, \ 700 | (off))) 701 | 702 | #define list_for_each_safe_off_dir_(h, i, nxt, off, dir) \ 703 | for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \ 704 | (off)), \ 705 | nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \ 706 | (off)); \ 707 | list_node_from_off_(i, (off)) != &(h)->n; \ 708 | i = nxt, \ 709 | nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \ 710 | (off))) 711 | 712 | /** 713 | * list_for_each_off - iterate through a list of memory regions. 714 | * @h: the list_head 715 | * @i: the pointer to a memory region which contains list node data. 716 | * @off: offset(relative to @i) at which list node data resides. 717 | * 718 | * This is a low-level wrapper to iterate @i over the entire list, used to 719 | * implement all oher, more high-level, for-each constructs. It's a for loop, 720 | * so you can break and continue as normal. 721 | * 722 | * WARNING! Being the low-level macro that it is, this wrapper doesn't know 723 | * nor care about the type of @i. The only assumption made is that @i points 724 | * to a chunk of memory that at some @offset, relative to @i, contains a 725 | * properly filled `struct list_node' which in turn contains pointers to 726 | * memory chunks and it's turtles all the way down. With all that in mind 727 | * remember that given the wrong pointer/offset couple this macro will 728 | * happily churn all you memory until SEGFAULT stops it, in other words 729 | * caveat emptor. 730 | * 731 | * It is worth mentioning that one of legitimate use-cases for that wrapper 732 | * is operation on opaque types with known offset for `struct list_node' 733 | * member(preferably 0), because it allows you not to disclose the type of 734 | * @i. 735 | * 736 | * Example: 737 | * list_for_each_off(&parent->children, child, 738 | * offsetof(struct child, list)) 739 | * printf("Name: %s\n", child->name); 740 | */ 741 | #define list_for_each_off(h, i, off) \ 742 | list_for_each_off_dir_((h),(i),(off),next) 743 | 744 | /** 745 | * list_for_each_rev_off - iterate through a list of memory regions backwards 746 | * @h: the list_head 747 | * @i: the pointer to a memory region which contains list node data. 748 | * @off: offset(relative to @i) at which list node data resides. 749 | * 750 | * See list_for_each_off for details 751 | */ 752 | #define list_for_each_rev_off(h, i, off) \ 753 | list_for_each_off_dir_((h),(i),(off),prev) 754 | 755 | /** 756 | * list_for_each_safe_off - iterate through a list of memory regions, maybe 757 | * during deletion 758 | * @h: the list_head 759 | * @i: the pointer to a memory region which contains list node data. 760 | * @nxt: the structure containing the list_node 761 | * @off: offset(relative to @i) at which list node data resides. 762 | * 763 | * For details see `list_for_each_off' and `list_for_each_safe' 764 | * descriptions. 765 | * 766 | * Example: 767 | * list_for_each_safe_off(&parent->children, child, 768 | * next, offsetof(struct child, list)) 769 | * printf("Name: %s\n", child->name); 770 | */ 771 | #define list_for_each_safe_off(h, i, nxt, off) \ 772 | list_for_each_safe_off_dir_((h),(i),(nxt),(off),next) 773 | 774 | /** 775 | * list_for_each_rev_safe_off - iterate backwards through a list of 776 | * memory regions, maybe during deletion 777 | * @h: the list_head 778 | * @i: the pointer to a memory region which contains list node data. 779 | * @nxt: the structure containing the list_node 780 | * @off: offset(relative to @i) at which list node data resides. 781 | * 782 | * For details see `list_for_each_rev_off' and `list_for_each_rev_safe' 783 | * descriptions. 784 | * 785 | * Example: 786 | * list_for_each_rev_safe_off(&parent->children, child, 787 | * next, offsetof(struct child, list)) 788 | * printf("Name: %s\n", child->name); 789 | */ 790 | #define list_for_each_rev_safe_off(h, i, nxt, off) \ 791 | list_for_each_safe_off_dir_((h),(i),(nxt),(off),prev) 792 | 793 | /* Other -off variants. */ 794 | #define list_entry_off(n, type, off) \ 795 | ((type *)list_node_from_off_((n), (off))) 796 | 797 | #define list_head_off(h, type, off) \ 798 | ((type *)list_head_off((h), (off))) 799 | 800 | #define list_tail_off(h, type, off) \ 801 | ((type *)list_tail_((h), (off))) 802 | 803 | #define list_add_off(h, n, off) \ 804 | list_add((h), list_node_from_off_((n), (off))) 805 | 806 | #define list_del_off(n, off) \ 807 | list_del(list_node_from_off_((n), (off))) 808 | 809 | #define list_del_from_off(h, n, off) \ 810 | list_del_from(h, list_node_from_off_((n), (off))) 811 | 812 | /* Offset helper functions so we only single-evaluate. */ 813 | static inline void *list_node_to_off_(struct list_node *node, size_t off) 814 | { 815 | return (void *)((char *)node - off); 816 | } 817 | static inline struct list_node *list_node_from_off_(void *ptr, size_t off) 818 | { 819 | return (struct list_node *)((char *)ptr + off); 820 | } 821 | 822 | /* Get the offset of the member, but make sure it's a list_node. */ 823 | #define list_off_(type, member) \ 824 | (container_off(type, member) + \ 825 | check_type(((type *)0)->member, struct list_node)) 826 | 827 | #define list_off_var_(var, member) \ 828 | (container_off_var(var, member) + \ 829 | check_type(var->member, struct list_node)) 830 | 831 | #if HAVE_TYPEOF 832 | #define list_typeof(var) typeof(var) 833 | #else 834 | #define list_typeof(var) void * 835 | #endif 836 | 837 | /* Returns member, or NULL if at end of list. */ 838 | static inline void *list_entry_or_null(const struct list_head *h, 839 | const struct list_node *n, 840 | size_t off) 841 | { 842 | if (n == &h->n) 843 | return NULL; 844 | return (char *)n - off; 845 | } 846 | #endif /* CCAN_LIST_H */ 847 | -------------------------------------------------------------------------------- /ccan/str/str.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | /* CC0 (Public domain) - see LICENSE file for details */ 3 | #ifndef CCAN_STR_H 4 | #define CCAN_STR_H 5 | #include "../config.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /** 12 | * streq - Are two strings equal? 13 | * @a: first string 14 | * @b: first string 15 | * 16 | * This macro is arguably more readable than "!strcmp(a, b)". 17 | * 18 | * Example: 19 | * if (streq(somestring, "")) 20 | * printf("String is empty!\n"); 21 | */ 22 | #define streq(a,b) (strcmp((a),(b)) == 0) 23 | 24 | /** 25 | * strstarts - Does this string start with this prefix? 26 | * @str: string to test 27 | * @prefix: prefix to look for at start of str 28 | * 29 | * Example: 30 | * if (strstarts(somestring, "foo")) 31 | * printf("String %s begins with 'foo'!\n", somestring); 32 | */ 33 | #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) 34 | 35 | /** 36 | * strends - Does this string end with this postfix? 37 | * @str: string to test 38 | * @postfix: postfix to look for at end of str 39 | * 40 | * Example: 41 | * if (strends(somestring, "foo")) 42 | * printf("String %s end with 'foo'!\n", somestring); 43 | */ 44 | static inline bool strends(const char *str, const char *postfix) 45 | { 46 | if (strlen(str) < strlen(postfix)) 47 | return false; 48 | 49 | return streq(str + strlen(str) - strlen(postfix), postfix); 50 | } 51 | 52 | /** 53 | * stringify - Turn expression into a string literal 54 | * @expr: any C expression 55 | * 56 | * Example: 57 | * #define PRINT_COND_IF_FALSE(cond) \ 58 | * ((cond) || printf("%s is false!", stringify(cond))) 59 | */ 60 | #define stringify(expr) stringify_1(expr) 61 | /* Double-indirection required to stringify expansions */ 62 | #define stringify_1(expr) #expr 63 | 64 | /** 65 | * strcount - Count number of (non-overlapping) occurrences of a substring. 66 | * @haystack: a C string 67 | * @needle: a substring 68 | * 69 | * Example: 70 | * assert(strcount("aaa aaa", "a") == 6); 71 | * assert(strcount("aaa aaa", "ab") == 0); 72 | * assert(strcount("aaa aaa", "aa") == 2); 73 | */ 74 | size_t strcount(const char *haystack, const char *needle); 75 | 76 | /** 77 | * STR_MAX_CHARS - Maximum possible size of numeric string for this type. 78 | * @type_or_expr: a pointer or integer type or expression. 79 | * 80 | * This provides enough space for a nul-terminated string which represents the 81 | * largest possible value for the type or expression. 82 | * 83 | * Note: The implementation adds extra space so hex values or negative 84 | * values will fit (eg. sprintf(... "%p"). ) 85 | * 86 | * Example: 87 | * char str[STR_MAX_CHARS(int)]; 88 | * 89 | * sprintf(str, "%i", 7); 90 | */ 91 | #define STR_MAX_CHARS(type_or_expr) \ 92 | ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ 93 | + STR_MAX_CHARS_TCHECK_(type_or_expr)) 94 | 95 | #if HAVE_TYPEOF 96 | /* Only a simple type can have 0 assigned, so test that. */ 97 | #define STR_MAX_CHARS_TCHECK_(type_or_expr) \ 98 | (sizeof(({ typeof(type_or_expr) x = 0; x; }))*0) 99 | #else 100 | #define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 101 | #endif 102 | 103 | /** 104 | * cisalnum - isalnum() which takes a char (and doesn't accept EOF) 105 | * @c: a character 106 | * 107 | * Surprisingly, the standard ctype.h isalnum() takes an int, which 108 | * must have the value of EOF (-1) or an unsigned char. This variant 109 | * takes a real char, and doesn't accept EOF. 110 | */ 111 | static inline bool cisalnum(char c) 112 | { 113 | return isalnum((unsigned char)c); 114 | } 115 | static inline bool cisalpha(char c) 116 | { 117 | return isalpha((unsigned char)c); 118 | } 119 | static inline bool cisascii(char c) 120 | { 121 | return isascii((unsigned char)c); 122 | } 123 | #if HAVE_ISBLANK 124 | static inline bool cisblank(char c) 125 | { 126 | return isblank((unsigned char)c); 127 | } 128 | #endif 129 | static inline bool ciscntrl(char c) 130 | { 131 | return iscntrl((unsigned char)c); 132 | } 133 | static inline bool cisdigit(char c) 134 | { 135 | return isdigit((unsigned char)c); 136 | } 137 | static inline bool cisgraph(char c) 138 | { 139 | return isgraph((unsigned char)c); 140 | } 141 | static inline bool cislower(char c) 142 | { 143 | return islower((unsigned char)c); 144 | } 145 | static inline bool cisprint(char c) 146 | { 147 | return isprint((unsigned char)c); 148 | } 149 | static inline bool cispunct(char c) 150 | { 151 | return ispunct((unsigned char)c); 152 | } 153 | static inline bool cisspace(char c) 154 | { 155 | return isspace((unsigned char)c); 156 | } 157 | static inline bool cisupper(char c) 158 | { 159 | return isupper((unsigned char)c); 160 | } 161 | static inline bool cisxdigit(char c) 162 | { 163 | return isxdigit((unsigned char)c); 164 | } 165 | 166 | #include "../str/str_debug.h" 167 | 168 | /* These checks force things out of line, hence they are under DEBUG. */ 169 | #ifdef CCAN_STR_DEBUG 170 | #include 171 | 172 | /* These are commonly misused: they take -1 or an *unsigned* char value. */ 173 | #undef isalnum 174 | #undef isalpha 175 | #undef isascii 176 | #undef isblank 177 | #undef iscntrl 178 | #undef isdigit 179 | #undef isgraph 180 | #undef islower 181 | #undef isprint 182 | #undef ispunct 183 | #undef isspace 184 | #undef isupper 185 | #undef isxdigit 186 | 187 | /* You can use a char if char is unsigned. */ 188 | #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF 189 | #define str_check_arg_(i) \ 190 | ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \ 191 | char) \ 192 | || (char)255 > 0)) 193 | #else 194 | #define str_check_arg_(i) (i) 195 | #endif 196 | 197 | #define isalnum(i) str_isalnum(str_check_arg_(i)) 198 | #define isalpha(i) str_isalpha(str_check_arg_(i)) 199 | #define isascii(i) str_isascii(str_check_arg_(i)) 200 | #if HAVE_ISBLANK 201 | #define isblank(i) str_isblank(str_check_arg_(i)) 202 | #endif 203 | #define iscntrl(i) str_iscntrl(str_check_arg_(i)) 204 | #define isdigit(i) str_isdigit(str_check_arg_(i)) 205 | #define isgraph(i) str_isgraph(str_check_arg_(i)) 206 | #define islower(i) str_islower(str_check_arg_(i)) 207 | #define isprint(i) str_isprint(str_check_arg_(i)) 208 | #define ispunct(i) str_ispunct(str_check_arg_(i)) 209 | #define isspace(i) str_isspace(str_check_arg_(i)) 210 | #define isupper(i) str_isupper(str_check_arg_(i)) 211 | #define isxdigit(i) str_isxdigit(str_check_arg_(i)) 212 | 213 | #if HAVE_TYPEOF 214 | /* With GNU magic, we can make const-respecting standard string functions. */ 215 | #undef strstr 216 | #undef strchr 217 | #undef strrchr 218 | 219 | /* + 0 is needed to decay array into pointer. */ 220 | #define strstr(haystack, needle) \ 221 | ((typeof((haystack) + 0))str_strstr((haystack), (needle))) 222 | #define strchr(haystack, c) \ 223 | ((typeof((haystack) + 0))str_strchr((haystack), (c))) 224 | #define strrchr(haystack, c) \ 225 | ((typeof((haystack) + 0))str_strrchr((haystack), (c))) 226 | #endif 227 | #endif /* CCAN_STR_DEBUG */ 228 | 229 | #endif /* CCAN_STR_H */ 230 | -------------------------------------------------------------------------------- /ccan/str/str_debug.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | /* CC0 (Public domain) - see LICENSE file for details */ 3 | #ifndef CCAN_STR_DEBUG_H 4 | #define CCAN_STR_DEBUG_H 5 | 6 | /* #define CCAN_STR_DEBUG 1 */ 7 | 8 | #ifdef CCAN_STR_DEBUG 9 | /* Because we mug the real ones with macros, we need our own wrappers. */ 10 | int str_isalnum(int i); 11 | int str_isalpha(int i); 12 | int str_isascii(int i); 13 | #if HAVE_ISBLANK 14 | int str_isblank(int i); 15 | #endif 16 | int str_iscntrl(int i); 17 | int str_isdigit(int i); 18 | int str_isgraph(int i); 19 | int str_islower(int i); 20 | int str_isprint(int i); 21 | int str_ispunct(int i); 22 | int str_isspace(int i); 23 | int str_isupper(int i); 24 | int str_isxdigit(int i); 25 | 26 | char *str_strstr(const char *haystack, const char *needle); 27 | char *str_strchr(const char *s, int c); 28 | char *str_strrchr(const char *s, int c); 29 | #endif /* CCAN_STR_DEBUG */ 30 | 31 | #endif /* CCAN_STR_DEBUG_H */ 32 | -------------------------------------------------------------------------------- /com.kroah.usbview.metainfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.kroah.usbview 4 | 5 | USBView 6 | A USB device tree viewer 7 | 8 | FSFAP 9 | GPL-2.0-only 10 | 11 | 12 |

13 | USBView is a small application to show what the device tree of the USB bus looks like. 14 | It shows a graphical representation of the devices that are currently plugged in, 15 | showing the topology of the USB bus. 16 | It also displays information on each individual device on the bus. 17 |

18 |
19 | 20 | usbview.desktop 21 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (c) 2022 Greg Kroah-Hartman 3 | dnl Process this file with autoconf to produce a configure script. 4 | 5 | # Prologue. 6 | 7 | AC_PREREQ([2.69]) 8 | AC_INIT([USBView],[3.1],[Greg Kroah-Hartman ],[usbview],[http://www.kroah.com/linux-usb/]) 9 | AC_CONFIG_SRCDIR([usbtree.c]) 10 | AC_CONFIG_HEADERS([config.h]) 11 | AM_INIT_AUTOMAKE([foreign subdir-objects -Wall]) 12 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 13 | 14 | # Process options. 15 | 16 | AC_MSG_CHECKING([whether to try to install icons]) 17 | AC_ARG_ENABLE(icons, 18 | [AS_HELP_STRING([--enable-icons],[try to install icons (default=yes)])], 19 | [icons=$enableval], 20 | [icons=yes]) 21 | AC_MSG_RESULT([$icons]) 22 | 23 | AC_MSG_CHECKING([whether to try to install desktop file]) 24 | AC_ARG_ENABLE(desktop, 25 | [AS_HELP_STRING([--enable-desktop],[try to install desktop file (default=yes)])], 26 | [desktop=$enableval], 27 | [desktop=yes]) 28 | AC_MSG_RESULT([$desktop]) 29 | 30 | # Checks for programs. 31 | 32 | AC_PROG_CC 33 | AC_CHECK_PROG([have_convert],[convert],[yes],[no]) 34 | 35 | # Set automake conditionals. 36 | 37 | AC_SUBST(CONVERT,[convert]) 38 | AM_CONDITIONAL(HAVE_CONVERT, [test "$have_convert" = "yes"]) 39 | 40 | AS_IF([test "$have_convert" = "no"], 41 | [AC_MSG_WARN([no bitmap conversion utility, disabling icon installation]) 42 | icons=no]) 43 | 44 | AM_CONDITIONAL(DESKTOP,[test x${desktop} = xyes]) 45 | AM_CONDITIONAL(ICONS,[test x${icons} = xyes]) 46 | 47 | # Checks for libraries. 48 | 49 | AC_SEARCH_LIBS([strerror],[cposix]) 50 | PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= 3.0]) 51 | AC_SUBST([GTK_FLAGS]) 52 | AC_SUBST([GTK_LIBS]) 53 | 54 | # Checks for header files. 55 | 56 | AC_CHECK_HEADERS([ctype.h errno.h stdio.h],, 57 | AC_MSG_ERROR(required system header file unavailable)) 58 | 59 | # Checks for typedefs, structures, and compiler characteristics. 60 | 61 | # Checks for library functions. 62 | 63 | AC_CHECK_FUNCS([memset strstr strtol],, 64 | AC_MSG_ERROR(required library function unavailable)) 65 | 66 | # Epilogue. 67 | 68 | AC_CONFIG_FILES([Makefile]) 69 | AC_OUTPUT 70 | AC_MSG_RESULT([ 71 | usbview $VERSION 72 | =========== 73 | 74 | prefix: ${prefix} 75 | datarootdir: ${datarootdir} 76 | datadir: ${datadir} 77 | mandir: ${mandir} 78 | 79 | compiler: ${CC} 80 | cflags: ${CFLAGS} 81 | ldflags: ${LDFLAGS} 82 | ]) 83 | -------------------------------------------------------------------------------- /interface.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * interface.c for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000, 2009 by Greg Kroah-Hartman, 5 | */ 6 | #ifdef HAVE_CONFIG_H 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "usbtree.h" 16 | #include "hicolor/64x64/apps/usbview_icon.xpm" 17 | 18 | GtkWidget *treeUSB; 19 | GtkTreeStore *treeStore; 20 | GtkTextBuffer *textDescriptionBuffer; 21 | GtkWidget *textDescriptionView; 22 | GtkWidget *windowMain; 23 | static GtkTreeViewColumn *treeColumn; 24 | 25 | int timer; 26 | 27 | GtkWidget* 28 | create_windowMain () 29 | { 30 | GtkWidget *vbox1; 31 | GtkWidget *hpaned1; 32 | GtkWidget *scrolledwindow1; 33 | GtkWidget *hbuttonbox1; 34 | GtkWidget *buttonRefresh; 35 | GtkWidget *buttonClose; 36 | GtkWidget *buttonAbout; 37 | GdkPixbuf *icon; 38 | GtkCellRenderer *treeRenderer; 39 | 40 | windowMain = gtk_window_new (GTK_WINDOW_TOPLEVEL); 41 | gtk_widget_set_name (windowMain, "windowMain"); 42 | gtk_window_set_title (GTK_WINDOW (windowMain), "USB Viewer"); 43 | gtk_window_set_default_size (GTK_WINDOW (windowMain), 600, 300); 44 | 45 | icon = gdk_pixbuf_new_from_xpm_data((const char **)usbview_icon); 46 | gtk_window_set_icon(GTK_WINDOW(windowMain), icon); 47 | 48 | vbox1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); 49 | gtk_widget_set_name (vbox1, "vbox1"); 50 | gtk_widget_show (vbox1); 51 | gtk_container_add (GTK_CONTAINER (windowMain), vbox1); 52 | 53 | hpaned1 = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL); 54 | gtk_widget_set_name (hpaned1, "hpaned1"); 55 | gtk_widget_show (hpaned1); 56 | gtk_box_pack_start (GTK_BOX (vbox1), hpaned1, TRUE, TRUE, 0); 57 | 58 | treeStore = gtk_tree_store_new (N_COLUMNS, 59 | G_TYPE_STRING, /* NAME_COLUMN */ 60 | G_TYPE_INT, /* DEVICE_ADDR_COLUMN */ 61 | G_TYPE_STRING, /* COLOR_COLUMN */ 62 | G_TYPE_STRING /* TOOLTIP_COLUMN */); 63 | treeUSB = gtk_tree_view_new_with_model (GTK_TREE_MODEL (treeStore)); 64 | treeRenderer = gtk_cell_renderer_text_new (); 65 | treeColumn = gtk_tree_view_column_new_with_attributes ( 66 | "USB devices", 67 | treeRenderer, 68 | "text", NAME_COLUMN, 69 | "foreground", COLOR_COLUMN, 70 | NULL); 71 | gtk_tree_view_append_column (GTK_TREE_VIEW (treeUSB), treeColumn); 72 | gtk_tree_view_set_tooltip_column( 73 | GTK_TREE_VIEW (treeUSB), TOOLTIP_COLUMN 74 | ); 75 | 76 | gtk_widget_set_name (treeUSB, "treeUSB"); 77 | gtk_widget_show (treeUSB); 78 | gtk_paned_pack1 (GTK_PANED (hpaned1), treeUSB, FALSE, FALSE); 79 | 80 | scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); 81 | gtk_widget_set_name (scrolledwindow1, "scrolledwindow1"); 82 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); 83 | gtk_widget_show (scrolledwindow1); 84 | gtk_paned_pack2 (GTK_PANED (hpaned1), scrolledwindow1, TRUE, FALSE); 85 | 86 | textDescriptionBuffer = gtk_text_buffer_new(NULL); 87 | //textDescription = gtk_text_new (NULL, NULL); 88 | textDescriptionView = gtk_text_view_new_with_buffer(textDescriptionBuffer); 89 | gtk_widget_set_name (textDescriptionView, "textDescription"); 90 | gtk_text_view_set_editable(GTK_TEXT_VIEW(textDescriptionView), FALSE); 91 | gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textDescriptionView), FALSE); 92 | gtk_widget_show (textDescriptionView); 93 | gtk_container_add (GTK_CONTAINER (scrolledwindow1), textDescriptionView); 94 | 95 | hbuttonbox1 = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); 96 | gtk_widget_set_name (hbuttonbox1, "hbuttonbox1"); 97 | gtk_widget_show (hbuttonbox1); 98 | gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox1, FALSE, FALSE, 5); 99 | //gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox1), 10); 100 | //gtk_button_box_set_child_size (GTK_BUTTON_BOX (hbuttonbox1), 50, 25); 101 | //gtk_button_box_set_child_ipadding (GTK_BUTTON_BOX (hbuttonbox1), 25, 10); 102 | 103 | buttonRefresh = gtk_button_new_with_label("Refresh"); 104 | gtk_widget_set_name (buttonRefresh, "buttonRefresh"); 105 | gtk_widget_show (buttonRefresh); 106 | gtk_container_add (GTK_CONTAINER (hbuttonbox1), buttonRefresh); 107 | gtk_container_set_border_width (GTK_CONTAINER (buttonRefresh), 4); 108 | gtk_widget_set_can_default (buttonRefresh, TRUE); 109 | 110 | buttonAbout = gtk_button_new_with_label("About"); 111 | gtk_widget_set_name (buttonAbout, "buttonAbout"); 112 | gtk_widget_show (buttonAbout); 113 | gtk_container_add (GTK_CONTAINER (hbuttonbox1), buttonAbout); 114 | gtk_container_set_border_width (GTK_CONTAINER (buttonAbout), 4); 115 | gtk_widget_set_can_default (buttonAbout, TRUE); 116 | 117 | buttonClose = gtk_button_new_with_label("Quit"); 118 | gtk_widget_set_name (buttonClose, "buttonClose"); 119 | gtk_widget_show (buttonClose); 120 | gtk_container_add (GTK_CONTAINER (hbuttonbox1), buttonClose); 121 | gtk_container_set_border_width (GTK_CONTAINER (buttonClose), 4); 122 | gtk_widget_set_can_default (buttonClose, TRUE); 123 | 124 | g_signal_connect (G_OBJECT (windowMain), "delete_event", 125 | G_CALLBACK (on_window1_delete_event), 126 | NULL); 127 | g_signal_connect (G_OBJECT (buttonRefresh), "clicked", 128 | G_CALLBACK (on_buttonRefresh_clicked), 129 | NULL); 130 | g_signal_connect (G_OBJECT (buttonAbout), "clicked", 131 | G_CALLBACK (on_buttonAbout_clicked), 132 | NULL); 133 | g_signal_connect (G_OBJECT (buttonClose), "clicked", 134 | G_CALLBACK (on_buttonClose_clicked), 135 | NULL); 136 | 137 | /* create our timer */ 138 | //timer = gtk_timeout_add (2000, on_timer_timeout, 0); 139 | 140 | return windowMain; 141 | } 142 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * main.c for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000 by Greg Kroah-Hartman, 5 | */ 6 | #ifdef HAVE_CONFIG_H 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include "usbtree.h" 15 | 16 | int main (int argc, char *argv[]) 17 | { 18 | GtkWidget *window1; 19 | 20 | gtk_init (&argc, &argv); 21 | 22 | initialize_stuff(); 23 | 24 | /* 25 | * The following code was added by Glade to create one of each component 26 | * (except popup menus), just so that you see something after building 27 | * the project. Delete any components that you don't want shown initially. 28 | */ 29 | window1 = create_windowMain (); 30 | gtk_widget_show (window1); 31 | 32 | LoadUSBTree(0); 33 | gtk_main (); 34 | return 0; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /sysfs.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * sysfs.c for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000, 2022 by Greg Kroah-Hartman, 5 | */ 6 | 7 | #ifdef HAVE_CONFIG_H 8 | #include 9 | #endif 10 | 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "usbtree.h" 26 | #include "sysfs.h" 27 | #include "ccan/list/list.h" 28 | 29 | struct Device *rootDevice = NULL; 30 | static struct DeviceBandwidth *currentBandwidth = NULL; 31 | 32 | 33 | /* taken from all-io.h from util-linux repo */ 34 | static inline ssize_t read_all(int fd, char *buf, size_t count) 35 | { 36 | ssize_t ret; 37 | ssize_t c = 0; 38 | int tries = 0; 39 | 40 | while (count > 0) { 41 | ret = read(fd, buf, count); 42 | if (ret <= 0) { 43 | if (ret < 0 && (errno == EAGAIN || errno == EINTR) && 44 | (tries++ < 5)) { 45 | usleep(250000); 46 | continue; 47 | } 48 | return c ? c : -1; 49 | } 50 | tries = 0; 51 | count -= ret; 52 | buf += ret; 53 | c += ret; 54 | } 55 | return c; 56 | } 57 | 58 | static int openreadclose(const char *filename, char *buffer, size_t bufsize) 59 | { 60 | ssize_t count; 61 | int fd; 62 | 63 | fd = openat(0, filename, O_RDONLY); 64 | if (fd < 0) { 65 | printf("error opening %s\n", filename); 66 | return fd; 67 | } 68 | 69 | count = read_all(fd, buffer, bufsize); 70 | if (count < 0) { 71 | printf("Error %ld reading from %s\n", count, filename); 72 | } 73 | 74 | close(fd); 75 | return count; 76 | } 77 | 78 | static int check_file_present(const char *filename) 79 | { 80 | struct stat sb; 81 | int retval; 82 | 83 | retval = stat(filename, &sb); 84 | if (retval == -1) { 85 | //fprintf(stderr, 86 | // "filename %s is not present\n", filename); 87 | return retval; 88 | } 89 | 90 | if ((sb.st_mode & S_IFMT) != S_IFREG) { 91 | fprintf(stderr, 92 | "filename %s must be a real file, not anything else.\n", 93 | filename); 94 | return -1; 95 | } 96 | return 0; 97 | } 98 | 99 | static int check_dir_present(const char *filename) 100 | { 101 | struct stat sb; 102 | int retval; 103 | 104 | retval = stat(filename, &sb); 105 | if (retval == -1) { 106 | // fprintf(stderr, 107 | // "filename %s is not present\n", filename); 108 | return retval; 109 | } 110 | 111 | if ((sb.st_mode & S_IFMT) != S_IFDIR) { 112 | fprintf(stderr, 113 | "filename %s must be a directory, not anything else.\n", 114 | filename); 115 | return -1; 116 | } 117 | return 0; 118 | } 119 | 120 | static char *sysfs_string(const char *directory, const char *filename) 121 | { 122 | char full_filename[PATH_MAX]; 123 | char buffer[256]; 124 | char *temp; 125 | int retval; 126 | 127 | snprintf(full_filename, PATH_MAX, "%s/%s", directory, filename); 128 | 129 | if (check_file_present(full_filename)) 130 | return NULL; 131 | 132 | retval = openreadclose(full_filename, buffer, sizeof(buffer)); 133 | if (retval <= 0) 134 | return NULL; 135 | 136 | /* strip trailing \n off */ 137 | buffer[retval-1] = 0x00; 138 | temp = g_strdup(buffer); 139 | return temp; 140 | } 141 | 142 | static int sysfs_int(const char *dir, const char *filename, int base) 143 | { 144 | char *string = sysfs_string(dir, filename); 145 | 146 | if (!string) 147 | return 0; 148 | 149 | int value = strtol(string, NULL, base); 150 | 151 | g_free(string); 152 | return value; 153 | } 154 | 155 | static void DestroyEndpoint (struct DeviceEndpoint *endpoint) 156 | { 157 | if (endpoint == NULL) 158 | return; 159 | 160 | g_free (endpoint->type); 161 | g_free (endpoint->interval); 162 | 163 | g_free (endpoint); 164 | 165 | return; 166 | } 167 | 168 | 169 | static void DestroyInterface (struct DeviceInterface *interface) 170 | { 171 | int i; 172 | 173 | if (interface == NULL) 174 | return; 175 | 176 | for (i = 0; i < MAX_ENDPOINTS; ++i) 177 | DestroyEndpoint (interface->endpoint[i]); 178 | 179 | g_free (interface->name); 180 | g_free (interface->class); 181 | 182 | g_free (interface); 183 | 184 | return; 185 | } 186 | 187 | 188 | static void DestroyConfig (struct DeviceConfig *config) 189 | { 190 | int i; 191 | 192 | if (config == NULL) 193 | return; 194 | 195 | for (i = 0; i < MAX_INTERFACES; ++i) 196 | DestroyInterface (config->interface[i]); 197 | 198 | g_free (config->maxPower); 199 | 200 | g_free (config); 201 | 202 | return; 203 | } 204 | 205 | 206 | static void DestroyBandwidth (struct DeviceBandwidth *bandwidth) 207 | { 208 | /* nothing dynamic in the bandwidth structure yet. */ 209 | return; 210 | } 211 | 212 | static void DestroyDevice(struct Device *device) 213 | { 214 | int i; 215 | 216 | if (device == NULL) 217 | return; 218 | 219 | for (i = 0; i < MAX_CHILDREN; ++i) 220 | DestroyDevice (device->child[i]); 221 | 222 | for (i = 0; i < MAX_CONFIGS; ++i) 223 | DestroyConfig (device->config[i]); 224 | 225 | if (device->bandwidth != NULL) { 226 | DestroyBandwidth (device->bandwidth); 227 | g_free (device->bandwidth); 228 | } 229 | 230 | g_free (device->name); 231 | g_free (device->version); 232 | g_free (device->class); 233 | g_free (device->subClass); 234 | g_free (device->protocol); 235 | g_free (device->revisionNumber); 236 | g_free (device->manufacturer); 237 | g_free (device->product); 238 | g_free (device->serialNumber); 239 | 240 | g_free (device); 241 | 242 | return; 243 | } 244 | 245 | static struct Device *FindDeviceNode(struct Device *device, 246 | int deviceNumber, int busNumber) 247 | { 248 | int i; 249 | struct Device *result; 250 | 251 | if (device == NULL) 252 | return(NULL); 253 | 254 | if ((device->deviceNumber == deviceNumber) && 255 | (device->busNumber == busNumber)) 256 | return(device); 257 | 258 | for (i = 0; i < MAX_CHILDREN; ++i) { 259 | result = FindDeviceNode (device->child[i], deviceNumber, busNumber); 260 | if (result != NULL) 261 | return(result); 262 | } 263 | 264 | return(NULL); 265 | } 266 | 267 | struct Device *usb_find_device (int deviceNumber, int busNumber) 268 | { 269 | int i; 270 | struct Device *result; 271 | 272 | /* search through the tree to try to find a device */ 273 | for (i = 0; i < MAX_CHILDREN; ++i) { 274 | result = FindDeviceNode (rootDevice->child[i], deviceNumber, busNumber); 275 | if (result != NULL) 276 | return(result); 277 | } 278 | return(NULL); 279 | } 280 | 281 | /* Build all of the names of the devices */ 282 | static void NameDevice (struct Device *device) 283 | { 284 | int configNum; 285 | int interfaceNum; 286 | int i; 287 | 288 | if (device == NULL) 289 | return; 290 | 291 | /* build the name for this device */ 292 | if (device != rootDevice) { 293 | if (device->name) 294 | g_free (device->name); 295 | device->name = (gchar *)g_malloc0 (DEVICE_STRING_MAXSIZE*sizeof (gchar)); 296 | 297 | /* see if this device has a product name */ 298 | if (device->product != NULL) { 299 | strcpy (device->name, device->product); 300 | goto create_children_names; 301 | } 302 | 303 | /* see if this device is a root hub */ 304 | if (device->level == 0) { 305 | strcpy (device->name, "root hub"); 306 | goto create_children_names; 307 | } 308 | 309 | /* look through all of the interfaces of this device, adding them all up to form a name */ 310 | for (configNum = 0; configNum < MAX_CONFIGS; ++configNum) { 311 | if (device->config[configNum]) { 312 | struct DeviceConfig *config = device->config[configNum]; 313 | for (interfaceNum = 0; interfaceNum < MAX_INTERFACES; ++interfaceNum) { 314 | if (config->interface[interfaceNum]) { 315 | struct DeviceInterface *interface = config->interface[interfaceNum]; 316 | if (interface->name != NULL) { 317 | if (strstr (interface->name, "none") == NULL) { 318 | if (strcmp (interface->name, "hid") == 0) { 319 | if (interface->subClass == 1) { 320 | switch (interface->protocol) { 321 | case 1 : 322 | strcat (device->name, "keyboard"); 323 | continue; 324 | case 2 : 325 | strcat (device->name, "mouse"); 326 | continue; 327 | } 328 | } 329 | } 330 | if (strstr (device->name, interface->name) == NULL) { 331 | if (strlen (device->name) > 0) { 332 | strcat (device->name, " / "); 333 | } 334 | strcat (device->name, interface->name); 335 | } 336 | } 337 | } 338 | } 339 | } 340 | } 341 | } 342 | 343 | if (strlen (device->name) == 0) { 344 | strcat (device->name, "Unknown Device"); 345 | } 346 | 347 | } 348 | 349 | create_children_names: 350 | 351 | /* create all of the children's names */ 352 | for (i = 0; i < MAX_CHILDREN; ++i) { 353 | NameDevice (device->child[i]); 354 | } 355 | 356 | return; 357 | } 358 | 359 | 360 | void usb_initialize_list (void) 361 | { 362 | if (rootDevice != NULL) { 363 | DestroyDevice (rootDevice); 364 | rootDevice = NULL; 365 | } 366 | 367 | rootDevice = (struct Device *)g_malloc0 (sizeof(struct Device)); 368 | 369 | /* clean up any bandwidth devices */ 370 | if (currentBandwidth != NULL) { 371 | DestroyBandwidth (currentBandwidth); 372 | g_free (currentBandwidth); 373 | } 374 | 375 | return; 376 | } 377 | 378 | struct entity { 379 | struct list_node list; 380 | char *name; 381 | }; 382 | 383 | static struct entity *entity_create(char *name) 384 | { 385 | struct entity *e; 386 | 387 | e = malloc(sizeof(*e)); 388 | memset(e, 0x00, sizeof(*e)); 389 | e->name = g_strdup(name); 390 | return e; 391 | } 392 | 393 | static void entity_destroy(struct entity *e) 394 | { 395 | list_del(&e->list); 396 | g_free(e->name); 397 | g_free(e); 398 | } 399 | 400 | static void entity_add(struct list_head *list, struct entity *e) 401 | { 402 | struct entity *iter; 403 | 404 | list_for_each(list, iter, list) { 405 | if (strcmp(iter->name, e->name) > 0) { 406 | list_add_before(list, &iter->list, &e->list); 407 | goto exit; 408 | } 409 | } 410 | list_add_tail(list, &e->list); 411 | 412 | exit: 413 | #ifdef DEBUG_LIST 414 | printf("list = "); 415 | list_for_each(list, iter, list) { 416 | printf("%s ", iter->name); 417 | } 418 | printf("\n"); 419 | #endif 420 | return; 421 | } 422 | 423 | static void endpoint_parse(struct DeviceInterface *interface, const char *dir) 424 | { 425 | struct DeviceEndpoint *endpoint; 426 | int i; 427 | 428 | /* find a place in this interface to place the endpoint */ 429 | for (i = 0; i < MAX_ENDPOINTS; ++i) { 430 | if (interface->endpoint[i] == NULL) { 431 | break; 432 | } 433 | } 434 | if (i >= MAX_ENDPOINTS) { 435 | /* ran out of room to hold this endpoint */ 436 | g_warning ("Too many endpoints for this device.\n"); 437 | return; 438 | } 439 | 440 | endpoint = g_malloc0(sizeof(struct DeviceEndpoint)); 441 | 442 | endpoint->attribute = sysfs_int(dir, "bmAttributes", 16); 443 | endpoint->maxPacketSize = sysfs_int(dir, "wMaxPacketSize", 16); 444 | 445 | char *epdir = sysfs_string(dir, "direction"); 446 | if (!strcmp(epdir, "in")) 447 | endpoint->in = TRUE; 448 | else 449 | endpoint->in = FALSE; 450 | g_free(epdir); 451 | 452 | endpoint->type = sysfs_string(dir, "type"); 453 | endpoint->interval = sysfs_string(dir, "interval"); 454 | endpoint->address = sysfs_int(dir, "bEndpointAddress", 16); 455 | 456 | /* point the interface to the endpoint */ 457 | interface->endpoint[i] = endpoint; 458 | 459 | // FIXME check max packet size 460 | #if 0 461 | char *maxps_hex = sysfs_read(dir, "wMaxPacketSize"); 462 | int maxps_num = strtol(maxps_hex, NULL, 16); 463 | 464 | /* max packet size is bits 0-10 with multiplicity values in bits 11 and 12 */ 465 | maxps_num = (maxps_num & 0x7ff) * (1 + ((maxps_num >> 11) & 0x03)); 466 | 467 | printf("E: Ad=%s(%s) Atr=%s(%s) MxPS=%4d Ivl=%s\n", 468 | addr, direction, attr, type, maxps_num, interval); 469 | #endif 470 | } 471 | 472 | static void endpoints_parse(struct DeviceInterface *interface, const char *dir) 473 | { 474 | DIR *d; 475 | struct dirent *de; 476 | LIST_HEAD(endpoint_list); 477 | struct entity *temp; 478 | struct entity *e; 479 | 480 | d = opendir(dir); 481 | if (!d) { 482 | fprintf(stderr, "Can not open %s directory\n", dir); 483 | return; 484 | } 485 | 486 | while ((de = readdir(d))) { 487 | if (de->d_type == DT_DIR) { 488 | /* See if this directory is an interface or not */ 489 | char endpoint[PATH_MAX]; 490 | 491 | snprintf(endpoint, PATH_MAX, "%s/%s", dir, de->d_name); 492 | char *endpointaddr = sysfs_string(endpoint, "bEndpointAddress"); 493 | if (endpointaddr) { 494 | e = entity_create(endpoint); 495 | entity_add(&endpoint_list, e); 496 | g_free(endpointaddr); 497 | } 498 | 499 | } 500 | } 501 | 502 | closedir(d); 503 | 504 | list_for_each_safe(&endpoint_list, e, temp, list) { 505 | endpoint_parse(interface, e->name); 506 | entity_destroy(e); 507 | } 508 | } 509 | 510 | static void interface_parse(struct Device *device, const char *dir) 511 | { 512 | struct DeviceConfig *config; 513 | struct DeviceInterface *interface; 514 | int i; 515 | int configNum; 516 | 517 | /* find the LAST config in the device */ 518 | configNum = -1; 519 | for (i = 0; i < MAX_CONFIGS; ++i) 520 | if (device->config[i]) 521 | configNum = i; 522 | if (configNum == -1) { 523 | /* no config to put this interface at, not good */ 524 | g_warning ("No config to put an interface at for this device.\n"); 525 | return; 526 | } 527 | 528 | config = device->config[configNum]; 529 | 530 | /* now find a place in this config to place the interface */ 531 | for (i = 0; i < MAX_INTERFACES; ++i) 532 | if (config->interface[i] == NULL) 533 | break; 534 | if (i >= MAX_INTERFACES) { 535 | /* ran out of room to hold this interface */ 536 | g_warning ("Too many interfaces for this device.\n"); 537 | return; 538 | } 539 | 540 | interface = g_malloc0(sizeof(struct DeviceInterface)); 541 | 542 | interface->interfaceNumber = sysfs_int(dir, "bInterfaceNumber", 10); 543 | interface->alternateNumber = sysfs_int(dir, "bAlternateSetting", 10); 544 | interface->numEndpoints = sysfs_int(dir, "bNumEndpoints", 10); 545 | interface->subClass = sysfs_int(dir, "bInterfaceSubClass", 16); 546 | interface->protocol = sysfs_int(dir, "bInterfaceProtocol", 16); 547 | 548 | interface->class = sysfs_string(dir, "bInterfaceClass"); 549 | 550 | char drivername[PATH_MAX]; 551 | char link[PATH_MAX]; 552 | struct stat sb; 553 | int retval; 554 | char *driver = "(none)"; 555 | 556 | /* 557 | * find the driver name by looking at the basename of the driver 558 | * symbolic link. 559 | * In bash this would just be: 560 | * driver=`readlink $ifpath/driver` 561 | * driver=`basename "$driver"` 562 | */ 563 | snprintf(drivername, PATH_MAX, "%s/driver", dir); 564 | retval = stat(drivername, &sb); 565 | memset(link, 0x00, sizeof(link)); 566 | if (!retval) { 567 | retval = readlink(drivername, link, sizeof(link)); 568 | if (retval > 0) { 569 | /* 570 | * crude 'basename', go to the end of the string and 571 | * walk back to find a '/' and then go forward one. 572 | */ 573 | driver = &link[strlen(link)]; 574 | while (driver[0] != '/') 575 | driver--; 576 | driver++; 577 | } 578 | } 579 | 580 | interface->name = g_strdup(driver); 581 | 582 | /* if this interface does not have a driver attached to it, save that info for later */ 583 | if (strncmp(interface->name, INTERFACE_DRIVERNAME_NODRIVER_STRING, 584 | INTERFACE_DRIVERNAME_STRING_MAXLENGTH) == 0) { 585 | interface->driverAttached = FALSE; 586 | } else { 587 | interface->driverAttached = TRUE; 588 | } 589 | 590 | /* now point the config to this interface */ 591 | config->interface[i] = interface; 592 | 593 | endpoints_parse(interface, dir); 594 | } 595 | 596 | static void interfaces_parse(struct Device *device, const char *dir) 597 | { 598 | DIR *d; 599 | struct dirent *de; 600 | LIST_HEAD(interface_list); 601 | struct entity *temp; 602 | struct entity *e; 603 | 604 | d = opendir(dir); 605 | if (!d) { 606 | fprintf(stderr, "Can not open %s directory\n", dir); 607 | return; 608 | } 609 | 610 | while ((de = readdir(d))) { 611 | if (de->d_type == DT_DIR) { 612 | /* See if this directory is an interface or not */ 613 | char interface[PATH_MAX]; 614 | 615 | snprintf(interface, PATH_MAX, "%s/%s", dir, de->d_name); 616 | char *interfacenum = sysfs_string(interface, "bInterfaceNumber"); 617 | if (interfacenum) { 618 | e = entity_create(interface); 619 | entity_add(&interface_list, e); 620 | g_free(interfacenum); 621 | } 622 | } 623 | } 624 | 625 | closedir(d); 626 | 627 | list_for_each_safe(&interface_list, e, temp, list) { 628 | interface_parse(device, e->name); 629 | entity_destroy(e); 630 | } 631 | } 632 | 633 | static void device_parse(struct Device *parent, const char *directory); 634 | 635 | static void children_parse(struct Device *parent, const char *dir) 636 | { 637 | int devcount = 0; 638 | DIR *d; 639 | struct dirent *de; 640 | 641 | d = opendir(dir); 642 | if (!d) { 643 | fprintf(stderr, "Can not open %s directory\n", dir); 644 | return; 645 | } 646 | 647 | while ((de = readdir(d))) { 648 | if (de->d_type == DT_DIR) { 649 | if (de->d_name[0] == '.') 650 | continue; 651 | 652 | /* See if this directory is an interface or not */ 653 | char device[PATH_MAX]; 654 | 655 | snprintf(device, PATH_MAX, "%s/%s", dir, de->d_name); 656 | char *device_class = sysfs_string(device, "bDeviceClass"); 657 | if (device_class) { 658 | ++devcount; 659 | device_parse(parent, device); 660 | } 661 | 662 | g_free(device_class); 663 | } 664 | } 665 | 666 | closedir(d); 667 | } 668 | 669 | static void device_parse(struct Device *parent, const char *dir) 670 | { 671 | struct Device *device; 672 | 673 | if (check_dir_present(dir)) 674 | return; 675 | 676 | device = (struct Device *)(g_malloc0 (sizeof(struct Device))); 677 | 678 | if (parent == rootDevice) 679 | device->level = 0; 680 | else 681 | device->level = 1; 682 | 683 | int portnum = 0; 684 | 685 | if (parent != rootDevice) { 686 | /* 687 | * The port number is the last number of the file (if present) 688 | * before the '.' or '-' - 1 689 | */ 690 | const char *temp = &dir[strlen(dir)]; 691 | 692 | while ((*temp != '.') && (*temp != '-')) 693 | --temp; 694 | temp++; 695 | portnum = strtol(temp, NULL, 10) - 1; 696 | if (portnum < 0) 697 | portnum = 0; 698 | } 699 | device->portNumber = portnum; 700 | device->busNumber = sysfs_int(dir, "busnum", 10); 701 | device->deviceNumber = sysfs_int(dir, "devnum", 10); 702 | device->speed = sysfs_int(dir, "speed", 10); 703 | device->maxChildren = sysfs_int(dir, "maxchild", 10); 704 | 705 | device->parent = parent; 706 | 707 | if (parent == rootDevice) { 708 | ++rootDevice->maxChildren; 709 | rootDevice->child[rootDevice->maxChildren-1] = device; 710 | } else { 711 | parent->child[portnum] = device; 712 | } 713 | 714 | device->vendorId = sysfs_int(dir, "idVendor", 16); 715 | device->productId = sysfs_int(dir, "idProduct", 16); 716 | 717 | device->version = sysfs_string(dir, "version"); 718 | device->class = sysfs_string(dir, "bDeviceClass"); 719 | device->subClass = sysfs_string(dir, "bDeviceSubClass"); 720 | device->protocol = sysfs_string(dir, "bDeviceProtocol"); 721 | device->maxPacketSize = sysfs_int(dir, "bMaxPacketSize0", 10); 722 | device->numConfigs = sysfs_int(dir, "bNumConfigurations", 10); 723 | 724 | device->manufacturer = sysfs_string(dir, "manufacturer"); 725 | device->product = sysfs_string(dir, "product"); 726 | device->serialNumber = sysfs_string(dir, "serial"); 727 | char *bcddevice = sysfs_string(dir, "bcdDevice"); 728 | 729 | device->revisionNumber = (char *)g_malloc0 ((DEVICE_REVISION_NUMBER_SIZE) * sizeof(char)); 730 | device->revisionNumber[0] = bcddevice[0]; 731 | device->revisionNumber[1] = bcddevice[1]; 732 | device->revisionNumber[2] = '.'; 733 | device->revisionNumber[3] = bcddevice[2]; 734 | device->revisionNumber[4] = bcddevice[3]; 735 | g_free(bcddevice); 736 | 737 | struct DeviceConfig *config; 738 | 739 | config = g_malloc0(sizeof(struct DeviceConfig)); 740 | config->maxPower = sysfs_string(dir, "bMaxPower"); 741 | config->numInterfaces = sysfs_int(dir, "bNumInterfaces", 10); 742 | config->configNumber = sysfs_int(dir, "bConfigurationValue", 10); 743 | config->attributes = sysfs_int(dir, "bmAttributes", 16); 744 | 745 | /* have the device now point to this config */ 746 | device->config[0] = config; 747 | 748 | interfaces_parse(device, dir); 749 | 750 | children_parse(device, dir); 751 | } 752 | 753 | 754 | void sysfs_parse(void) 755 | { 756 | char filename[PATH_MAX]; 757 | int i; 758 | 759 | snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/"); 760 | 761 | if (check_dir_present(filename)) { 762 | fprintf(stderr, "%s must be present, exiting...\n", filename); 763 | return; 764 | } 765 | 766 | for (i = 1; i < 100; ++i) { 767 | snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/usb%d/", i); 768 | device_parse(rootDevice, filename); 769 | } 770 | } 771 | 772 | void usb_name_devices (void) 773 | { 774 | NameDevice (rootDevice); 775 | } 776 | -------------------------------------------------------------------------------- /sysfs.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * sysfs.h for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000, 2021-2022 by Greg Kroah-Hartman, greg@kroah.com 5 | */ 6 | #ifndef __SYSFS_H 7 | #define __SYSFS_H 8 | 9 | /* should make these dynamic someday... */ 10 | #define MAX_ENDPOINTS 32 11 | #define MAX_INTERFACES 128 12 | #define MAX_CONFIGS 32 13 | #define MAX_CHILDREN 32 14 | 15 | #define DEVICE_REVISION_NUMBER_SIZE 6 16 | #define DEVICE_STRING_MAXSIZE 255 17 | 18 | #define INTERFACE_DRIVERNAME_NODRIVER_STRING "(none)" 19 | #define INTERFACE_DRIVERNAME_STRING_MAXLENGTH 50 20 | 21 | struct DeviceEndpoint { 22 | gint address; 23 | gboolean in; /* TRUE if in, FALSE if out */ 24 | gint attribute; 25 | gchar *type; 26 | gint maxPacketSize; 27 | gchar *interval; 28 | }; 29 | 30 | struct DeviceInterface { 31 | gchar *name; 32 | gint interfaceNumber; 33 | gint alternateNumber; 34 | gint numEndpoints; 35 | gint subClass; 36 | gint protocol; 37 | gchar *class; 38 | struct DeviceEndpoint *endpoint[MAX_ENDPOINTS]; 39 | gboolean driverAttached; /* TRUE if driver is attached to this interface currently */ 40 | }; 41 | 42 | struct DeviceConfig { 43 | gint configNumber; 44 | gint numInterfaces; 45 | gint attributes; 46 | gchar *maxPower; 47 | struct DeviceInterface *interface[MAX_INTERFACES]; 48 | }; 49 | 50 | struct DeviceBandwidth { 51 | gint allocated; 52 | gint total; 53 | gint percent; 54 | gint numInterruptRequests; 55 | gint numIsocRequests; 56 | }; 57 | 58 | struct Device { 59 | gchar *name; 60 | gint busNumber; 61 | gint level; 62 | gint portNumber; 63 | gint connectorNumber; 64 | gint deviceNumber; 65 | gint speed; 66 | gint maxChildren; 67 | gchar *version; 68 | gchar *class; 69 | gchar *subClass; 70 | gchar *protocol; 71 | gint maxPacketSize; 72 | gint numConfigs; 73 | gint vendorId; 74 | gint productId; 75 | gchar *revisionNumber; 76 | gchar *manufacturer; 77 | gchar *product; 78 | gchar *serialNumber; 79 | struct DeviceConfig *config[MAX_CONFIGS]; 80 | struct Device *parent; 81 | struct Device *child[MAX_CHILDREN]; 82 | struct DeviceBandwidth *bandwidth; 83 | GtkWidget *tree; 84 | GtkTreeIter leaf; 85 | }; 86 | 87 | extern struct Device *rootDevice; 88 | 89 | struct Device *usb_find_device(int deviceNumber, int busNumber); 90 | void usb_initialize_list(void); 91 | void sysfs_parse(void); 92 | void usb_name_devices(void); 93 | 94 | #endif /* __USB_PARSE_H */ 95 | 96 | -------------------------------------------------------------------------------- /usbtree.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * usbtree.c for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000, 2021-2022 by Greg Kroah-Hartman, 5 | */ 6 | #ifdef HAVE_CONFIG_H 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "usbtree.h" 20 | #include "sysfs.h" 21 | 22 | #define MAX_LINE_SIZE 1000 23 | 24 | 25 | static void Init (void) 26 | { 27 | GtkTextIter begin; 28 | GtkTextIter end; 29 | 30 | /* blow away the tree if there is one */ 31 | if (rootDevice != NULL) { 32 | gtk_tree_store_clear (treeStore); 33 | } 34 | 35 | /* clean out the text box */ 36 | gtk_text_buffer_get_start_iter(textDescriptionBuffer,&begin); 37 | gtk_text_buffer_get_end_iter(textDescriptionBuffer,&end); 38 | gtk_text_buffer_delete (textDescriptionBuffer, &begin, &end); 39 | 40 | return; 41 | } 42 | 43 | 44 | static void PopulateListBox (int deviceId) 45 | { 46 | struct Device *device; 47 | char *string; 48 | char *tempString; 49 | int configNum; 50 | int interfaceNum; 51 | int endpointNum; 52 | int deviceNumber = (deviceId >> 8); 53 | int busNumber = (deviceId & 0x00ff); 54 | GtkTextIter begin; 55 | GtkTextIter end; 56 | 57 | device = usb_find_device (deviceNumber, busNumber); 58 | if (device == NULL) { 59 | printf ("Can't seem to find device info to display\n"); 60 | return; 61 | } 62 | 63 | /* clear the textbox */ 64 | gtk_text_buffer_get_start_iter(textDescriptionBuffer,&begin); 65 | gtk_text_buffer_get_end_iter(textDescriptionBuffer,&end); 66 | gtk_text_buffer_delete (textDescriptionBuffer, &begin, &end); 67 | 68 | /* freeze the display */ 69 | /* this keeps the annoying scroll from happening */ 70 | gtk_widget_freeze_child_notify(textDescriptionView); 71 | 72 | string = (char *)g_malloc (1000); 73 | 74 | /* add the name to the textbox if we have one*/ 75 | if (device->name != NULL) { 76 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, device->name,strlen(device->name)); 77 | } 78 | 79 | /* add the manufacturer if we have one */ 80 | if (device->manufacturer != NULL) { 81 | sprintf (string, "\nManufacturer: %s", device->manufacturer); 82 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 83 | } 84 | 85 | /* add the serial number if we have one */ 86 | if (device->serialNumber != NULL) { 87 | sprintf (string, "\nSerial Number: %s", device->serialNumber); 88 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 89 | } 90 | 91 | /* add speed */ 92 | switch (device->speed) { 93 | case 1 : tempString = "1.5Mb/s (low)"; break; 94 | case 12 : tempString = "12Mb/s (full)"; break; 95 | case 480 : tempString = "480Mb/s (high)"; break; 96 | case 5000 : tempString = "5Gb/s (super)"; break; 97 | case 10000 : tempString = "10Gb/s (super+)"; break; 98 | case 20000 : tempString = "20Gb/s (super+)"; break; 99 | default : tempString = "unknown"; break; 100 | } 101 | sprintf (string, "\nSpeed: %s", tempString); 102 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 103 | 104 | /* Add Bus number */ 105 | sprintf (string, "\nBus:%4d", busNumber); 106 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 107 | 108 | /* Add device address */ 109 | sprintf (string, "\nAddress:%4d", deviceNumber); 110 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 111 | 112 | /* add ports if available */ 113 | if (device->maxChildren) { 114 | sprintf (string, "\nNumber of Ports: %i", device->maxChildren); 115 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 116 | } 117 | 118 | /* add the bandwidth info if available */ 119 | if (device->bandwidth != NULL) { 120 | sprintf (string, "\nBandwidth allocated: %i / %i (%i%%)", device->bandwidth->allocated, device->bandwidth->total, device->bandwidth->percent); 121 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 122 | 123 | sprintf (string, "\nTotal number of interrupt requests: %i", device->bandwidth->numInterruptRequests); 124 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 125 | 126 | sprintf (string, "\nTotal number of isochronous requests: %i", device->bandwidth->numIsocRequests); 127 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 128 | } 129 | 130 | /* add the USB version, device class, subclass, protocol, max packet size, and the number of configurations (if it is there) */ 131 | if (device->version) { 132 | sprintf (string, "\nUSB Version: %s\nDevice Class: %s\nDevice Subclass: %s\nDevice Protocol: %s\n" 133 | "Maximum Default Endpoint Size: %i\nNumber of Configurations: %i", 134 | device->version, device->class, device->subClass, device->protocol, 135 | device->maxPacketSize, device->numConfigs); 136 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 137 | } 138 | 139 | /* add the vendor id, product id, and revision number (if it is there) */ 140 | if (device->vendorId) { 141 | sprintf (string, "\nVendor Id: %.4x\nProduct Id: %.4x\nRevision Number: %s", 142 | device->vendorId, device->productId, device->revisionNumber); 143 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 144 | } 145 | 146 | /* display all the info for the configs */ 147 | for (configNum = 0; configNum < MAX_CONFIGS; ++configNum) { 148 | if (device->config[configNum]) { 149 | struct DeviceConfig *config = device->config[configNum]; 150 | 151 | /* show this config */ 152 | sprintf (string, "\n\nConfig Number: %i\n\tNumber of Interfaces: %i\n\t" 153 | "Attributes: %.2x\n\tMaxPower Needed: %s", 154 | config->configNumber, config->numInterfaces, 155 | config->attributes, config->maxPower); 156 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 157 | 158 | /* show all of the interfaces for this config */ 159 | for (interfaceNum = 0; interfaceNum < MAX_INTERFACES; ++interfaceNum) { 160 | if (config->interface[interfaceNum]) { 161 | struct DeviceInterface *interface = config->interface[interfaceNum]; 162 | 163 | sprintf (string, "\n\n\tInterface Number: %i", interface->interfaceNumber); 164 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string, strlen(string)); 165 | 166 | if (interface->name != NULL) { 167 | sprintf (string, "\n\t\tName: %s", interface->name); 168 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string, strlen(string)); 169 | } 170 | 171 | sprintf (string, "\n\t\tAlternate Number: %i\n\t\tClass: %s\n\t\t" 172 | "Sub Class: %.2x\n\t\tProtocol: %.2x\n\t\tNumber of Endpoints: %i", 173 | interface->alternateNumber, interface->class, 174 | interface->subClass, interface->protocol, interface->numEndpoints); 175 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string, strlen(string)); 176 | 177 | /* show all of the endpoints for this interface */ 178 | for (endpointNum = 0; endpointNum < MAX_ENDPOINTS; ++endpointNum) { 179 | if (interface->endpoint[endpointNum]) { 180 | struct DeviceEndpoint *endpoint = interface->endpoint[endpointNum]; 181 | 182 | sprintf (string, "\n\n\t\t\tEndpoint Address: %.2x\n\t\t\t" 183 | "Direction: %s\n\t\t\tAttribute: %i\n\t\t\t" 184 | "Type: %s\n\t\t\tMax Packet Size: %i\n\t\t\tInterval: %s", 185 | endpoint->address, 186 | endpoint->in ? "in" : "out", endpoint->attribute, 187 | endpoint->type, endpoint->maxPacketSize, endpoint->interval); 188 | gtk_text_buffer_insert_at_cursor(textDescriptionBuffer, string,strlen(string)); 189 | } 190 | } 191 | } 192 | } 193 | } 194 | } 195 | 196 | /* thaw the display */ 197 | gtk_widget_thaw_child_notify(textDescriptionView); 198 | 199 | /* clean up our string */ 200 | g_free (string); 201 | 202 | return; 203 | } 204 | 205 | 206 | static void SelectItem (GtkTreeSelection *selection, gpointer userData) 207 | { 208 | GtkTreeIter iter; 209 | GtkTreeModel *model; 210 | gint deviceAddr; 211 | 212 | if (gtk_tree_selection_get_selected (selection, &model, &iter)) { 213 | gtk_tree_model_get (model, &iter, 214 | DEVICE_ADDR_COLUMN, &deviceAddr, 215 | -1); 216 | PopulateListBox (deviceAddr); 217 | } 218 | } 219 | 220 | 221 | static void DisplayDevice (struct Device *parent, struct Device *device) 222 | { 223 | int i; 224 | int configNum; 225 | int interfaceNum; 226 | gboolean driverAttached = TRUE; 227 | gint deviceAddr; 228 | const gchar *tooltip = NULL; 229 | const gchar *color = NULL; 230 | 231 | if (device == NULL) 232 | return; 233 | 234 | /* build this node */ 235 | deviceAddr = (device->deviceNumber << 8) | device->busNumber; 236 | gtk_tree_store_append (treeStore, &device->leaf, 237 | (device->level != 0) ? &parent->leaf : NULL); 238 | 239 | /* determine if this device has drivers attached to all interfaces */ 240 | for (configNum = 0; configNum < MAX_CONFIGS; ++configNum) { 241 | if (device->config[configNum]) { 242 | struct DeviceConfig *config = device->config[configNum]; 243 | for (interfaceNum = 0; interfaceNum < MAX_INTERFACES; ++interfaceNum) { 244 | if (config->interface[interfaceNum]) { 245 | struct DeviceInterface *interface = config->interface[interfaceNum]; 246 | if (interface->driverAttached == FALSE) { 247 | driverAttached = FALSE; 248 | break; 249 | } 250 | } 251 | } 252 | } 253 | } 254 | 255 | /* change the color of this leaf if there are no drivers attached to it */ 256 | if (driverAttached == FALSE) { 257 | color = "red"; 258 | tooltip = "This device has no attached driver"; 259 | } 260 | 261 | gtk_tree_store_set (treeStore, &device->leaf, 262 | NAME_COLUMN, device->name, 263 | DEVICE_ADDR_COLUMN, deviceAddr, 264 | COLOR_COLUMN, color, 265 | TOOLTIP_COLUMN, tooltip, 266 | -1); 267 | 268 | /* create all of the children's leafs */ 269 | for (i = 0; i < MAX_CHILDREN; ++i) { 270 | DisplayDevice (device, device->child[i]); 271 | } 272 | 273 | return; 274 | } 275 | 276 | 277 | void LoadUSBTree (int refresh) 278 | { 279 | static gboolean signal_connected = FALSE; 280 | int i; 281 | 282 | Init(); 283 | 284 | usb_initialize_list (); 285 | 286 | sysfs_parse(); 287 | usb_name_devices (); 288 | 289 | /* build our tree */ 290 | for (i = 0; i < rootDevice->maxChildren; ++i) { 291 | DisplayDevice (rootDevice, rootDevice->child[i]); 292 | } 293 | 294 | gtk_widget_show (treeUSB); 295 | 296 | gtk_tree_view_expand_all (GTK_TREE_VIEW (treeUSB)); 297 | 298 | /* hook up our callback function to this tree if we haven't yet */ 299 | if (!signal_connected) { 300 | GtkTreeSelection *select; 301 | select = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeUSB)); 302 | g_signal_connect (G_OBJECT (select), "changed", 303 | G_CALLBACK (SelectItem), NULL); 304 | signal_connected = TRUE; 305 | } 306 | 307 | return; 308 | } 309 | 310 | void initialize_stuff(void) 311 | { 312 | return; 313 | } 314 | -------------------------------------------------------------------------------- /usbtree.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * usbtree.h for USBView - a USB device viewer 4 | * Copyright (c) 1999, 2000 by Greg Kroah-Hartman, greg@kroah.com 5 | */ 6 | #ifndef __USB_TREE_H 7 | #define __USB_TREE_H 8 | 9 | enum { 10 | NAME_COLUMN, 11 | DEVICE_ADDR_COLUMN, 12 | COLOR_COLUMN, 13 | TOOLTIP_COLUMN, 14 | N_COLUMNS 15 | }; 16 | 17 | extern GtkTreeStore *treeStore; 18 | extern GtkWidget *treeUSB; 19 | extern GtkWidget *textDescriptionView; 20 | extern GtkTextBuffer *textDescriptionBuffer; 21 | extern GtkWidget *windowMain; 22 | 23 | void LoadUSBTree(int refresh); 24 | void initialize_stuff(void); 25 | GtkWidget *create_windowMain(void); 26 | 27 | void on_buttonClose_clicked(GtkButton *button, gpointer user_data); 28 | gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data); 29 | void on_buttonRefresh_clicked(GtkButton *button, gpointer user_data); 30 | void on_buttonAbout_clicked(GtkButton *button, gpointer user_data); 31 | gint on_timer_timeout(gpointer user_data); 32 | 33 | #endif /* __USB_TREE_H */ 34 | -------------------------------------------------------------------------------- /usbview.8: -------------------------------------------------------------------------------- 1 | .\"SPDX-License-Identifier: GPL-2.0-only 2 | .\"Copyright (c) 1999-2012, 2021-2022 by Greg Kroah-Hartman 3 | .TH USBVIEW 8 "July 2002" 4 | .SH NAME 5 | usbview \- display information on USB devices 6 | .SH SYNOPSIS 7 | .B usbview 8 | .SH DESCRIPTION 9 | .B usbview 10 | provides a graphical summary of USB devices connected to the system. 11 | Detailed information may be displayed by selecting individual devices 12 | in the tree display. Red items are those that have no driver associated 13 | with them. 14 | .SH OPTIONS 15 | No command line options are accepted by 16 | .B usbview. 17 | .SH FILES 18 | .TP 19 | .B /sys/kernel/debug/usb/devices 20 | Kernel file providing information on USB devices attached to 21 | the machine. 22 | .SH SEE ALSO 23 | .BR lsusb (8), 24 | .BR lspci (8). 25 | .SH AUTHOR 26 | .B usbview 27 | was written by Greg Kroah-Hartman . This manual page 28 | was written by Mark Brown , for the Debian 29 | GNU/Linux system (but may be used freely by others). 30 | -------------------------------------------------------------------------------- /usbview.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=USBView 3 | GenericName=USB Device Viewer 4 | Comment=View USB devices attached to system 5 | Exec=usbview 6 | Icon=usbview 7 | Terminal=false 8 | Type=Application 9 | Keywords=USB;devices;connected;removable; 10 | Categories=GTK;System;HardwareSettings;Monitor; 11 | -------------------------------------------------------------------------------- /usbview.spdx: -------------------------------------------------------------------------------- 1 | SPDXVersion: SPDX-2.1 2 | DataLicense: CC0-1.0 3 | SPDXID: SPDXRef-DOCUMENT 4 | DocumentName: usbview 5 | DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-a9287fb6-2365-4719-9ccb-06ff24241234 6 | Creator: Person: Greg Kroah-Hartman 7 | Creator: Tool: reuse-2.1.0 8 | Created: 2023-10-22T11:46:53Z 9 | CreatorComment: This document was created automatically using available reuse information consistent with REUSE. 10 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-af757b707a7df157d48678eff4e62eac 11 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-e2153fe3ba8152ebc727c4824ee7337c 12 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-2dcb4377a58651d5eae07b05e9e885b6 13 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-fe8c25904ac74abf3a1c17a5eb2ab2b0 14 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-15375c669ee326296dbc83ea7c483a41 15 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-9284ac22f8518afa94b327efe9c90538 16 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-d324605884e4312a7f2b6aee65cd4d2d 17 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-c6b3a82be18f75ee7a05a568b391b8bf 18 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-0f62092c7a9d9815df2df8b6958306af 19 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-250f19b4509f500cc89de2b1abafeb0b 20 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-ad9a5ec48dee5b9adb0a9e451e383969 21 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-7652a985504658fd064d8bfae1747ee8 22 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-75cee34b41a71b3b013d939d09811403 23 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-485752152149364e149dba35cc73c625 24 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-2183fb9e39bb1ff5bf7f481aa1cc3447 25 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-66d380db6db52c0098ebf8c214e820b2 26 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-ea03c6e3042f071effab145109806c78 27 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-10cfedd6373ce3fe2296d60f9d36a186 28 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-a8975d94e1e4ad48994c82ff48f25e14 29 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-a3a19b39f90875a0b55f6c21900ed05a 30 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-5864d4e966d35437c29027ba0646586c 31 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-4e40e661397514e816ebd492e1710257 32 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-43179f15ed6fe3b883ecc4941a2d8a94 33 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-d718a08e04ac1847aea69b49fd11025a 34 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-c78a2b046e621d98c0e20776c29a7edb 35 | Relationship: SPDXRef-DOCUMENT describes SPDXRef-a365373ff97b945c58093e6576db3d9a 36 | 37 | FileName: ./.gitignore 38 | SPDXID: SPDXRef-af757b707a7df157d48678eff4e62eac 39 | FileChecksum: SHA1: 3a74b2ff42504d7603466f2c4bd9fe19d5fa4872 40 | LicenseConcluded: NOASSERTION 41 | LicenseInfoInFile: GPL-2.0-only 42 | FileCopyrightText: Copyright (c) 2022 Greg Kroah-Hartman 43 | 44 | FileName: ./AUTHORS 45 | SPDXID: SPDXRef-e2153fe3ba8152ebc727c4824ee7337c 46 | FileChecksum: SHA1: 574f8d14fc250efe98fe9ebb15ec936e724495e3 47 | LicenseConcluded: NOASSERTION 48 | LicenseInfoInFile: GPL-2.0-only 49 | FileCopyrightText: Copyright (c) 1999-2012, 2021-2022 by Greg Kroah-Hartman, greg@kroah.com 50 | 51 | FileName: ./ChangeLog 52 | SPDXID: SPDXRef-2dcb4377a58651d5eae07b05e9e885b6 53 | FileChecksum: SHA1: 1097d51bc4261dcd4ba93785b7e1c8f986230aed 54 | LicenseConcluded: NOASSERTION 55 | LicenseInfoInFile: GPL-2.0-only 56 | FileCopyrightText: Copyright (c) 2022 Greg Kroah-Hartman 57 | 58 | FileName: ./Makefile.am 59 | SPDXID: SPDXRef-fe8c25904ac74abf3a1c17a5eb2ab2b0 60 | FileChecksum: SHA1: 5f1e843658aaecffce6ff494ec918f0eb86d29ef 61 | LicenseConcluded: NOASSERTION 62 | LicenseInfoInFile: GPL-2.0-only 63 | FileCopyrightText: Copyright (c) 2022 Greg Kroah-Hartman 64 | 65 | FileName: ./README 66 | SPDXID: SPDXRef-15375c669ee326296dbc83ea7c483a41 67 | FileChecksum: SHA1: f5185253ff8f16abb0dfb485667ee3e482b8eeb1 68 | LicenseConcluded: NOASSERTION 69 | LicenseInfoInFile: GPL-2.0-only 70 | FileCopyrightText: Copyright (c) 1999-2012, 2021-2022 by Greg Kroah-Hartman, greg@kroah.com 71 | 72 | FileName: ./autogen.sh 73 | SPDXID: SPDXRef-9284ac22f8518afa94b327efe9c90538 74 | FileChecksum: SHA1: ce6330b2ba084b3c2fb2d34571dc3bdc3adb0b4e 75 | LicenseConcluded: NOASSERTION 76 | LicenseInfoInFile: GPL-2.0-only 77 | FileCopyrightText: Copyright (c) 2009,2010 Greg Kroah-Hartman 78 | 79 | FileName: ./callbacks.c 80 | SPDXID: SPDXRef-d324605884e4312a7f2b6aee65cd4d2d 81 | FileChecksum: SHA1: ca943582e897159dad2f1b406db7eb7c3f792ce0 82 | LicenseConcluded: NOASSERTION 83 | LicenseInfoInFile: GPL-2.0-only 84 | FileCopyrightText: Copyright (c) 1999, 2000 by Greg Kroah-Hartman, 85 | Copyright © 1999-2012, 2021-2022", 86 | 87 | FileName: ./ccan/check_type/check_type.h 88 | SPDXID: SPDXRef-c6b3a82be18f75ee7a05a568b391b8bf 89 | FileChecksum: SHA1: dcb6d2f713d116e0b63810a8de435614adc2c396 90 | LicenseConcluded: NOASSERTION 91 | LicenseInfoInFile: CC0-1.0 92 | FileCopyrightText: NONE 93 | 94 | FileName: ./ccan/config.h 95 | SPDXID: SPDXRef-0f62092c7a9d9815df2df8b6958306af 96 | FileChecksum: SHA1: cfde79b783ad2f94ed5da27236f366411376eaa7 97 | LicenseConcluded: NOASSERTION 98 | LicenseInfoInFile: CC0-1.0 99 | FileCopyrightText: NONE 100 | 101 | FileName: ./ccan/container_of/container_of.h 102 | SPDXID: SPDXRef-250f19b4509f500cc89de2b1abafeb0b 103 | FileChecksum: SHA1: 659d386d04ea43b6342138d166fe6f3a21b988fd 104 | LicenseConcluded: NOASSERTION 105 | LicenseInfoInFile: CC0-1.0 106 | FileCopyrightText: NONE 107 | 108 | FileName: ./ccan/list/list.h 109 | SPDXID: SPDXRef-ad9a5ec48dee5b9adb0a9e451e383969 110 | FileChecksum: SHA1: 74d16d358dba58139a36e75096fef780b5b48c0e 111 | LicenseConcluded: NOASSERTION 112 | LicenseInfoInFile: MIT 113 | FileCopyrightText: NONE 114 | 115 | FileName: ./ccan/str/str.h 116 | SPDXID: SPDXRef-7652a985504658fd064d8bfae1747ee8 117 | FileChecksum: SHA1: faa0d06168c5518dc503c398293a7e8ea96be94e 118 | LicenseConcluded: NOASSERTION 119 | LicenseInfoInFile: CC0-1.0 120 | FileCopyrightText: NONE 121 | 122 | FileName: ./ccan/str/str_debug.h 123 | SPDXID: SPDXRef-75cee34b41a71b3b013d939d09811403 124 | FileChecksum: SHA1: 231540f6e3e0fc02299f0169ab221948cf1fe6cb 125 | LicenseConcluded: NOASSERTION 126 | LicenseInfoInFile: CC0-1.0 127 | FileCopyrightText: NONE 128 | 129 | FileName: ./com.kroah.usbview.metainfo.xml 130 | SPDXID: SPDXRef-485752152149364e149dba35cc73c625 131 | FileChecksum: SHA1: 1c5d9430f6b1551b1e940d40b0775b13dbed92d4 132 | LicenseConcluded: NOASSERTION 133 | FileCopyrightText: NONE 134 | 135 | FileName: ./configure.ac 136 | SPDXID: SPDXRef-2183fb9e39bb1ff5bf7f481aa1cc3447 137 | FileChecksum: SHA1: 4028b78a87b40d82ce5fcb2fea8637340ab93d38 138 | LicenseConcluded: NOASSERTION 139 | LicenseInfoInFile: GPL-2.0-only 140 | FileCopyrightText: Copyright (c) 2022 Greg Kroah-Hartman 141 | 142 | FileName: ./interface.c 143 | SPDXID: SPDXRef-66d380db6db52c0098ebf8c214e820b2 144 | FileChecksum: SHA1: 5ff88186bc5f1cecff9312eabc61544f47abe700 145 | LicenseConcluded: NOASSERTION 146 | LicenseInfoInFile: GPL-2.0-only 147 | FileCopyrightText: Copyright (c) 1999, 2000, 2009 by Greg Kroah-Hartman, 148 | 149 | FileName: ./main.c 150 | SPDXID: SPDXRef-ea03c6e3042f071effab145109806c78 151 | FileChecksum: SHA1: fef900212573c442cd835cc70b3dc42d6c19f029 152 | LicenseConcluded: NOASSERTION 153 | LicenseInfoInFile: GPL-2.0-only 154 | FileCopyrightText: Copyright (c) 1999, 2000 by Greg Kroah-Hartman, 155 | 156 | FileName: ./sysfs.c 157 | SPDXID: SPDXRef-10cfedd6373ce3fe2296d60f9d36a186 158 | FileChecksum: SHA1: a2233ef39597b84d31656e763859e31eb2f11a0b 159 | LicenseConcluded: NOASSERTION 160 | LicenseInfoInFile: GPL-2.0-only 161 | FileCopyrightText: Copyright (c) 1999, 2000, 2022 by Greg Kroah-Hartman, 162 | 163 | FileName: ./sysfs.h 164 | SPDXID: SPDXRef-a8975d94e1e4ad48994c82ff48f25e14 165 | FileChecksum: SHA1: a84039bb08513853edd3e9370eaef2c74d105c5f 166 | LicenseConcluded: NOASSERTION 167 | LicenseInfoInFile: GPL-2.0-only 168 | FileCopyrightText: Copyright (c) 1999, 2000, 2021-2022 by Greg Kroah-Hartman, greg@kroah.com 169 | 170 | FileName: ./usbtree.c 171 | SPDXID: SPDXRef-a3a19b39f90875a0b55f6c21900ed05a 172 | FileChecksum: SHA1: 847f50c52060a860aa304de04378db400a27be78 173 | LicenseConcluded: NOASSERTION 174 | LicenseInfoInFile: GPL-2.0-only 175 | FileCopyrightText: Copyright (c) 1999, 2000, 2021-2022 by Greg Kroah-Hartman, 176 | 177 | FileName: ./usbtree.h 178 | SPDXID: SPDXRef-5864d4e966d35437c29027ba0646586c 179 | FileChecksum: SHA1: 93ed93968376f93c3648618221bce0bc4905f709 180 | LicenseConcluded: NOASSERTION 181 | LicenseInfoInFile: GPL-2.0-only 182 | FileCopyrightText: Copyright (c) 1999, 2000 by Greg Kroah-Hartman, greg@kroah.com 183 | 184 | FileName: ./usbview.8 185 | SPDXID: SPDXRef-4e40e661397514e816ebd492e1710257 186 | FileChecksum: SHA1: 1a4eeeecfa4447d3600acb422b0028960f6a6c36 187 | LicenseConcluded: NOASSERTION 188 | LicenseInfoInFile: GPL-2.0-only 189 | FileCopyrightText: Copyright (c) 1999-2012, 2021-2022 by Greg Kroah-Hartman 190 | 191 | FileName: ./usbview.desktop 192 | SPDXID: SPDXRef-43179f15ed6fe3b883ecc4941a2d8a94 193 | FileChecksum: SHA1: fb5e92853ecb872b1df4aa65efb2859f8624c75d 194 | LicenseConcluded: NOASSERTION 195 | FileCopyrightText: NONE 196 | 197 | FileName: ./usbview_icon.svg 198 | SPDXID: SPDXRef-d718a08e04ac1847aea69b49fd11025a 199 | FileChecksum: SHA1: 9490ae1ab95b9e87d8ae962d45b29b03e696f7b3 200 | LicenseConcluded: NOASSERTION 201 | FileCopyrightText: NONE 202 | 203 | FileName: ./usbview_logo.xcf 204 | SPDXID: SPDXRef-c78a2b046e621d98c0e20776c29a7edb 205 | FileChecksum: SHA1: 6a720f1ea150258c58a75bf195cd4962d4078706 206 | LicenseConcluded: NOASSERTION 207 | FileCopyrightText: Copyright (C) 1999-2012 ...�!?� " 208 | Copyright (C) 1999-2012\nGreg Kroah-Hartman <greg@kroah.com>") 209 | 210 | FileName: ./usbview_logo.xpm 211 | SPDXID: SPDXRef-a365373ff97b945c58093e6576db3d9a 212 | FileChecksum: SHA1: dafd2b5f02ae9e4605dd36035cc3d507574e5325 213 | LicenseConcluded: NOASSERTION 214 | FileCopyrightText: NONE 215 | -------------------------------------------------------------------------------- /usbview_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 16 | 17 | 19 | image/svg+xml 20 | 22 | 23 | 24 | 25 | 26 | 28 | 36 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /usbview_logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregkh/usbview/592e5ed367dc09f773357a39c3141636be5de248/usbview_logo.xcf --------------------------------------------------------------------------------