├── .github └── FUNDING.yml ├── .gitignore ├── Kbuild ├── LICENSE ├── README.md ├── auth ├── auth.c ├── auth.h ├── crypto.c └── crypto.h ├── bus ├── bus.c ├── bus.h ├── protocol.c └── protocol.h ├── dkms.conf ├── driver ├── chatpad.c ├── common.c ├── common.h ├── gamepad.c ├── headset.c ├── madcatz_glam.c ├── madcatz_strat.c └── pdp_jaguar.c ├── install.sh ├── install ├── firmware.sh └── modprobe.conf ├── logo.svg ├── transport ├── dongle.c ├── mt76.c ├── mt76.h ├── mt76_defs.h └── wired.c └── uninstall.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://www.paypal.com/donate?hosted_button_id=BWUECKFDNY446 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | internal/* 2 | .vscode/* 3 | -------------------------------------------------------------------------------- /Kbuild: -------------------------------------------------------------------------------- 1 | xone-wired-y := transport/wired.o 2 | xone-dongle-y := transport/dongle.o transport/mt76.o 3 | xone-gip-y := bus/bus.o bus/protocol.o auth/auth.o auth/crypto.o driver/common.o 4 | xone-gip-gamepad-y := driver/gamepad.o 5 | xone-gip-headset-y := driver/headset.o 6 | xone-gip-chatpad-y := driver/chatpad.o 7 | xone-gip-madcatz-strat-y := driver/madcatz_strat.o 8 | xone-gip-madcatz-glam-y := driver/madcatz_glam.o 9 | xone-gip-pdp-jaguar-y := driver/pdp_jaguar.o 10 | obj-m := xone-wired.o xone-dongle.o xone-gip.o xone-gip-gamepad.o xone-gip-headset.o xone-gip-chatpad.o xone-gip-madcatz-strat.o xone-gip-madcatz-glam.o xone-gip-pdp-jaguar.o 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | Everyone is permitted to copy and distribute verbatim copies 7 | 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 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 3 |

4 | 5 |

6 | Release Badge 7 | Discord Badge 8 | Donate Button 9 |

10 | 11 | `xone` is a Linux kernel driver for Xbox One and Xbox Series X|S accessories. It serves as a modern replacement for `xpad`, aiming to be compatible with Microsoft's *Game Input Protocol* (GIP). 12 | 13 | **NOTE**: This is a fork, please support the upstream project. 14 | 15 | ## Compatibility 16 | 17 | - [x] Wired devices (via USB) 18 | - [x] Wireless devices (with Xbox Wireless Dongle) 19 | - [ ] Bluetooth devices (check out [`xpadneo`](https://github.com/atar-axis/xpadneo)) 20 | 21 | Installing `xone` will disable the `xpad` kernel driver. If you are still using Xbox or Xbox 360 peripherals, you will have to install [`xpad-noone`](https://github.com/medusalix/xpad-noone) as a replacement for `xpad`. 22 | 23 | ## Important notes 24 | 25 | This driver is still in active development. Use at your own risk! 26 | If you are running `xow` upgrading to `xone` is *highly recommended*! 27 | Always update your Xbox devices to the latest firmware version! 28 | **Any feedback including bug reports, suggestions or ideas is [*greatly appreciated*](https://discord.gg/FDQxwWk).** 29 | 30 | ## Features 31 | 32 | - [x] Input and force feedback (rumble) 33 | - [x] Battery reporting (`UPower` integration) 34 | - [x] LED control (using `/sys/class/leds`) 35 | - [x] Audio capture/playback (through `ALSA`) 36 | - [x] Power management (suspend/resume and remote/wireless wakeup) 37 | 38 | ## Supported devices 39 | 40 | - [x] Gamepads 41 | - [x] Xbox One Controllers 42 | - [x] Xbox Series X|S Controllers 43 | - [x] Xbox Adaptive Controller 44 | - [x] Third party controllers (PowerA, PDP, etc.) 45 | - [x] Headsets 46 | - [x] Xbox One Chat Headset 47 | - [x] Xbox One Stereo Headset (adapter or jack) 48 | - [x] Xbox Wireless Headset 49 | - [x] Third party wired and wireless headsets (SteelSeries, Razer, etc.) 50 | - [x] Guitars & Drums 51 | - [x] Mad Catz Rock Band 4 Wireless Fender Stratocaster 52 | - [x] Mad Catz Rock Band 4 Wireless Drum Kit 53 | - [x] PDP Rock Band 4 Wireless Fender Jaguar 54 | - [x] Xbox One Chatpad 55 | - [ ] Third party racing wheels (Thrustmaster, Logitech, etc.) 56 | 57 | ## Releases 58 | 59 | [![Packaging status](https://repology.org/badge/vertical-allrepos/xone.svg)](https://repology.org/project/xone/versions) 60 | 61 | Feel free to package `xone` for any Linux distribution or hardware you like. 62 | Any issues regarding the packaging should be reported to the respective maintainers. 63 | 64 | ## Installation 65 | 66 | ### Prerequisites 67 | 68 | - Linux (kernel 5.13+ and headers) 69 | - DKMS 70 | - curl (for firmware download) 71 | - cabextract (for firmware extraction) 72 | 73 | ### Guide 74 | 75 | 1. Unplug your Xbox devices. 76 | 77 | 2. Clone the repository: 78 | 79 | ``` 80 | git clone https://github.com/dlundqvist/xone 81 | ``` 82 | 83 | 3. Install `xone`: 84 | 85 | ``` 86 | cd xone 87 | sudo ./install.sh 88 | ``` 89 | 90 | **NOTE:** You can use the `--release` flag to disable debug logging. 91 | 92 | 4. Download the firmware for the wireless dongle: 93 | 94 | ``` 95 | sudo xone-get-firmware.sh 96 | ``` 97 | 98 | **NOTE:** The `--skip-disclaimer` flag might be useful for scripting purposes. 99 | 100 | 5. Plug in your Xbox devices. 101 | 102 | ### Updating 103 | 104 | Make sure to completely uninstall `xone` before updating: 105 | 106 | ``` 107 | sudo ./uninstall.sh 108 | ``` 109 | 110 | ### Using Xbox 360 controllers with xone 111 | 112 | `xone` doesn't support Xbox 360 controllers at all. On top of that, `xone` needs to disable `xpad` driver to work properly, which would normally support Xbox 360 controllers. This is due to `xpad` also trying to handle Xbox One controllers, which `xone` aims to support. 113 | 114 | To fix that, there is a fork of `xpad` driver, called [`xpad-noone`](https://github.com/medusalix/xpad-noone) that has disabled support for Xbox One controllers, so it can coexist with `xone` driver. If you're using Xbox 360 controllers, it is recommended to use it to replace the standard `xpad` driver. 115 | 116 | ### Installation on Steam Deck 117 | 118 | An installation script for the Steam Deck is available [here](https://gist.github.com/SavageCore/263a3413532bc181c9bb215c8fe6c30d). It handles all the prerequisites and other quirks, along with installing `xone-noone`. 119 | 120 | You can run it by executing the following command: `wget -O /tmp/bootstrap.sh https://gist.githubusercontent.com/SavageCore/263a3413532bc181c9bb215c8fe6c30d/raw/8cfbc292c4b55612a2ebea3227911a3c3a6ae214/bootstrap.sh && sh /tmp/bootstrap.sh` 121 | 122 | ## Wireless pairing 123 | 124 | Xbox devices have to be paired to the wireless dongle. They will not automatically connect to the dongle if they have been previously plugged into a USB port or used via Bluetooth. 125 | 126 | Instructions for pairing your devices can be found [here](https://support.xbox.com/en-US/help/hardware-network/controller/connect-xbox-wireless-controller-to-pc) (see the section on *Xbox Wireless*). 127 | 128 | ## Kernel interface 129 | 130 | ### LED control 131 | 132 | The guide button LED can be controlled via `sysfs`: 133 | 134 | ``` 135 | echo 2 | sudo tee /sys/class/leds/gip*/mode 136 | echo 5 | sudo tee /sys/class/leds/gip*/brightness 137 | ``` 138 | 139 | Changing the LED in the above way is temporary, it will only last until the device disconnects. To apply these settings automatically when a device connects, you can create a new `udev` rule in `/etc/udev/rules.d/50-xone.rules` with the following content: 140 | 141 | ``` 142 | ACTION=="add", SUBSYSTEM=="leds", KERNEL=="gip*", ATTR{mode}="2", ATTR{brightness}="5" 143 | ``` 144 | 145 | Replace the wildcard (`gip*`) if you want to control the LED of a specific device. 146 | The modes and the maximum brightness can vary from device to device. 147 | 148 | ### Pairing mode 149 | 150 | The pairing mode of the dongle can be queried via `sysfs`: 151 | 152 | ``` 153 | cat /sys/bus/usb/drivers/xone-dongle/*/pairing 154 | ``` 155 | 156 | You can enable (`1`) or disable (`0`) the pairing using the following command: 157 | 158 | ``` 159 | echo 1 | sudo tee /sys/bus/usb/drivers/xone-dongle/*/pairing 160 | ``` 161 | 162 | ## Troubleshooting 163 | 164 | Uninstall the release version and install a debug build of `xone` (see installation guide). 165 | Run `sudo dmesg` to gather logs and check for any error messages related to `xone`. 166 | If `xone` is not being loaded automatically you might have to reboot your system. 167 | 168 | ### Error messages 169 | 170 | - `Direct firmware load for xow_dongle.bin failed with error -2` 171 | - Download the firmware for the wireless dongle (see installation guide). 172 | 173 | ### Input issues 174 | 175 | You can use `evtest` and `fftest` to check the input and force feedback functionality of your devices. 176 | 177 | ### Other problems 178 | 179 | Please join the [Discord server](https://discord.gg/FDQxwWk) in case of any other problems. 180 | 181 | ## License 182 | 183 | `xone` is released under the [GNU General Public License, Version 2](LICENSE). 184 | 185 | ``` 186 | Copyright (C) 2021 Severin von Wnuck-Lipinski 187 | 188 | This program is free software; you can redistribute it and/or 189 | modify it under the terms of the GNU General Public License 190 | as published by the Free Software Foundation; either version 2 191 | of the License, or (at your option) any later version. 192 | ``` 193 | -------------------------------------------------------------------------------- /auth/auth.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2023 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "auth.h" 10 | #include "crypto.h" 11 | #include "../bus/bus.h" 12 | 13 | enum gip_auth_context { 14 | GIP_AUTH_CTX_HANDSHAKE = 0x00, 15 | GIP_AUTH_CTX_CONTROL = 0x01, 16 | }; 17 | 18 | enum gip_auth_command_handshake { 19 | GIP_AUTH_CMD_HOST_HELLO = 0x01, 20 | GIP_AUTH_CMD_CLIENT_HELLO = 0x02, 21 | GIP_AUTH_CMD_CLIENT_CERTIFICATE = 0x03, 22 | GIP_AUTH_CMD_HOST_SECRET = 0x05, 23 | GIP_AUTH_CMD_HOST_FINISH = 0x07, 24 | GIP_AUTH_CMD_CLIENT_FINISH = 0x08, 25 | 26 | GIP_AUTH2_CMD_HOST_HELLO = 0x21, 27 | GIP_AUTH2_CMD_CLIENT_HELLO = 0x22, 28 | GIP_AUTH2_CMD_CLIENT_CERTIFICATE = 0x23, 29 | GIP_AUTH2_CMD_CLIENT_PUBKEY = 0x24, 30 | GIP_AUTH2_CMD_HOST_PUBKEY = 0x25, 31 | GIP_AUTH2_CMD_HOST_FINISH = 0x26, 32 | GIP_AUTH2_CMD_CLIENT_FINISH = 0x27, 33 | }; 34 | 35 | enum gip_auth_command_control { 36 | GIP_AUTH_CTRL_COMPLETE = 0x00, 37 | GIP_AUTH_CTRL_RESET = 0x01, 38 | }; 39 | 40 | enum gip_auth_option { 41 | GIP_AUTH_OPT_ACKNOWLEDGE = BIT(0), 42 | GIP_AUTH_OPT_REQUEST = BIT(1), 43 | GIP_AUTH_OPT_FROM_HOST = BIT(6), 44 | GIP_AUTH_OPT_FROM_CLIENT = BIT(6) | BIT(7), 45 | }; 46 | 47 | struct gip_auth_header_handshake { 48 | u8 context; 49 | u8 options; 50 | u8 error; 51 | u8 command; 52 | __be16 length; 53 | } __packed; 54 | 55 | struct gip_auth_header_data { 56 | u8 command; 57 | u8 version; 58 | __be16 length; 59 | } __packed; 60 | 61 | struct gip_auth_header_full { 62 | struct gip_auth_header_handshake handshake; 63 | struct gip_auth_header_data data; 64 | } __packed; 65 | 66 | struct gip_auth_header_control { 67 | u8 context; 68 | u8 control; 69 | } __packed; 70 | 71 | struct gip_auth_request { 72 | struct gip_auth_header_handshake header; 73 | 74 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 75 | } __packed; 76 | 77 | struct gip_auth_pkt_host_hello { 78 | struct gip_auth_header_full header; 79 | 80 | u8 random[GIP_AUTH_RANDOM_LEN]; 81 | u8 unknown1[4]; 82 | u8 unknown2[4]; 83 | 84 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 85 | } __packed; 86 | 87 | struct gip_auth_pkt_host_secret { 88 | struct gip_auth_header_full header; 89 | 90 | u8 encrypted_pms[GIP_AUTH_ENCRYPTED_PMS_LEN]; 91 | 92 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 93 | } __packed; 94 | 95 | struct gip_auth_pkt_host_finish { 96 | struct gip_auth_header_full header; 97 | 98 | u8 transcript[GIP_AUTH_TRANSCRIPT_LEN]; 99 | 100 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 101 | } __packed; 102 | 103 | struct gip_auth_pkt_client_hello { 104 | u8 random[GIP_AUTH_RANDOM_LEN]; 105 | u8 unknown[48]; 106 | } __packed; 107 | 108 | struct gip_auth_pkt_client_finish { 109 | u8 transcript[GIP_AUTH_TRANSCRIPT_LEN]; 110 | u8 unknown[32]; 111 | } __packed; 112 | 113 | struct gip_auth2_pkt_host_hello { 114 | struct gip_auth_header_full header; 115 | 116 | u8 random[GIP_AUTH_RANDOM_LEN]; 117 | u8 unknown[4]; 118 | 119 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 120 | } __packed; 121 | 122 | struct gip_auth2_pkt_host_pubkey { 123 | struct gip_auth_header_full header; 124 | 125 | u8 pubkey[GIP_AUTH2_PUBKEY_LEN]; 126 | 127 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 128 | } __packed; 129 | 130 | struct gip_auth2_pkt_host_finish { 131 | struct gip_auth_header_full header; 132 | 133 | u8 transcript[GIP_AUTH_TRANSCRIPT_LEN]; 134 | 135 | u8 trailer[GIP_AUTH_TRAILER_LEN]; 136 | } __packed; 137 | 138 | struct gip_auth2_pkt_client_hello { 139 | u8 random[GIP_AUTH_RANDOM_LEN]; 140 | u8 unknown1[108]; 141 | u8 unknown2[32]; 142 | } __packed; 143 | 144 | struct gip_auth2_pkt_client_cert { 145 | char header[4]; 146 | u8 unknown1[136]; 147 | char chip[32]; 148 | char revision[20]; 149 | u8 unknown2[576]; 150 | } __packed; 151 | 152 | struct gip_auth2_pkt_client_pubkey { 153 | u8 pubkey[GIP_AUTH2_PUBKEY_LEN]; 154 | u8 unknown[64]; 155 | } __packed; 156 | 157 | struct gip_auth2_pkt_client_finish { 158 | u8 transcript[GIP_AUTH_TRANSCRIPT_LEN]; 159 | u8 unknown[32]; 160 | } __packed; 161 | 162 | static int gip_auth_send_pkt(struct gip_auth *auth, 163 | enum gip_auth_command_handshake cmd, 164 | void *pkt, u16 len) 165 | { 166 | struct gip_auth_header_full *hdr = pkt; 167 | u16 data_len = len - sizeof(hdr->handshake) - GIP_AUTH_TRAILER_LEN; 168 | 169 | hdr->handshake.context = GIP_AUTH_CTX_HANDSHAKE; 170 | hdr->handshake.options = GIP_AUTH_OPT_ACKNOWLEDGE | 171 | GIP_AUTH_OPT_FROM_HOST; 172 | hdr->handshake.command = cmd; 173 | hdr->handshake.length = cpu_to_be16(data_len); 174 | 175 | hdr->data.command = cmd; 176 | hdr->data.version = cmd >= GIP_AUTH2_CMD_HOST_HELLO ? 0x02 : 0x01; 177 | hdr->data.length = cpu_to_be16(data_len - sizeof(hdr->data)); 178 | 179 | auth->last_sent_command = cmd; 180 | crypto_shash_update(auth->shash_transcript, 181 | pkt + sizeof(hdr->handshake), data_len); 182 | 183 | return gip_send_authenticate(auth->client, pkt, len, true); 184 | } 185 | 186 | static int gip_auth_request_pkt(struct gip_auth *auth, 187 | enum gip_auth_command_handshake cmd, u16 len) 188 | { 189 | struct gip_auth_request req = {}; 190 | u16 data_len = len + sizeof(struct gip_auth_header_data); 191 | 192 | req.header.context = GIP_AUTH_CTX_HANDSHAKE; 193 | req.header.options = GIP_AUTH_OPT_REQUEST | GIP_AUTH_OPT_FROM_HOST; 194 | req.header.command = cmd; 195 | req.header.length = cpu_to_be16(data_len); 196 | 197 | return gip_send_authenticate(auth->client, &req, sizeof(req), true); 198 | } 199 | 200 | static int gip_auth2_send_hello(struct gip_auth *auth) 201 | { 202 | struct gip_auth2_pkt_host_hello pkt = {}; 203 | 204 | get_random_bytes(auth->random_host, sizeof(auth->random_host)); 205 | memcpy(pkt.random, auth->random_host, sizeof(pkt.random)); 206 | 207 | return gip_auth_send_pkt(auth, GIP_AUTH2_CMD_HOST_HELLO, 208 | &pkt, sizeof(pkt)); 209 | } 210 | 211 | static int gip_auth2_handle_pkt_hello(struct gip_auth *auth, 212 | void *data, u32 len) 213 | { 214 | struct gip_auth2_pkt_client_hello *pkt = data; 215 | 216 | if (len < sizeof(*pkt)) 217 | return -EINVAL; 218 | 219 | memcpy(auth->random_client, pkt->random, sizeof(auth->random_client)); 220 | 221 | return gip_auth_request_pkt(auth, GIP_AUTH2_CMD_CLIENT_CERTIFICATE, 222 | sizeof(struct gip_auth2_pkt_client_cert)); 223 | } 224 | 225 | static int gip_auth2_handle_pkt_certificate(struct gip_auth *auth, 226 | void *data, u32 len) 227 | { 228 | struct gip_auth2_pkt_client_cert *pkt = data; 229 | 230 | if (len < sizeof(*pkt)) 231 | return -EINVAL; 232 | 233 | dev_dbg(&auth->client->dev, 234 | "%s: header=%.*s, chip=%.*s, revision=%.*s\n", __func__, 235 | (int)sizeof(pkt->header), pkt->header, 236 | (int)sizeof(pkt->chip), pkt->chip, 237 | (int)sizeof(pkt->revision), pkt->revision); 238 | 239 | return gip_auth_request_pkt(auth, GIP_AUTH2_CMD_CLIENT_PUBKEY, 240 | sizeof(struct gip_auth2_pkt_client_pubkey)); 241 | } 242 | 243 | static int gip_auth2_handle_pkt_pubkey(struct gip_auth *auth, 244 | void *data, u32 len) 245 | { 246 | struct gip_auth2_pkt_client_pubkey *pkt = data; 247 | 248 | if (len < sizeof(*pkt)) 249 | return -EINVAL; 250 | 251 | memcpy(auth->pubkey_client2, pkt->pubkey, sizeof(pkt->pubkey)); 252 | schedule_work(&auth->work_exchange_ecdh); 253 | 254 | return 0; 255 | } 256 | 257 | static void gip_auth2_exchange_ecdh(struct work_struct *work) 258 | { 259 | struct gip_auth *auth = container_of(work, typeof(*auth), 260 | work_exchange_ecdh); 261 | struct gip_auth2_pkt_host_pubkey pkt = {}; 262 | u8 random[GIP_AUTH_RANDOM_LEN * 2]; 263 | u8 secret[GIP_AUTH2_SECRET_LEN]; 264 | int err; 265 | 266 | memcpy(random, auth->random_host, sizeof(auth->random_host)); 267 | memcpy(random + sizeof(auth->random_host), auth->random_client, 268 | sizeof(auth->random_client)); 269 | 270 | err = gip_auth_compute_ecdh(auth->pubkey_client2, pkt.pubkey, 271 | sizeof(pkt.pubkey), secret); 272 | if (err) { 273 | dev_err(&auth->client->dev, "%s: compute ECDH failed: %d\n", 274 | __func__, err); 275 | return; 276 | } 277 | 278 | err = gip_auth_compute_prf(auth->shash_prf, "Master Secret", 279 | secret, sizeof(secret), 280 | random, sizeof(random), 281 | auth->master_secret, 282 | sizeof(auth->master_secret)); 283 | if (err) { 284 | dev_err(&auth->client->dev, "%s: compute PRF failed: %d\n", 285 | __func__, err); 286 | return; 287 | } 288 | 289 | err = gip_auth_send_pkt(auth, GIP_AUTH2_CMD_HOST_PUBKEY, 290 | &pkt, sizeof(pkt)); 291 | if (err) 292 | dev_err(&auth->client->dev, "%s: send pkt failed: %d\n", 293 | __func__, err); 294 | } 295 | 296 | static int gip_auth_send_pkt_hello(struct gip_auth *auth) 297 | { 298 | struct gip_auth_pkt_host_hello pkt = {}; 299 | 300 | get_random_bytes(auth->random_host, sizeof(auth->random_host)); 301 | memcpy(pkt.random, auth->random_host, sizeof(pkt.random)); 302 | 303 | return gip_auth_send_pkt(auth, GIP_AUTH_CMD_HOST_HELLO, 304 | &pkt, sizeof(pkt)); 305 | } 306 | 307 | static int gip_auth_send_pkt_finish(struct gip_auth *auth, 308 | enum gip_auth_command_handshake cmd) 309 | { 310 | struct gip_auth_pkt_host_finish pkt = {}; 311 | u8 transcript[GIP_AUTH_TRANSCRIPT_LEN]; 312 | int err; 313 | 314 | err = gip_auth_get_transcript(auth->shash_transcript, transcript); 315 | if (err) { 316 | dev_err(&auth->client->dev, "%s: get transcript failed: %d\n", 317 | __func__, err); 318 | return err; 319 | } 320 | 321 | err = gip_auth_compute_prf(auth->shash_prf, "Host Finished", 322 | auth->master_secret, 323 | sizeof(auth->master_secret), 324 | transcript, sizeof(transcript), 325 | pkt.transcript, sizeof(pkt.transcript)); 326 | if (err) { 327 | dev_err(&auth->client->dev, "%s: compute PRF failed: %d\n", 328 | __func__, err); 329 | return err; 330 | } 331 | 332 | return gip_auth_send_pkt(auth, cmd, &pkt, sizeof(pkt)); 333 | } 334 | 335 | static int gip_auth_handle_pkt_acknowledge(struct gip_auth *auth) 336 | { 337 | switch (auth->last_sent_command) { 338 | case GIP_AUTH2_CMD_HOST_HELLO: 339 | return gip_auth_request_pkt(auth, GIP_AUTH2_CMD_CLIENT_HELLO, 340 | sizeof(struct gip_auth2_pkt_client_hello)); 341 | case GIP_AUTH2_CMD_HOST_PUBKEY: 342 | return gip_auth_send_pkt_finish(auth, GIP_AUTH2_CMD_HOST_FINISH); 343 | case GIP_AUTH2_CMD_HOST_FINISH: 344 | return gip_auth_request_pkt(auth, GIP_AUTH2_CMD_CLIENT_FINISH, 345 | sizeof(struct gip_auth2_pkt_client_finish)); 346 | case GIP_AUTH_CMD_HOST_HELLO: 347 | return gip_auth_request_pkt(auth, GIP_AUTH_CMD_CLIENT_HELLO, 348 | sizeof(struct gip_auth_pkt_client_hello)); 349 | case GIP_AUTH_CMD_HOST_SECRET: 350 | return gip_auth_send_pkt_finish(auth, GIP_AUTH_CMD_HOST_FINISH); 351 | case GIP_AUTH_CMD_HOST_FINISH: 352 | return gip_auth_request_pkt(auth, GIP_AUTH_CMD_CLIENT_FINISH, 353 | sizeof(struct gip_auth_pkt_client_finish)); 354 | default: 355 | return -EPROTO; 356 | } 357 | } 358 | 359 | static int gip_auth_handle_pkt_hello(struct gip_auth *auth, 360 | void *data, u32 len) 361 | { 362 | struct gip_auth_pkt_client_hello *pkt = data; 363 | 364 | if (len < sizeof(*pkt)) 365 | return -EINVAL; 366 | 367 | memcpy(auth->random_client, pkt->random, sizeof(pkt->random)); 368 | 369 | return gip_auth_request_pkt(auth, GIP_AUTH_CMD_CLIENT_CERTIFICATE, 370 | GIP_AUTH_CERTIFICATE_MAX_LEN); 371 | } 372 | 373 | static int gip_auth_handle_pkt_certificate(struct gip_auth *auth, 374 | void *data, u32 len) 375 | { 376 | /* ASN.1 SEQUENCE (len = 0x04 + 0x010a) */ 377 | u8 asn1_seq[] = { 0x30, 0x82, 0x01, 0x0a }; 378 | int i; 379 | 380 | if (len > GIP_AUTH_CERTIFICATE_MAX_LEN) 381 | return -EINVAL; 382 | 383 | /* 384 | * Poor way of extracting a pubkey from an X.509 certificate. 385 | * The certificates issued by Microsoft do not comply with RFC 5280. 386 | * They have an empty subject and no subjectAltName. 387 | * This is explicitly forbidden by section 4.2.1.6 of the RFC. 388 | * The kernel's ASN.1 parser will fail when using x509_cert_parse. 389 | */ 390 | for (i = 0; i + sizeof(asn1_seq) <= len; i++) { 391 | if (memcmp(data + i, asn1_seq, sizeof(asn1_seq))) 392 | continue; 393 | 394 | if (i + GIP_AUTH_PUBKEY_LEN > len) 395 | return -EINVAL; 396 | 397 | memcpy(auth->pubkey_client, data + i, GIP_AUTH_PUBKEY_LEN); 398 | schedule_work(&auth->work_exchange_rsa); 399 | 400 | return 0; 401 | } 402 | 403 | return -EPROTO; 404 | } 405 | 406 | static int gip_auth_handle_pkt_finish(struct gip_auth *auth, 407 | void *data, u32 len) 408 | { 409 | struct gip_auth_pkt_client_finish *pkt = data; 410 | u8 transcript[GIP_AUTH_TRANSCRIPT_LEN]; 411 | u8 finished[GIP_AUTH_TRANSCRIPT_LEN]; 412 | int err; 413 | 414 | if (len < sizeof(*pkt)) 415 | return -EINVAL; 416 | 417 | err = gip_auth_get_transcript(auth->shash_transcript, transcript); 418 | if (err) { 419 | dev_err(&auth->client->dev, "%s: get transcript failed: %d\n", 420 | __func__, err); 421 | return err; 422 | } 423 | 424 | err = gip_auth_compute_prf(auth->shash_prf, "Device Finished", 425 | auth->master_secret, 426 | sizeof(auth->master_secret), 427 | transcript, sizeof(transcript), 428 | finished, sizeof(finished)); 429 | if (err) { 430 | dev_err(&auth->client->dev, "%s: compute PRF failed: %d\n", 431 | __func__, err); 432 | return err; 433 | } 434 | 435 | if (memcmp(pkt->transcript, finished, sizeof(finished))) { 436 | dev_err(&auth->client->dev, "%s: transcript mismatch\n", 437 | __func__); 438 | return -EPROTO; 439 | } 440 | 441 | schedule_work(&auth->work_complete); 442 | 443 | return 0; 444 | } 445 | 446 | static void gip_auth_exchange_rsa(struct work_struct *work) 447 | { 448 | struct gip_auth *auth = container_of(work, typeof(*auth), 449 | work_exchange_rsa); 450 | struct gip_auth_pkt_host_secret pkt = {}; 451 | u8 random[GIP_AUTH_RANDOM_LEN * 2]; 452 | int err; 453 | 454 | memcpy(random, auth->random_host, sizeof(auth->random_host)); 455 | memcpy(random + sizeof(auth->random_host), auth->random_client, 456 | sizeof(auth->random_client)); 457 | 458 | /* get random premaster secret */ 459 | get_random_bytes(auth->pms, sizeof(auth->pms)); 460 | 461 | err = gip_auth_encrypt_rsa(auth->pubkey_client, 462 | sizeof(auth->pubkey_client), 463 | auth->pms, sizeof(auth->pms), 464 | pkt.encrypted_pms, 465 | sizeof(pkt.encrypted_pms)); 466 | if (err) { 467 | dev_err(&auth->client->dev, "%s: encrypt RSA failed: %d\n", 468 | __func__, err); 469 | return; 470 | } 471 | 472 | err = gip_auth_compute_prf(auth->shash_prf, "Master Secret", 473 | auth->pms, sizeof(auth->pms), 474 | random, sizeof(random), 475 | auth->master_secret, 476 | sizeof(auth->master_secret)); 477 | if (err) { 478 | dev_err(&auth->client->dev, "%s: compute PRF failed: %d\n", 479 | __func__, err); 480 | return; 481 | } 482 | 483 | err = gip_auth_send_pkt(auth, GIP_AUTH_CMD_HOST_SECRET, 484 | &pkt, sizeof(pkt)); 485 | if (err) 486 | dev_err(&auth->client->dev, "%s: send pkt failed: %d\n", 487 | __func__, err); 488 | } 489 | 490 | int gip_auth_send_complete(struct gip_client *client) 491 | { 492 | struct gip_auth_header_control hdr = {}; 493 | 494 | hdr.context = GIP_AUTH_CTX_CONTROL; 495 | hdr.control = GIP_AUTH_CTRL_COMPLETE; 496 | 497 | return gip_send_authenticate(client, &hdr, sizeof(hdr), false); 498 | } 499 | EXPORT_SYMBOL_GPL(gip_auth_send_complete); 500 | 501 | static void gip_auth_complete_handshake(struct work_struct *work) 502 | { 503 | struct gip_auth *auth = container_of(work, typeof(*auth), 504 | work_complete); 505 | u8 random[GIP_AUTH_RANDOM_LEN * 2]; 506 | u8 key[GIP_AUTH_SESSION_KEY_LEN]; 507 | int err; 508 | 509 | memcpy(random, auth->random_host, sizeof(auth->random_host)); 510 | memcpy(random + sizeof(auth->random_host), auth->random_client, 511 | sizeof(auth->random_client)); 512 | 513 | err = gip_auth_compute_prf(auth->shash_prf, 514 | "EXPORTER DAWN data channel session key for controller", 515 | auth->master_secret, 516 | sizeof(auth->master_secret), 517 | random, sizeof(random), 518 | key, sizeof(key)); 519 | if (err) { 520 | dev_err(&auth->client->dev, "%s: compute PRF failed: %d\n", 521 | __func__, err); 522 | return; 523 | } 524 | 525 | dev_dbg(&auth->client->dev, "%s: key=%*phD\n", __func__, 526 | (int)sizeof(key), key); 527 | 528 | err = gip_auth_send_complete(auth->client); 529 | if (err) { 530 | dev_err(&auth->client->dev, "%s: send complete failed: %d\n", 531 | __func__, err); 532 | return; 533 | } 534 | 535 | err = gip_set_encryption_key(auth->client, key, sizeof(key)); 536 | if (err) 537 | dev_err(&auth->client->dev, 538 | "%s: set encryption key failed: %d\n", __func__, err); 539 | } 540 | 541 | static int gip_auth_dispatch_pkt(struct gip_auth *auth, 542 | enum gip_auth_command_handshake cmd, 543 | void *data, u32 len) 544 | { 545 | switch (cmd) { 546 | case GIP_AUTH2_CMD_CLIENT_HELLO: 547 | return gip_auth2_handle_pkt_hello(auth, data, len); 548 | case GIP_AUTH2_CMD_CLIENT_CERTIFICATE: 549 | return gip_auth2_handle_pkt_certificate(auth, data, len); 550 | case GIP_AUTH2_CMD_CLIENT_PUBKEY: 551 | return gip_auth2_handle_pkt_pubkey(auth, data, len); 552 | case GIP_AUTH2_CMD_CLIENT_FINISH: 553 | return gip_auth_handle_pkt_finish(auth, data, len); 554 | case GIP_AUTH_CMD_CLIENT_HELLO: 555 | return gip_auth_handle_pkt_hello(auth, data, len); 556 | case GIP_AUTH_CMD_CLIENT_CERTIFICATE: 557 | return gip_auth_handle_pkt_certificate(auth, data, len); 558 | case GIP_AUTH_CMD_CLIENT_FINISH: 559 | return gip_auth_handle_pkt_finish(auth, data, len); 560 | default: 561 | return -EPROTO; 562 | } 563 | } 564 | 565 | static int gip_auth_process_pkt_data(struct gip_auth *auth, void *data, u32 len) 566 | { 567 | struct gip_auth_header_full *hdr = data; 568 | int err; 569 | 570 | if (len < sizeof(*hdr)) 571 | return -EINVAL; 572 | 573 | /* client uses auth v2 */ 574 | if (hdr->handshake.command != hdr->data.command) { 575 | /* reset transcript hash and restart handshake */ 576 | dev_dbg(&auth->client->dev, "%s: protocol upgrade\n", __func__); 577 | crypto_shash_init(auth->shash_transcript); 578 | return gip_auth2_send_hello(auth); 579 | } 580 | 581 | err = gip_auth_dispatch_pkt(auth, hdr->data.command, 582 | data + sizeof(*hdr), len - sizeof(*hdr)); 583 | if (err) 584 | return err; 585 | 586 | return crypto_shash_update(auth->shash_transcript, 587 | data + sizeof(hdr->handshake), 588 | len - sizeof(hdr->handshake)); 589 | } 590 | 591 | int gip_auth_process_pkt(struct gip_auth *auth, void *data, u32 len) 592 | { 593 | struct gip_auth_header_handshake *hdr = data; 594 | 595 | if (!auth->client) 596 | return -ENODEV; 597 | 598 | if (len < sizeof(*hdr)) 599 | return -EINVAL; 600 | 601 | if (hdr->error) 602 | return -EPROTO; 603 | 604 | if (hdr->options & GIP_AUTH_OPT_ACKNOWLEDGE) { 605 | if (hdr->command == 0x01) 606 | return gip_auth_handle_pkt_acknowledge(auth); 607 | 608 | dev_err(&auth->client->dev, "%s: handshake failed: 0x%02x\n", 609 | __func__, hdr->command); 610 | return -EPROTO; 611 | } 612 | 613 | return gip_auth_process_pkt_data(auth, data, len); 614 | } 615 | EXPORT_SYMBOL_GPL(gip_auth_process_pkt); 616 | 617 | static void gip_auth_release(void *res) 618 | { 619 | struct gip_auth *auth = res; 620 | 621 | cancel_work_sync(&auth->work_exchange_rsa); 622 | cancel_work_sync(&auth->work_exchange_ecdh); 623 | cancel_work_sync(&auth->work_complete); 624 | 625 | crypto_free_shash(auth->shash_transcript->tfm); 626 | crypto_free_shash(auth->shash_prf->tfm); 627 | kfree(auth->shash_transcript); 628 | kfree(auth->shash_prf); 629 | 630 | auth->client = NULL; 631 | auth->shash_transcript = NULL; 632 | auth->shash_prf = NULL; 633 | } 634 | 635 | int gip_auth_start_handshake(struct gip_auth *auth, struct gip_client *client) 636 | { 637 | struct shash_desc *shash_transcript, *shash_prf; 638 | int err; 639 | 640 | shash_transcript = gip_auth_alloc_shash("sha256"); 641 | if (IS_ERR(shash_transcript)) 642 | return PTR_ERR(shash_transcript); 643 | 644 | shash_prf = gip_auth_alloc_shash("hmac(sha256)"); 645 | if (IS_ERR(shash_prf)) { 646 | crypto_free_shash(shash_transcript->tfm); 647 | kfree(shash_transcript); 648 | return PTR_ERR(shash_prf); 649 | } 650 | 651 | auth->client = client; 652 | auth->shash_transcript = shash_transcript; 653 | auth->shash_prf = shash_prf; 654 | 655 | INIT_WORK(&auth->work_exchange_rsa, gip_auth_exchange_rsa); 656 | INIT_WORK(&auth->work_exchange_ecdh, gip_auth2_exchange_ecdh); 657 | INIT_WORK(&auth->work_complete, gip_auth_complete_handshake); 658 | 659 | err = devm_add_action_or_reset(&client->dev, gip_auth_release, auth); 660 | if (err) 661 | return err; 662 | 663 | return gip_auth_send_pkt_hello(auth); 664 | } 665 | EXPORT_SYMBOL_GPL(gip_auth_start_handshake); 666 | -------------------------------------------------------------------------------- /auth/auth.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2023 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | /* trailer is required for v1 clients */ 12 | #define GIP_AUTH_TRAILER_LEN 8 13 | #define GIP_AUTH_RANDOM_LEN 32 14 | #define GIP_AUTH_CERTIFICATE_MAX_LEN 1024 15 | #define GIP_AUTH_PUBKEY_LEN 270 16 | #define GIP_AUTH_SECRET_LEN 48 17 | #define GIP_AUTH_ENCRYPTED_PMS_LEN 256 18 | #define GIP_AUTH_TRANSCRIPT_LEN 32 19 | #define GIP_AUTH_SESSION_KEY_LEN 16 20 | 21 | #define GIP_AUTH2_PUBKEY_LEN 64 22 | #define GIP_AUTH2_SECRET_LEN 32 23 | 24 | struct gip_client; 25 | 26 | struct gip_auth { 27 | struct gip_client *client; 28 | 29 | struct shash_desc *shash_transcript; 30 | struct shash_desc *shash_prf; 31 | 32 | struct work_struct work_exchange_rsa; 33 | struct work_struct work_exchange_ecdh; 34 | struct work_struct work_complete; 35 | 36 | u8 last_sent_command; 37 | 38 | u8 random_host[GIP_AUTH_RANDOM_LEN]; 39 | u8 random_client[GIP_AUTH_RANDOM_LEN]; 40 | 41 | u8 pubkey_client[GIP_AUTH_PUBKEY_LEN]; 42 | u8 pubkey_client2[GIP_AUTH2_PUBKEY_LEN]; 43 | 44 | u8 pms[GIP_AUTH_SECRET_LEN]; 45 | u8 master_secret[GIP_AUTH_SECRET_LEN]; 46 | }; 47 | 48 | int gip_auth_send_complete(struct gip_client *client); 49 | int gip_auth_process_pkt(struct gip_auth *auth, void *data, u32 len); 50 | int gip_auth_start_handshake(struct gip_auth *auth, struct gip_client *client); 51 | -------------------------------------------------------------------------------- /auth/crypto.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2023 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "crypto.h" 14 | 15 | #define GIP_AUTH_ECDH_SECRET_LEN 32 16 | 17 | struct shash_desc *gip_auth_alloc_shash(const char *alg) 18 | { 19 | struct crypto_shash *tfm; 20 | struct shash_desc *desc; 21 | 22 | tfm = crypto_alloc_shash(alg, 0, 0); 23 | if (IS_ERR(tfm)) 24 | return ERR_CAST(tfm); 25 | 26 | desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); 27 | if (!desc) { 28 | crypto_free_shash(tfm); 29 | return ERR_PTR(-ENOMEM); 30 | } 31 | 32 | desc->tfm = tfm; 33 | crypto_shash_init(desc); 34 | 35 | return desc; 36 | } 37 | 38 | int gip_auth_get_transcript(struct shash_desc *desc, void *transcript) 39 | { 40 | struct sha256_state state; 41 | int err; 42 | 43 | err = crypto_shash_export(desc, &state); 44 | if (err) 45 | return err; 46 | 47 | err = crypto_shash_final(desc, transcript); 48 | if (err) 49 | return err; 50 | 51 | return crypto_shash_import(desc, &state); 52 | } 53 | 54 | int gip_auth_compute_prf(struct shash_desc *desc, const char *label, 55 | u8 *key, int key_len, 56 | u8 *seed, int seed_len, 57 | u8 *out, int out_len) 58 | { 59 | u8 hash[SHA256_DIGEST_SIZE], hash_out[SHA256_DIGEST_SIZE]; 60 | int err; 61 | 62 | err = crypto_shash_setkey(desc->tfm, key, key_len); 63 | if (err) 64 | return err; 65 | 66 | crypto_shash_init(desc); 67 | crypto_shash_update(desc, label, strlen(label)); 68 | crypto_shash_update(desc, seed, seed_len); 69 | crypto_shash_final(desc, hash); 70 | 71 | while (out_len > 0) { 72 | crypto_shash_init(desc); 73 | crypto_shash_update(desc, hash, sizeof(hash)); 74 | crypto_shash_update(desc, label, strlen(label)); 75 | crypto_shash_update(desc, seed, seed_len); 76 | crypto_shash_final(desc, hash_out); 77 | 78 | memcpy(out, hash_out, min_t(int, out_len, sizeof(hash))); 79 | out += sizeof(hash); 80 | out_len -= sizeof(hash); 81 | 82 | crypto_shash_digest(desc, hash, sizeof(hash), hash); 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | int gip_auth_encrypt_rsa(u8 *key, int key_len, 89 | u8 *in, int in_len, 90 | u8 *out, int out_len) 91 | { 92 | struct crypto_akcipher *tfm; 93 | struct akcipher_request *req; 94 | struct scatterlist src, dest; 95 | DECLARE_CRYPTO_WAIT(wait); 96 | u8 *buf; 97 | int err; 98 | 99 | buf = kzalloc(out_len, GFP_KERNEL); 100 | if (!buf) 101 | return -ENOMEM; 102 | 103 | tfm = crypto_alloc_akcipher("pkcs1pad(rsa)", 0, 0); 104 | if (IS_ERR(tfm)) { 105 | err = PTR_ERR(tfm); 106 | goto err_free_buf; 107 | } 108 | 109 | err = crypto_akcipher_set_pub_key(tfm, key, key_len); 110 | if (err) 111 | goto err_free_tfm; 112 | 113 | req = akcipher_request_alloc(tfm, GFP_KERNEL); 114 | if (!req) { 115 | err = -ENOMEM; 116 | goto err_free_tfm; 117 | } 118 | 119 | sg_init_one(&src, in, in_len); 120 | sg_init_one(&dest, buf, out_len); 121 | 122 | akcipher_request_set_crypt(req, &src, &dest, in_len, out_len); 123 | akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 124 | crypto_req_done, &wait); 125 | err = crypto_wait_req(crypto_akcipher_encrypt(req), &wait); 126 | if (!err) 127 | memcpy(out, buf, out_len); 128 | 129 | akcipher_request_free(req); 130 | 131 | err_free_tfm: 132 | crypto_free_akcipher(tfm); 133 | err_free_buf: 134 | kfree(buf); 135 | 136 | return err; 137 | } 138 | 139 | static int gip_auth_ecdh_get_pubkey(struct crypto_kpp *tfm, 140 | u8 *out, int len) 141 | { 142 | struct kpp_request *req; 143 | struct scatterlist dest; 144 | struct ecdh key = {}; 145 | DECLARE_CRYPTO_WAIT(wait); 146 | void *privkey, *pubkey; 147 | unsigned int privkey_len; 148 | int err; 149 | 150 | privkey_len = crypto_ecdh_key_len(&key); 151 | privkey = kzalloc(privkey_len, GFP_KERNEL); 152 | if (!privkey) 153 | return -ENOMEM; 154 | 155 | pubkey = kzalloc(len, GFP_KERNEL); 156 | if (!pubkey) 157 | goto err_free_privkey; 158 | 159 | /* generate private key */ 160 | err = crypto_ecdh_encode_key(privkey, privkey_len, &key); 161 | if (err) 162 | goto err_free_pubkey; 163 | 164 | err = crypto_kpp_set_secret(tfm, privkey, privkey_len); 165 | if (err) 166 | goto err_free_pubkey; 167 | 168 | req = kpp_request_alloc(tfm, GFP_KERNEL); 169 | if (!req) { 170 | err = -ENOMEM; 171 | goto err_free_pubkey; 172 | } 173 | 174 | sg_init_one(&dest, pubkey, len); 175 | 176 | kpp_request_set_input(req, NULL, 0); 177 | kpp_request_set_output(req, &dest, len); 178 | kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 179 | crypto_req_done, &wait); 180 | err = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); 181 | if (!err) 182 | memcpy(out, pubkey, len); 183 | 184 | kpp_request_free(req); 185 | 186 | err_free_pubkey: 187 | kfree(pubkey); 188 | err_free_privkey: 189 | kfree(privkey); 190 | 191 | return err; 192 | } 193 | 194 | static int gip_auth_ecdh_get_secret(struct crypto_kpp *tfm, 195 | u8 *pubkey, int pubkey_len, 196 | u8 *secret, int secret_len) 197 | { 198 | struct kpp_request *req; 199 | struct scatterlist src, dest; 200 | DECLARE_CRYPTO_WAIT(wait); 201 | int err; 202 | 203 | req = kpp_request_alloc(tfm, GFP_KERNEL); 204 | if (!req) 205 | return -ENOMEM; 206 | 207 | sg_init_one(&src, pubkey, pubkey_len); 208 | sg_init_one(&dest, secret, secret_len); 209 | 210 | kpp_request_set_input(req, &src, pubkey_len); 211 | kpp_request_set_output(req, &dest, secret_len); 212 | kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 213 | crypto_req_done, &wait); 214 | err = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); 215 | 216 | kpp_request_free(req); 217 | 218 | return err; 219 | } 220 | 221 | int gip_auth_compute_ecdh(u8 *pubkey_in, u8 *pubkey_out, 222 | int pubkey_len, u8 *secret_hash) 223 | { 224 | struct crypto_kpp *tfm_ecdh; 225 | struct crypto_shash *tfm_sha; 226 | u8 *secret; 227 | int err; 228 | 229 | secret = kzalloc(GIP_AUTH_ECDH_SECRET_LEN, GFP_KERNEL); 230 | if (!secret) 231 | return -ENOMEM; 232 | 233 | tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0); 234 | if (IS_ERR(tfm_ecdh)) { 235 | err = PTR_ERR(tfm_ecdh); 236 | goto err_free_secret; 237 | } 238 | 239 | tfm_sha = crypto_alloc_shash("sha256", 0, 0); 240 | if (IS_ERR(tfm_sha)) { 241 | err = PTR_ERR(tfm_sha); 242 | goto err_free_ecdh; 243 | } 244 | 245 | err = gip_auth_ecdh_get_pubkey(tfm_ecdh, pubkey_out, pubkey_len); 246 | if (err) 247 | goto err_free_sha; 248 | 249 | err = gip_auth_ecdh_get_secret(tfm_ecdh, pubkey_in, pubkey_len, 250 | secret, GIP_AUTH_ECDH_SECRET_LEN); 251 | if (err) 252 | goto err_free_sha; 253 | 254 | crypto_shash_tfm_digest(tfm_sha, secret, GIP_AUTH_ECDH_SECRET_LEN, 255 | secret_hash); 256 | 257 | err_free_sha: 258 | crypto_free_shash(tfm_sha); 259 | err_free_ecdh: 260 | crypto_free_kpp(tfm_ecdh); 261 | err_free_secret: 262 | kfree(secret); 263 | 264 | return err; 265 | } 266 | -------------------------------------------------------------------------------- /auth/crypto.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2023 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | struct shash_desc *gip_auth_alloc_shash(const char *alg); 11 | int gip_auth_get_transcript(struct shash_desc *desc, void *transcript); 12 | int gip_auth_compute_prf(struct shash_desc *desc, const char *label, 13 | u8 *key, int key_len, 14 | u8 *seed, int seed_len, 15 | u8 *out, int out_len); 16 | 17 | int gip_auth_encrypt_rsa(u8 *key, int key_len, 18 | u8 *in, int in_len, 19 | u8 *out, int out_len); 20 | int gip_auth_compute_ecdh(u8 *pubkey_in, u8 *pubkey_out, 21 | int pubkey_len, u8 *secret_hash); 22 | -------------------------------------------------------------------------------- /bus/bus.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "bus.h" 12 | 13 | #define to_gip_adapter(d) container_of(d, struct gip_adapter, dev) 14 | #define to_gip_client(d) container_of(d, struct gip_client, dev) 15 | #define to_gip_driver(d) container_of(d, struct gip_driver, drv) 16 | 17 | static DEFINE_IDA(gip_adapter_ida); 18 | 19 | static void gip_adapter_release(struct device *dev) 20 | { 21 | kfree(to_gip_adapter(dev)); 22 | } 23 | 24 | static struct device_type gip_adapter_type = { 25 | .release = gip_adapter_release, 26 | }; 27 | 28 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) 29 | static int gip_client_uevent(struct device *dev, struct kobj_uevent_env *env) 30 | #else 31 | static int gip_client_uevent(const struct device *dev, 32 | struct kobj_uevent_env *env) 33 | #endif 34 | { 35 | struct gip_client *client = to_gip_client(dev); 36 | struct gip_classes *classes = client->classes; 37 | 38 | if (!classes || !classes->count) 39 | return -EINVAL; 40 | 41 | return add_uevent_var(env, "MODALIAS=gip:%s", classes->strings[0]); 42 | } 43 | 44 | static void gip_client_release(struct device *dev) 45 | { 46 | struct gip_client *client = to_gip_client(dev); 47 | 48 | gip_free_client_info(client); 49 | kfree(client->chunk_buf_out); 50 | kfree(client->chunk_buf_in); 51 | kfree(client); 52 | } 53 | 54 | static struct device_type gip_client_type = { 55 | .uevent = gip_client_uevent, 56 | .release = gip_client_release, 57 | }; 58 | 59 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0) 60 | static int gip_bus_match(struct device *dev, struct device_driver *driver) 61 | #else 62 | static int gip_bus_match(struct device *dev, const struct device_driver *driver) 63 | #endif 64 | { 65 | struct gip_client *client; 66 | struct gip_driver *drv; 67 | int i; 68 | 69 | if (dev->type != &gip_client_type) 70 | return false; 71 | 72 | client = to_gip_client(dev); 73 | drv = to_gip_driver(driver); 74 | 75 | for (i = 0; i < client->classes->count; i++) 76 | if (!strcmp(client->classes->strings[i], drv->class)) 77 | return true; 78 | 79 | return false; 80 | } 81 | 82 | static int gip_bus_probe(struct device *dev) 83 | { 84 | struct gip_client *client = to_gip_client(dev); 85 | struct gip_driver *drv = to_gip_driver(dev->driver); 86 | int err = 0; 87 | 88 | if (down_interruptible(&client->drv_lock)) 89 | return -EINTR; 90 | 91 | if (!client->drv) { 92 | err = drv->probe(client); 93 | if (!err) 94 | client->drv = drv; 95 | } 96 | 97 | up(&client->drv_lock); 98 | 99 | return err; 100 | } 101 | 102 | static void gip_bus_remove(struct device *dev) 103 | { 104 | struct gip_client *client = to_gip_client(dev); 105 | struct gip_driver *drv; 106 | 107 | down(&client->drv_lock); 108 | 109 | drv = client->drv; 110 | if (drv) { 111 | client->drv = NULL; 112 | if (drv->remove) 113 | drv->remove(client); 114 | } 115 | 116 | up(&client->drv_lock); 117 | } 118 | 119 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 13, 0) 120 | static int gip_bus_remove_compat(struct device *dev) 121 | { 122 | gip_bus_remove(dev); 123 | 124 | return 0; 125 | } 126 | #endif 127 | 128 | static struct bus_type gip_bus_type = { 129 | .name = "xone-gip", 130 | .match = gip_bus_match, 131 | .probe = gip_bus_probe, 132 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 13, 0) 133 | .remove = gip_bus_remove_compat, 134 | #else 135 | .remove = gip_bus_remove, 136 | #endif 137 | }; 138 | 139 | struct gip_adapter *gip_create_adapter(struct device *parent, 140 | struct gip_adapter_ops *ops, 141 | int audio_pkts) 142 | { 143 | struct gip_adapter *adap; 144 | int err; 145 | 146 | adap = kzalloc(sizeof(*adap), GFP_KERNEL); 147 | if (!adap) 148 | return ERR_PTR(-ENOMEM); 149 | 150 | adap->id = ida_simple_get(&gip_adapter_ida, 0, 0, GFP_KERNEL); 151 | if (adap->id < 0) { 152 | err = adap->id; 153 | goto err_put_device; 154 | } 155 | 156 | adap->clients_wq = alloc_ordered_workqueue("gip%d", 0, adap->id); 157 | if (!adap->clients_wq) { 158 | err = -ENOMEM; 159 | goto err_remove_ida; 160 | } 161 | 162 | adap->dev.parent = parent; 163 | adap->dev.type = &gip_adapter_type; 164 | adap->dev.bus = &gip_bus_type; 165 | adap->ops = ops; 166 | adap->audio_packet_count = audio_pkts; 167 | dev_set_name(&adap->dev, "gip%d", adap->id); 168 | spin_lock_init(&adap->send_lock); 169 | 170 | err = device_register(&adap->dev); 171 | if (err) 172 | goto err_destroy_queue; 173 | 174 | dev_dbg(&adap->dev, "%s: registered\n", __func__); 175 | 176 | return adap; 177 | 178 | err_destroy_queue: 179 | destroy_workqueue(adap->clients_wq); 180 | err_remove_ida: 181 | ida_simple_remove(&gip_adapter_ida, adap->id); 182 | err_put_device: 183 | put_device(&adap->dev); 184 | 185 | return ERR_PTR(err); 186 | } 187 | EXPORT_SYMBOL_GPL(gip_create_adapter); 188 | 189 | int gip_power_off_adapter(struct gip_adapter *adap) 190 | { 191 | struct gip_client *client = adap->clients[0]; 192 | 193 | if (!client) 194 | return 0; 195 | 196 | /* power off main client */ 197 | return gip_set_power_mode(client, GIP_PWR_OFF); 198 | } 199 | EXPORT_SYMBOL_GPL(gip_power_off_adapter); 200 | 201 | void gip_destroy_adapter(struct gip_adapter *adap) 202 | { 203 | struct gip_client *client; 204 | int i; 205 | 206 | /* ensure all state changes have been processed */ 207 | flush_workqueue(adap->clients_wq); 208 | 209 | for (i = GIP_MAX_CLIENTS - 1; i >= 0; i--) { 210 | client = adap->clients[i]; 211 | if (!client || !device_is_registered(&client->dev)) 212 | continue; 213 | 214 | device_unregister(&client->dev); 215 | } 216 | 217 | ida_simple_remove(&gip_adapter_ida, adap->id); 218 | destroy_workqueue(adap->clients_wq); 219 | 220 | dev_dbg(&adap->dev, "%s: unregistered\n", __func__); 221 | device_unregister(&adap->dev); 222 | } 223 | EXPORT_SYMBOL_GPL(gip_destroy_adapter); 224 | 225 | static void gip_register_client(struct work_struct *work) 226 | { 227 | struct gip_client *client = container_of(work, typeof(*client), 228 | work_register); 229 | int err; 230 | 231 | client->dev.parent = &client->adapter->dev; 232 | client->dev.type = &gip_client_type; 233 | client->dev.bus = &gip_bus_type; 234 | sema_init(&client->drv_lock, 1); 235 | dev_set_name(&client->dev, "gip%d.%u", client->adapter->id, client->id); 236 | 237 | err = device_register(&client->dev); 238 | if (err) 239 | dev_err(&client->dev, "%s: register failed: %d\n", 240 | __func__, err); 241 | else 242 | dev_dbg(&client->dev, "%s: registered\n", __func__); 243 | } 244 | 245 | static void gip_unregister_client(struct work_struct *work) 246 | { 247 | struct gip_client *client = container_of(work, typeof(*client), 248 | work_unregister); 249 | 250 | if (!device_is_registered(&client->dev)) 251 | return; 252 | 253 | dev_dbg(&client->dev, "%s: unregistered\n", __func__); 254 | device_unregister(&client->dev); 255 | } 256 | 257 | struct gip_client *gip_get_client(struct gip_adapter *adap, u8 id) 258 | { 259 | struct gip_client *client; 260 | 261 | client = adap->clients[id]; 262 | if (client) 263 | return client; 264 | 265 | client = kzalloc(sizeof(*client), GFP_ATOMIC); 266 | if (!client) 267 | return ERR_PTR(-ENOMEM); 268 | 269 | client->id = id; 270 | client->adapter = adap; 271 | sema_init(&client->drv_lock, 1); 272 | INIT_WORK(&client->work_register, gip_register_client); 273 | INIT_WORK(&client->work_unregister, gip_unregister_client); 274 | 275 | adap->clients[id] = client; 276 | 277 | dev_dbg(&client->adapter->dev, "%s: initialized client %u\n", 278 | __func__, id); 279 | 280 | return client; 281 | } 282 | 283 | void gip_add_client(struct gip_client *client) 284 | { 285 | queue_work(client->adapter->clients_wq, &client->work_register); 286 | } 287 | 288 | void gip_remove_client(struct gip_client *client) 289 | { 290 | client->adapter->clients[client->id] = NULL; 291 | queue_work(client->adapter->clients_wq, &client->work_unregister); 292 | } 293 | 294 | void gip_free_client_info(struct gip_client *client) 295 | { 296 | int i; 297 | 298 | kfree(client->client_commands); 299 | kfree(client->firmware_versions); 300 | kfree(client->audio_formats); 301 | kfree(client->capabilities_out); 302 | kfree(client->capabilities_in); 303 | 304 | if (client->classes) 305 | for (i = 0; i < client->classes->count; i++) 306 | kfree(client->classes->strings[i]); 307 | 308 | kfree(client->classes); 309 | kfree(client->interfaces); 310 | kfree(client->hid_descriptor); 311 | 312 | client->client_commands = NULL; 313 | client->audio_formats = NULL; 314 | client->capabilities_out = NULL; 315 | client->capabilities_in = NULL; 316 | client->classes = NULL; 317 | client->interfaces = NULL; 318 | client->hid_descriptor = NULL; 319 | } 320 | 321 | int __gip_register_driver(struct gip_driver *drv, struct module *owner, 322 | const char *mod_name) 323 | { 324 | drv->drv.name = drv->name; 325 | drv->drv.bus = &gip_bus_type; 326 | drv->drv.owner = owner; 327 | drv->drv.mod_name = mod_name; 328 | 329 | return driver_register(&drv->drv); 330 | } 331 | EXPORT_SYMBOL_GPL(__gip_register_driver); 332 | 333 | void gip_unregister_driver(struct gip_driver *drv) 334 | { 335 | driver_unregister(&drv->drv); 336 | } 337 | EXPORT_SYMBOL_GPL(gip_unregister_driver); 338 | 339 | static int __init gip_bus_init(void) 340 | { 341 | return bus_register(&gip_bus_type); 342 | } 343 | 344 | static void __exit gip_bus_exit(void) 345 | { 346 | bus_unregister(&gip_bus_type); 347 | } 348 | 349 | module_init(gip_bus_init); 350 | module_exit(gip_bus_exit); 351 | 352 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 353 | MODULE_DESCRIPTION("xone GIP driver"); 354 | MODULE_VERSION("#VERSION#"); 355 | MODULE_LICENSE("GPL"); 356 | -------------------------------------------------------------------------------- /bus/bus.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "protocol.h" 13 | 14 | #define GIP_MAX_CLIENTS 16 15 | 16 | #define gip_register_driver(drv) \ 17 | __gip_register_driver(drv, THIS_MODULE, KBUILD_MODNAME) 18 | 19 | #define module_gip_driver(drv) \ 20 | module_driver(drv, gip_register_driver, gip_unregister_driver) 21 | 22 | struct gip_adapter_buffer { 23 | enum gip_adapter_buffer_type { 24 | GIP_BUF_DATA, 25 | GIP_BUF_AUDIO, 26 | } type; 27 | 28 | void *context; 29 | void *data; 30 | int length; 31 | }; 32 | 33 | struct gip_adapter_ops { 34 | int (*get_buffer)(struct gip_adapter *adap, 35 | struct gip_adapter_buffer *buf); 36 | int (*submit_buffer)(struct gip_adapter *adap, 37 | struct gip_adapter_buffer *buf); 38 | int (*set_encryption_key)(struct gip_adapter *adap, u8 *key, int len); 39 | int (*enable_audio)(struct gip_adapter *adap); 40 | int (*init_audio_in)(struct gip_adapter *adap); 41 | int (*init_audio_out)(struct gip_adapter *adap, int pkt_len); 42 | int (*disable_audio)(struct gip_adapter *adap); 43 | }; 44 | 45 | struct gip_adapter { 46 | struct device dev; 47 | int id; 48 | 49 | struct gip_adapter_ops *ops; 50 | int audio_packet_count; 51 | 52 | struct gip_client *clients[GIP_MAX_CLIENTS]; 53 | struct workqueue_struct *clients_wq; 54 | 55 | /* serializes access to data sequence number */ 56 | spinlock_t send_lock; 57 | 58 | u8 data_sequence; 59 | u8 audio_sequence; 60 | }; 61 | 62 | struct gip_client { 63 | struct device dev; 64 | u8 id; 65 | 66 | struct gip_adapter *adapter; 67 | struct gip_driver *drv; 68 | struct semaphore drv_lock; 69 | 70 | struct work_struct work_register; 71 | struct work_struct work_unregister; 72 | 73 | struct gip_chunk_buffer *chunk_buf_out; 74 | struct gip_chunk_buffer *chunk_buf_in; 75 | struct gip_hardware hardware; 76 | 77 | struct gip_info_element *client_commands; 78 | struct gip_info_element *firmware_versions; 79 | struct gip_info_element *audio_formats; 80 | struct gip_info_element *capabilities_out; 81 | struct gip_info_element *capabilities_in; 82 | struct gip_classes *classes; 83 | struct gip_info_element *interfaces; 84 | struct gip_info_element *hid_descriptor; 85 | 86 | struct gip_audio_config audio_config_in; 87 | struct gip_audio_config audio_config_out; 88 | }; 89 | 90 | struct gip_driver_ops { 91 | int (*battery)(struct gip_client *client, 92 | enum gip_battery_type type, 93 | enum gip_battery_level level); 94 | int (*authenticate)(struct gip_client *client, void *data, u32 len); 95 | int (*guide_button)(struct gip_client *client, bool down); 96 | int (*audio_ready)(struct gip_client *client); 97 | int (*audio_volume)(struct gip_client *client, u8 in, u8 out); 98 | int (*hid_report)(struct gip_client *client, void *data, u32 len); 99 | int (*input)(struct gip_client *client, void *data, u32 len); 100 | int (*audio_samples)(struct gip_client *client, void *data, u32 len); 101 | }; 102 | 103 | struct gip_driver { 104 | struct device_driver drv; 105 | const char *name; 106 | const char *class; 107 | 108 | struct gip_driver_ops ops; 109 | 110 | int (*probe)(struct gip_client *client); 111 | void (*remove)(struct gip_client *client); 112 | }; 113 | 114 | struct gip_adapter *gip_create_adapter(struct device *parent, 115 | struct gip_adapter_ops *ops, 116 | int audio_pkts); 117 | int gip_power_off_adapter(struct gip_adapter *adap); 118 | void gip_destroy_adapter(struct gip_adapter *adap); 119 | 120 | struct gip_client *gip_get_client(struct gip_adapter *adap, u8 id); 121 | void gip_add_client(struct gip_client *client); 122 | void gip_remove_client(struct gip_client *client); 123 | void gip_free_client_info(struct gip_client *client); 124 | 125 | int __gip_register_driver(struct gip_driver *drv, struct module *owner, 126 | const char *mod_name); 127 | void gip_unregister_driver(struct gip_driver *drv); 128 | -------------------------------------------------------------------------------- /bus/protocol.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #define GIP_VID_MICROSOFT 0x045e 12 | 13 | /* time between audio packets in ms */ 14 | #define GIP_AUDIO_INTERVAL 8 15 | 16 | enum gip_battery_type { 17 | GIP_BATT_TYPE_NONE = 0x00, 18 | GIP_BATT_TYPE_STANDARD = 0x01, 19 | GIP_BATT_TYPE_KIT = 0x02, 20 | }; 21 | 22 | enum gip_battery_level { 23 | GIP_BATT_LEVEL_LOW = 0x00, 24 | GIP_BATT_LEVEL_NORMAL = 0x01, 25 | GIP_BATT_LEVEL_HIGH = 0x02, 26 | GIP_BATT_LEVEL_FULL = 0x03, 27 | }; 28 | 29 | enum gip_power_mode { 30 | GIP_PWR_ON = 0x00, 31 | GIP_PWR_SLEEP = 0x01, 32 | GIP_PWR_OFF = 0x04, 33 | GIP_PWR_RESET = 0x07, 34 | }; 35 | 36 | enum gip_audio_format { 37 | GIP_AUD_FORMAT_16KHZ_MONO = 0x05, 38 | GIP_AUD_FORMAT_24KHZ_MONO = 0x09, 39 | GIP_AUD_FORMAT_48KHZ_STEREO = 0x10, 40 | }; 41 | 42 | enum gip_audio_format_chat { 43 | GIP_AUD_FORMAT_CHAT_24KHZ = 0x04, 44 | GIP_AUD_FORMAT_CHAT_16KHZ = 0x05, 45 | }; 46 | 47 | enum gip_led_mode { 48 | GIP_LED_OFF = 0x00, 49 | GIP_LED_ON = 0x01, 50 | GIP_LED_BLINK_FAST = 0x02, 51 | GIP_LED_BLINK_NORMAL = 0x03, 52 | GIP_LED_BLINK_SLOW = 0x04, 53 | GIP_LED_FADE_SLOW = 0x08, 54 | GIP_LED_FADE_FAST = 0x09, 55 | }; 56 | 57 | struct gip_header { 58 | u8 command; 59 | u8 options; 60 | u8 sequence; 61 | u32 packet_length; 62 | u32 chunk_offset; 63 | }; 64 | 65 | struct gip_chunk_buffer { 66 | struct gip_header header; 67 | u32 length; 68 | u8 data[]; 69 | }; 70 | 71 | struct gip_hardware { 72 | u16 vendor; 73 | u16 product; 74 | u16 version; 75 | }; 76 | 77 | struct gip_info_element { 78 | u8 count; 79 | u8 data[]; 80 | }; 81 | 82 | struct gip_audio_config { 83 | enum gip_audio_format format; 84 | 85 | int channels; 86 | int sample_rate; 87 | 88 | int buffer_size; 89 | int fragment_size; 90 | int packet_size; 91 | }; 92 | 93 | struct gip_classes { 94 | u8 count; 95 | const char *strings[]; 96 | }; 97 | 98 | struct gip_client; 99 | struct gip_adapter; 100 | 101 | int gip_set_power_mode(struct gip_client *client, enum gip_power_mode mode); 102 | int gip_send_authenticate(struct gip_client *client, void *pkt, u32 len, 103 | bool acknowledge); 104 | int gip_suggest_audio_format(struct gip_client *client, 105 | enum gip_audio_format in, 106 | enum gip_audio_format out, 107 | bool chat); 108 | int gip_set_audio_volume(struct gip_client *client, u8 in, u8 chat, u8 out); 109 | int gip_send_rumble(struct gip_client *client, void *pkt, u32 len); 110 | int gip_set_led_mode(struct gip_client *client, 111 | enum gip_led_mode mode, u8 brightness); 112 | int gip_send_audio_samples(struct gip_client *client, void *samples); 113 | 114 | bool gip_has_interface(struct gip_client *client, const guid_t *guid); 115 | int gip_set_encryption_key(struct gip_client *client, u8 *key, int len); 116 | int gip_enable_audio(struct gip_client *client); 117 | int gip_init_audio_in(struct gip_client *client); 118 | int gip_init_audio_out(struct gip_client *client); 119 | void gip_disable_audio(struct gip_client *client); 120 | 121 | int gip_process_buffer(struct gip_adapter *adap, void *data, int len); 122 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="xone" 2 | PACKAGE_VERSION="#VERSION#" 3 | BUILT_MODULE_NAME[0]="xone-wired" 4 | BUILT_MODULE_NAME[1]="xone-dongle" 5 | BUILT_MODULE_NAME[2]="xone-gip" 6 | BUILT_MODULE_NAME[3]="xone-gip-gamepad" 7 | BUILT_MODULE_NAME[4]="xone-gip-headset" 8 | BUILT_MODULE_NAME[5]="xone-gip-chatpad" 9 | BUILT_MODULE_NAME[6]="xone-gip-madcatz-strat" 10 | BUILT_MODULE_NAME[7]="xone-gip-madcatz-glam" 11 | BUILT_MODULE_NAME[8]="xone-gip-pdp-jaguar" 12 | DEST_MODULE_LOCATION[0]="/kernel/drivers/input/joystick" 13 | DEST_MODULE_LOCATION[1]="/kernel/drivers/input/joystick" 14 | DEST_MODULE_LOCATION[2]="/kernel/drivers/input/joystick" 15 | DEST_MODULE_LOCATION[3]="/kernel/drivers/input/joystick" 16 | DEST_MODULE_LOCATION[4]="/kernel/drivers/input/joystick" 17 | DEST_MODULE_LOCATION[5]="/kernel/drivers/input/joystick" 18 | DEST_MODULE_LOCATION[6]="/kernel/drivers/input/joystick" 19 | DEST_MODULE_LOCATION[7]="/kernel/drivers/input/joystick" 20 | DEST_MODULE_LOCATION[8]="/kernel/drivers/input/joystick" 21 | AUTOINSTALL="yes" 22 | -------------------------------------------------------------------------------- /driver/chatpad.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | 11 | #define GIP_CP_NAME "Microsoft Xbox Chatpad" 12 | 13 | struct gip_chatpad { 14 | struct gip_client *client; 15 | struct gip_input input; 16 | 17 | struct hid_device *hid_dev; 18 | }; 19 | 20 | static int gip_chatpad_hid_start(struct hid_device *dev) 21 | { 22 | return 0; 23 | } 24 | 25 | static void gip_chatpad_hid_stop(struct hid_device *dev) 26 | { 27 | } 28 | 29 | static int gip_chatpad_hid_open(struct hid_device *dev) 30 | { 31 | return 0; 32 | } 33 | 34 | static void gip_chatpad_hid_close(struct hid_device *dev) 35 | { 36 | } 37 | 38 | static int gip_chatpad_hid_parse(struct hid_device *dev) 39 | { 40 | struct gip_chatpad *chatpad = dev->driver_data; 41 | struct gip_client *client = chatpad->client; 42 | struct gip_info_element *desc_info = client->hid_descriptor; 43 | struct hid_descriptor *desc = (struct hid_descriptor *)desc_info->data; 44 | 45 | if (desc->bLength < sizeof(*desc) || desc->bNumDescriptors != 1) { 46 | dev_err(&client->dev, "%s: invalid descriptor\n", __func__); 47 | return -EINVAL; 48 | } 49 | 50 | dev->version = le16_to_cpu(desc->bcdHID); 51 | dev->country = desc->bCountryCode; 52 | 53 | return hid_parse_report(dev, desc_info->data + sizeof(*desc), 54 | desc_info->count - sizeof(*desc)); 55 | } 56 | 57 | static int gip_chatpad_hid_raw_request(struct hid_device *dev, 58 | unsigned char report_num, __u8 *buf, 59 | size_t len, unsigned char report_type, 60 | int request_type) 61 | { 62 | return 0; 63 | } 64 | 65 | static struct hid_ll_driver gip_chatpad_hid_driver = { 66 | .start = gip_chatpad_hid_start, 67 | .stop = gip_chatpad_hid_stop, 68 | .open = gip_chatpad_hid_open, 69 | .close = gip_chatpad_hid_close, 70 | .parse = gip_chatpad_hid_parse, 71 | .raw_request = gip_chatpad_hid_raw_request, 72 | }; 73 | 74 | static int gip_chatpad_init_input(struct gip_chatpad *chatpad) 75 | { 76 | int err; 77 | 78 | input_set_capability(chatpad->input.dev, EV_KEY, BTN_MODE); 79 | 80 | err = input_register_device(chatpad->input.dev); 81 | if (err) 82 | dev_err(&chatpad->client->dev, "%s: register failed: %d\n", 83 | __func__, err); 84 | 85 | return err; 86 | } 87 | 88 | static int gip_chatpad_init_hid(struct gip_chatpad *chatpad) 89 | { 90 | struct gip_client *client = chatpad->client; 91 | struct hid_device *dev; 92 | int err; 93 | 94 | dev = hid_allocate_device(); 95 | if (IS_ERR(dev)) { 96 | dev_err(&client->dev, "%s: allocate failed: %ld\n", 97 | __func__, PTR_ERR(dev)); 98 | return PTR_ERR(dev); 99 | } 100 | 101 | dev->bus = BUS_USB; 102 | dev->vendor = client->hardware.vendor; 103 | dev->product = client->hardware.product; 104 | dev->version = client->hardware.version; 105 | dev->dev.parent = &client->dev; 106 | dev->ll_driver = &gip_chatpad_hid_driver; 107 | 108 | strscpy(dev->name, GIP_CP_NAME, sizeof(dev->name)); 109 | snprintf(dev->phys, sizeof(dev->phys), "%s/input1", 110 | dev_name(&client->dev)); 111 | 112 | dev->driver_data = chatpad; 113 | 114 | err = hid_add_device(dev); 115 | if (err) { 116 | dev_err(&client->dev, "%s: add failed: %d\n", __func__, err); 117 | hid_destroy_device(dev); 118 | return err; 119 | } 120 | 121 | chatpad->hid_dev = dev; 122 | 123 | return 0; 124 | } 125 | 126 | static int gip_chatpad_op_guide_button(struct gip_client *client, bool down) 127 | { 128 | struct gip_chatpad *chatpad = dev_get_drvdata(&client->dev); 129 | 130 | input_report_key(chatpad->input.dev, BTN_MODE, down); 131 | input_sync(chatpad->input.dev); 132 | 133 | return 0; 134 | } 135 | 136 | static int gip_chatpad_op_hid_report(struct gip_client *client, 137 | void *data, u32 len) 138 | { 139 | struct gip_chatpad *chatpad = dev_get_drvdata(&client->dev); 140 | 141 | return hid_input_report(chatpad->hid_dev, HID_INPUT_REPORT, 142 | data, len, true); 143 | } 144 | 145 | static int gip_chatpad_probe(struct gip_client *client) 146 | { 147 | struct gip_chatpad *chatpad; 148 | struct gip_info_element *hid_desc = client->hid_descriptor; 149 | int err; 150 | 151 | if (!hid_desc || hid_desc->count < sizeof(struct hid_descriptor)) 152 | return -ENODEV; 153 | 154 | chatpad = devm_kzalloc(&client->dev, sizeof(*chatpad), GFP_KERNEL); 155 | if (!chatpad) 156 | return -ENOMEM; 157 | 158 | chatpad->client = client; 159 | 160 | err = gip_set_power_mode(client, GIP_PWR_ON); 161 | if (err) 162 | return err; 163 | 164 | err = gip_init_input(&chatpad->input, client, GIP_CP_NAME); 165 | if (err) 166 | return err; 167 | 168 | err = gip_chatpad_init_input(chatpad); 169 | if (err) 170 | return err; 171 | 172 | err = gip_chatpad_init_hid(chatpad); 173 | if (err) 174 | return err; 175 | 176 | dev_set_drvdata(&client->dev, chatpad); 177 | 178 | return 0; 179 | } 180 | 181 | static void gip_chatpad_remove(struct gip_client *client) 182 | { 183 | struct gip_chatpad *chatpad = dev_get_drvdata(&client->dev); 184 | 185 | hid_destroy_device(chatpad->hid_dev); 186 | } 187 | 188 | static struct gip_driver gip_chatpad_driver = { 189 | .name = "xone-gip-chatpad", 190 | .class = "Windows.Xbox.Input.Chatpad", 191 | .ops = { 192 | .guide_button = gip_chatpad_op_guide_button, 193 | .hid_report = gip_chatpad_op_hid_report, 194 | }, 195 | .probe = gip_chatpad_probe, 196 | .remove = gip_chatpad_remove, 197 | }; 198 | module_gip_driver(gip_chatpad_driver); 199 | 200 | MODULE_ALIAS("gip:Windows.Xbox.Input.Chatpad"); 201 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 202 | MODULE_DESCRIPTION("xone GIP chatpad driver"); 203 | MODULE_VERSION("#VERSION#"); 204 | MODULE_LICENSE("GPL"); 205 | -------------------------------------------------------------------------------- /driver/common.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | 8 | #include "common.h" 9 | 10 | #define GIP_LED_BRIGHTNESS_DEFAULT 20 11 | #define GIP_LED_BRIGHTNESS_MAX 50 12 | 13 | static enum power_supply_property gip_battery_props[] = { 14 | POWER_SUPPLY_PROP_STATUS, 15 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, 16 | POWER_SUPPLY_PROP_SCOPE, 17 | POWER_SUPPLY_PROP_MODEL_NAME, 18 | }; 19 | 20 | static int gip_get_battery_prop(struct power_supply *psy, 21 | enum power_supply_property psp, 22 | union power_supply_propval *val) 23 | { 24 | struct gip_battery *batt = power_supply_get_drvdata(psy); 25 | 26 | switch (psp) { 27 | case POWER_SUPPLY_PROP_STATUS: 28 | val->intval = batt->status; 29 | break; 30 | case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 31 | val->intval = batt->capacity; 32 | break; 33 | case POWER_SUPPLY_PROP_SCOPE: 34 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; 35 | break; 36 | case POWER_SUPPLY_PROP_MODEL_NAME: 37 | val->strval = batt->name; 38 | break; 39 | default: 40 | return -EINVAL; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | int gip_init_battery(struct gip_battery *batt, struct gip_client *client, 47 | const char *name) 48 | { 49 | struct power_supply_config cfg = {}; 50 | 51 | batt->name = name; 52 | batt->status = POWER_SUPPLY_STATUS_UNKNOWN; 53 | batt->capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 54 | 55 | batt->desc.name = dev_name(&client->dev); 56 | batt->desc.type = POWER_SUPPLY_TYPE_BATTERY; 57 | batt->desc.properties = gip_battery_props; 58 | batt->desc.num_properties = ARRAY_SIZE(gip_battery_props); 59 | batt->desc.get_property = gip_get_battery_prop; 60 | 61 | cfg.drv_data = batt; 62 | 63 | batt->supply = devm_power_supply_register(&client->dev, &batt->desc, 64 | &cfg); 65 | if (IS_ERR(batt->supply)) { 66 | dev_err(&client->dev, "%s: register failed: %ld\n", 67 | __func__, PTR_ERR(batt->supply)); 68 | return PTR_ERR(batt->supply); 69 | } 70 | 71 | power_supply_powers(batt->supply, &client->dev); 72 | 73 | return 0; 74 | } 75 | EXPORT_SYMBOL_GPL(gip_init_battery); 76 | 77 | void gip_report_battery(struct gip_battery *batt, 78 | enum gip_battery_type type, 79 | enum gip_battery_level level) 80 | { 81 | if (type == GIP_BATT_TYPE_NONE) 82 | batt->status = POWER_SUPPLY_STATUS_NOT_CHARGING; 83 | else 84 | batt->status = POWER_SUPPLY_STATUS_DISCHARGING; 85 | 86 | if (type == GIP_BATT_TYPE_NONE) 87 | batt->capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 88 | else if (level == GIP_BATT_LEVEL_LOW) 89 | batt->capacity = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 90 | else if (level == GIP_BATT_LEVEL_NORMAL) 91 | batt->capacity = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 92 | else if (level == GIP_BATT_LEVEL_HIGH) 93 | batt->capacity = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; 94 | else if (level == GIP_BATT_LEVEL_FULL) 95 | batt->capacity = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 96 | 97 | if (batt->supply) 98 | power_supply_changed(batt->supply); 99 | } 100 | EXPORT_SYMBOL_GPL(gip_report_battery); 101 | 102 | static void gip_led_brightness_set(struct led_classdev *dev, 103 | enum led_brightness brightness) 104 | { 105 | struct gip_led *led = container_of(dev, typeof(*led), dev); 106 | int err; 107 | 108 | if (dev->flags & LED_UNREGISTERING) 109 | return; 110 | 111 | dev_dbg(&led->client->dev, "%s: brightness=%d\n", __func__, brightness); 112 | 113 | err = gip_set_led_mode(led->client, led->mode, brightness); 114 | if (err) 115 | dev_err(&led->client->dev, "%s: set LED mode failed: %d\n", 116 | __func__, err); 117 | } 118 | 119 | static ssize_t gip_led_mode_show(struct device *dev, 120 | struct device_attribute *attr, char *buf) 121 | { 122 | struct led_classdev *cdev = dev_get_drvdata(dev); 123 | struct gip_led *led = container_of(cdev, typeof(*led), dev); 124 | 125 | return sprintf(buf, "%u\n", led->mode); 126 | } 127 | 128 | static ssize_t gip_led_mode_store(struct device *dev, 129 | struct device_attribute *attr, 130 | const char *buf, size_t count) 131 | { 132 | struct led_classdev *cdev = dev_get_drvdata(dev); 133 | struct gip_led *led = container_of(cdev, typeof(*led), dev); 134 | u8 mode; 135 | int err; 136 | 137 | err = kstrtou8(buf, 10, &mode); 138 | if (err) 139 | return err; 140 | 141 | dev_dbg(&led->client->dev, "%s: mode=%u\n", __func__, mode); 142 | led->mode = mode; 143 | 144 | err = gip_set_led_mode(led->client, mode, cdev->brightness); 145 | if (err) { 146 | dev_err(&led->client->dev, "%s: set LED mode failed: %d\n", 147 | __func__, err); 148 | return err; 149 | } 150 | 151 | return count; 152 | } 153 | 154 | static struct device_attribute gip_led_attr_mode = 155 | __ATTR(mode, 0644, gip_led_mode_show, gip_led_mode_store); 156 | 157 | static struct attribute *gip_led_attrs[] = { 158 | &gip_led_attr_mode.attr, 159 | NULL, 160 | }; 161 | ATTRIBUTE_GROUPS(gip_led); 162 | 163 | int gip_init_led(struct gip_led *led, struct gip_client *client) 164 | { 165 | int err; 166 | 167 | /* set default brightness */ 168 | err = gip_set_led_mode(client, GIP_LED_ON, GIP_LED_BRIGHTNESS_DEFAULT); 169 | if (err) { 170 | dev_err(&client->dev, "%s: set brightness failed: %d\n", 171 | __func__, err); 172 | return err; 173 | } 174 | 175 | led->dev.name = devm_kasprintf(&client->dev, GFP_KERNEL, 176 | "%s:white:status", 177 | dev_name(&client->dev)); 178 | if (!led->dev.name) 179 | return -ENOMEM; 180 | 181 | led->dev.brightness = GIP_LED_BRIGHTNESS_DEFAULT; 182 | led->dev.max_brightness = GIP_LED_BRIGHTNESS_MAX; 183 | led->dev.brightness_set = gip_led_brightness_set; 184 | led->dev.groups = gip_led_groups; 185 | 186 | led->client = client; 187 | led->mode = GIP_LED_ON; 188 | 189 | err = devm_led_classdev_register(&client->dev, &led->dev); 190 | if (err) 191 | dev_err(&client->dev, "%s: register failed: %d\n", 192 | __func__, err); 193 | 194 | return err; 195 | } 196 | EXPORT_SYMBOL_GPL(gip_init_led); 197 | 198 | int gip_init_input(struct gip_input *input, struct gip_client *client, 199 | const char *name) 200 | { 201 | input->dev = devm_input_allocate_device(&client->dev); 202 | if (!input->dev) 203 | return -ENOMEM; 204 | 205 | input->dev->phys = devm_kasprintf(&client->dev, GFP_KERNEL, 206 | "%s/input0", dev_name(&client->dev)); 207 | if (!input->dev->phys) 208 | return -ENOMEM; 209 | 210 | input->dev->name = name; 211 | input->dev->id.bustype = BUS_VIRTUAL; 212 | input->dev->id.vendor = client->hardware.vendor; 213 | input->dev->id.product = client->hardware.product; 214 | input->dev->id.version = client->hardware.version; 215 | input->dev->dev.parent = &client->dev; 216 | 217 | return 0; 218 | } 219 | EXPORT_SYMBOL_GPL(gip_init_input); 220 | -------------------------------------------------------------------------------- /driver/common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../bus/bus.h" 13 | 14 | struct gip_battery { 15 | struct power_supply *supply; 16 | struct power_supply_desc desc; 17 | 18 | const char *name; 19 | int status; 20 | int capacity; 21 | }; 22 | 23 | struct gip_led { 24 | struct led_classdev dev; 25 | 26 | struct gip_client *client; 27 | enum gip_led_mode mode; 28 | }; 29 | 30 | struct gip_input { 31 | struct input_dev *dev; 32 | }; 33 | 34 | int gip_init_battery(struct gip_battery *batt, struct gip_client *client, 35 | const char *name); 36 | void gip_report_battery(struct gip_battery *batt, 37 | enum gip_battery_type type, 38 | enum gip_battery_level level); 39 | 40 | int gip_init_led(struct gip_led *led, struct gip_client *client); 41 | 42 | int gip_init_input(struct gip_input *input, struct gip_client *client, 43 | const char *name); 44 | -------------------------------------------------------------------------------- /driver/gamepad.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "common.h" 12 | #include "../auth/auth.h" 13 | 14 | #define GIP_GP_NAME "Microsoft Xbox Controller" 15 | 16 | #define GIP_GP_RUMBLE_DELAY msecs_to_jiffies(10) 17 | #define GIP_GP_RUMBLE_MAX 100 18 | 19 | /* button offset from end of packet */ 20 | #define GIP_GP_BTN_SHARE_OFFSET 18 21 | 22 | static const guid_t gip_gamepad_guid_share = 23 | GUID_INIT(0xecddd2fe, 0xd387, 0x4294, 24 | 0xbd, 0x96, 0x1a, 0x71, 0x2e, 0x3d, 0xc7, 0x7d); 25 | 26 | static const guid_t gip_gamepad_guid_dli = 27 | GUID_INIT(0x87f2e56b, 0xc3bb, 0x49b1, 28 | 0x82, 0x65, 0xff, 0xff, 0xf3, 0x77, 0x99, 0xee); 29 | 30 | enum gip_gamepad_button { 31 | GIP_GP_BTN_MENU = BIT(2), 32 | GIP_GP_BTN_VIEW = BIT(3), 33 | GIP_GP_BTN_A = BIT(4), 34 | GIP_GP_BTN_B = BIT(5), 35 | GIP_GP_BTN_X = BIT(6), 36 | GIP_GP_BTN_Y = BIT(7), 37 | GIP_GP_BTN_DPAD_U = BIT(8), 38 | GIP_GP_BTN_DPAD_D = BIT(9), 39 | GIP_GP_BTN_DPAD_L = BIT(10), 40 | GIP_GP_BTN_DPAD_R = BIT(11), 41 | GIP_GP_BTN_BUMPER_L = BIT(12), 42 | GIP_GP_BTN_BUMPER_R = BIT(13), 43 | GIP_GP_BTN_STICK_L = BIT(14), 44 | GIP_GP_BTN_STICK_R = BIT(15), 45 | }; 46 | 47 | enum gip_gamepad_motor { 48 | GIP_GP_MOTOR_R = BIT(0), 49 | GIP_GP_MOTOR_L = BIT(1), 50 | GIP_GP_MOTOR_RT = BIT(2), 51 | GIP_GP_MOTOR_LT = BIT(3), 52 | }; 53 | 54 | struct gip_gamepad_pkt_input { 55 | __le16 buttons; 56 | __le16 trigger_left; 57 | __le16 trigger_right; 58 | __le16 stick_left_x; 59 | __le16 stick_left_y; 60 | __le16 stick_right_x; 61 | __le16 stick_right_y; 62 | } __packed; 63 | 64 | struct gip_gamepad_pkt_dli { 65 | u32 counter_us1; 66 | u32 counter_us2; 67 | } __packed; 68 | 69 | struct gip_gamepad_pkt_rumble { 70 | u8 unknown; 71 | u8 motors; 72 | u8 left_trigger; 73 | u8 right_trigger; 74 | u8 left; 75 | u8 right; 76 | u8 duration; 77 | u8 delay; 78 | u8 repeat; 79 | } __packed; 80 | 81 | struct gip_gamepad { 82 | struct gip_client *client; 83 | struct gip_battery battery; 84 | struct gip_auth auth; 85 | struct gip_led led; 86 | struct gip_input input; 87 | 88 | bool supports_share; 89 | bool supports_dli; 90 | 91 | struct gip_gamepad_rumble { 92 | /* serializes access to rumble packet */ 93 | spinlock_t lock; 94 | unsigned long last; 95 | struct timer_list timer; 96 | struct gip_gamepad_pkt_rumble pkt; 97 | } rumble; 98 | }; 99 | 100 | static void gip_gamepad_send_rumble(struct timer_list *timer) 101 | { 102 | struct gip_gamepad_rumble *rumble = from_timer(rumble, timer, timer); 103 | struct gip_gamepad *gamepad = container_of(rumble, typeof(*gamepad), 104 | rumble); 105 | unsigned long flags; 106 | 107 | spin_lock_irqsave(&rumble->lock, flags); 108 | 109 | gip_send_rumble(gamepad->client, &rumble->pkt, sizeof(rumble->pkt)); 110 | rumble->last = jiffies; 111 | 112 | spin_unlock_irqrestore(&rumble->lock, flags); 113 | } 114 | 115 | static int gip_gamepad_queue_rumble(struct input_dev *dev, void *data, 116 | struct ff_effect *effect) 117 | { 118 | struct gip_gamepad_rumble *rumble = input_get_drvdata(dev); 119 | u32 mag_left = effect->u.rumble.strong_magnitude; 120 | u32 mag_right = effect->u.rumble.weak_magnitude; 121 | unsigned long flags; 122 | 123 | if (effect->type != FF_RUMBLE) 124 | return 0; 125 | 126 | spin_lock_irqsave(&rumble->lock, flags); 127 | 128 | rumble->pkt.left = (mag_left * GIP_GP_RUMBLE_MAX + S16_MAX) / U16_MAX; 129 | rumble->pkt.right = (mag_right * GIP_GP_RUMBLE_MAX + S16_MAX) / U16_MAX; 130 | 131 | /* delay rumble to work around firmware bug */ 132 | if (!timer_pending(&rumble->timer)) 133 | mod_timer(&rumble->timer, rumble->last + GIP_GP_RUMBLE_DELAY); 134 | 135 | spin_unlock_irqrestore(&rumble->lock, flags); 136 | 137 | return 0; 138 | } 139 | 140 | static int gip_gamepad_init_rumble(struct gip_gamepad *gamepad) 141 | { 142 | struct gip_gamepad_rumble *rumble = &gamepad->rumble; 143 | struct input_dev *dev = gamepad->input.dev; 144 | 145 | spin_lock_init(&rumble->lock); 146 | timer_setup(&rumble->timer, gip_gamepad_send_rumble, 0); 147 | 148 | /* stop rumble (required for some exotic gamepads to start input) */ 149 | rumble->pkt.motors = GIP_GP_MOTOR_R | GIP_GP_MOTOR_L | 150 | GIP_GP_MOTOR_RT | GIP_GP_MOTOR_LT; 151 | rumble->pkt.duration = 0xff; 152 | rumble->pkt.repeat = 0xeb; 153 | gip_gamepad_send_rumble(&rumble->timer); 154 | 155 | input_set_capability(dev, EV_FF, FF_RUMBLE); 156 | input_set_drvdata(dev, rumble); 157 | 158 | return input_ff_create_memless(dev, NULL, gip_gamepad_queue_rumble); 159 | } 160 | 161 | static int gip_gamepad_init_input(struct gip_gamepad *gamepad) 162 | { 163 | struct input_dev *dev = gamepad->input.dev; 164 | int err; 165 | 166 | gamepad->supports_share = gip_has_interface(gamepad->client, 167 | &gip_gamepad_guid_share); 168 | gamepad->supports_dli = gip_has_interface(gamepad->client, 169 | &gip_gamepad_guid_dli); 170 | 171 | if (gamepad->supports_share) 172 | input_set_capability(dev, EV_KEY, KEY_RECORD); 173 | 174 | input_set_capability(dev, EV_KEY, BTN_MODE); 175 | input_set_capability(dev, EV_KEY, BTN_START); 176 | input_set_capability(dev, EV_KEY, BTN_SELECT); 177 | input_set_capability(dev, EV_KEY, BTN_A); 178 | input_set_capability(dev, EV_KEY, BTN_B); 179 | input_set_capability(dev, EV_KEY, BTN_X); 180 | input_set_capability(dev, EV_KEY, BTN_Y); 181 | input_set_capability(dev, EV_KEY, BTN_TL); 182 | input_set_capability(dev, EV_KEY, BTN_TR); 183 | input_set_capability(dev, EV_KEY, BTN_THUMBL); 184 | input_set_capability(dev, EV_KEY, BTN_THUMBR); 185 | input_set_abs_params(dev, ABS_X, -32768, 32767, 16, 128); 186 | input_set_abs_params(dev, ABS_RX, -32768, 32767, 16, 128); 187 | input_set_abs_params(dev, ABS_Y, -32768, 32767, 16, 128); 188 | input_set_abs_params(dev, ABS_RY, -32768, 32767, 16, 128); 189 | input_set_abs_params(dev, ABS_Z, 0, 1023, 0, 0); 190 | input_set_abs_params(dev, ABS_RZ, 0, 1023, 0, 0); 191 | input_set_abs_params(dev, ABS_HAT0X, -1, 1, 0, 0); 192 | input_set_abs_params(dev, ABS_HAT0Y, -1, 1, 0, 0); 193 | 194 | err = gip_gamepad_init_rumble(gamepad); 195 | if (err) { 196 | dev_err(&gamepad->client->dev, "%s: init rumble failed: %d\n", 197 | __func__, err); 198 | goto err_delete_timer; 199 | } 200 | 201 | err = input_register_device(dev); 202 | if (err) { 203 | dev_err(&gamepad->client->dev, "%s: register failed: %d\n", 204 | __func__, err); 205 | goto err_delete_timer; 206 | } 207 | 208 | return 0; 209 | 210 | err_delete_timer: 211 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,15,0) 212 | timer_delete_sync(&gamepad->rumble.timer); 213 | #else 214 | del_timer_sync(&gamepad->rumble.timer); 215 | #endif 216 | return err; 217 | } 218 | 219 | static int gip_gamepad_op_battery(struct gip_client *client, 220 | enum gip_battery_type type, 221 | enum gip_battery_level level) 222 | { 223 | struct gip_gamepad *gamepad = dev_get_drvdata(&client->dev); 224 | 225 | gip_report_battery(&gamepad->battery, type, level); 226 | 227 | return 0; 228 | } 229 | 230 | static int gip_gamepad_op_authenticate(struct gip_client *client, 231 | void *data, u32 len) 232 | { 233 | struct gip_gamepad *gamepad = dev_get_drvdata(&client->dev); 234 | 235 | return gip_auth_process_pkt(&gamepad->auth, data, len); 236 | } 237 | 238 | static int gip_gamepad_op_guide_button(struct gip_client *client, bool down) 239 | { 240 | struct gip_gamepad *gamepad = dev_get_drvdata(&client->dev); 241 | 242 | input_report_key(gamepad->input.dev, BTN_MODE, down); 243 | input_sync(gamepad->input.dev); 244 | 245 | return 0; 246 | } 247 | 248 | static int gip_gamepad_op_input(struct gip_client *client, void *data, u32 len) 249 | { 250 | struct gip_gamepad *gamepad = dev_get_drvdata(&client->dev); 251 | struct gip_gamepad_pkt_input *pkt = data; 252 | struct input_dev *dev = gamepad->input.dev; 253 | u16 buttons; 254 | u8 share_offset = GIP_GP_BTN_SHARE_OFFSET; 255 | 256 | if (len < sizeof(*pkt)) 257 | return -EINVAL; 258 | 259 | buttons = le16_to_cpu(pkt->buttons); 260 | 261 | /* share button byte is always at fixed offset from end of packet */ 262 | if (gamepad->supports_share) { 263 | if (gamepad->supports_dli) 264 | share_offset += sizeof(struct gip_gamepad_pkt_dli); 265 | 266 | if (len < share_offset) 267 | return -EINVAL; 268 | 269 | input_report_key(dev, KEY_RECORD, 270 | ((u8 *)data)[len - share_offset]); 271 | } 272 | 273 | input_report_key(dev, BTN_START, buttons & GIP_GP_BTN_MENU); 274 | input_report_key(dev, BTN_SELECT, buttons & GIP_GP_BTN_VIEW); 275 | input_report_key(dev, BTN_A, buttons & GIP_GP_BTN_A); 276 | input_report_key(dev, BTN_B, buttons & GIP_GP_BTN_B); 277 | input_report_key(dev, BTN_X, buttons & GIP_GP_BTN_X); 278 | input_report_key(dev, BTN_Y, buttons & GIP_GP_BTN_Y); 279 | input_report_key(dev, BTN_TL, buttons & GIP_GP_BTN_BUMPER_L); 280 | input_report_key(dev, BTN_TR, buttons & GIP_GP_BTN_BUMPER_R); 281 | input_report_key(dev, BTN_THUMBL, buttons & GIP_GP_BTN_STICK_L); 282 | input_report_key(dev, BTN_THUMBR, buttons & GIP_GP_BTN_STICK_R); 283 | input_report_abs(dev, ABS_X, (s16)le16_to_cpu(pkt->stick_left_x)); 284 | input_report_abs(dev, ABS_RX, (s16)le16_to_cpu(pkt->stick_right_x)); 285 | input_report_abs(dev, ABS_Y, ~(s16)le16_to_cpu(pkt->stick_left_y)); 286 | input_report_abs(dev, ABS_RY, ~(s16)le16_to_cpu(pkt->stick_right_y)); 287 | input_report_abs(dev, ABS_Z, le16_to_cpu(pkt->trigger_left)); 288 | input_report_abs(dev, ABS_RZ, le16_to_cpu(pkt->trigger_right)); 289 | input_report_abs(dev, ABS_HAT0X, !!(buttons & GIP_GP_BTN_DPAD_R) - 290 | !!(buttons & GIP_GP_BTN_DPAD_L)); 291 | input_report_abs(dev, ABS_HAT0Y, !!(buttons & GIP_GP_BTN_DPAD_D) - 292 | !!(buttons & GIP_GP_BTN_DPAD_U)); 293 | input_sync(dev); 294 | 295 | return 0; 296 | } 297 | 298 | static int gip_gamepad_probe(struct gip_client *client) 299 | { 300 | struct gip_gamepad *gamepad; 301 | int err; 302 | 303 | gamepad = devm_kzalloc(&client->dev, sizeof(*gamepad), GFP_KERNEL); 304 | if (!gamepad) 305 | return -ENOMEM; 306 | 307 | gamepad->client = client; 308 | 309 | err = gip_set_power_mode(client, GIP_PWR_ON); 310 | if (err) 311 | return err; 312 | 313 | err = gip_init_battery(&gamepad->battery, client, GIP_GP_NAME); 314 | if (err) 315 | return err; 316 | 317 | err = gip_init_led(&gamepad->led, client); 318 | if (err) 319 | return err; 320 | 321 | err = gip_auth_start_handshake(&gamepad->auth, client); 322 | if (err) 323 | return err; 324 | 325 | err = gip_init_input(&gamepad->input, client, GIP_GP_NAME); 326 | if (err) 327 | return err; 328 | 329 | err = gip_gamepad_init_input(gamepad); 330 | if (err) 331 | return err; 332 | 333 | dev_set_drvdata(&client->dev, gamepad); 334 | 335 | return 0; 336 | } 337 | 338 | static void gip_gamepad_remove(struct gip_client *client) 339 | { 340 | struct gip_gamepad *gamepad = dev_get_drvdata(&client->dev); 341 | 342 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,15,0) 343 | timer_delete_sync(&gamepad->rumble.timer); 344 | #else 345 | del_timer_sync(&gamepad->rumble.timer); 346 | #endif 347 | } 348 | 349 | static struct gip_driver gip_gamepad_driver = { 350 | .name = "xone-gip-gamepad", 351 | .class = "Windows.Xbox.Input.Gamepad", 352 | .ops = { 353 | .battery = gip_gamepad_op_battery, 354 | .authenticate = gip_gamepad_op_authenticate, 355 | .guide_button = gip_gamepad_op_guide_button, 356 | .input = gip_gamepad_op_input, 357 | }, 358 | .probe = gip_gamepad_probe, 359 | .remove = gip_gamepad_remove, 360 | }; 361 | module_gip_driver(gip_gamepad_driver); 362 | 363 | MODULE_ALIAS("gip:Windows.Xbox.Input.Gamepad"); 364 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 365 | MODULE_DESCRIPTION("xone GIP gamepad driver"); 366 | MODULE_VERSION("#VERSION#"); 367 | MODULE_LICENSE("GPL"); 368 | -------------------------------------------------------------------------------- /driver/headset.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "common.h" 15 | #include "../auth/auth.h" 16 | 17 | #define GIP_HS_NAME "Microsoft Xbox Headset" 18 | 19 | #define GIP_HS_NUM_BUFFERS 128 20 | 21 | /* product ID for the chat headset */ 22 | #define GIP_HS_PID_CHAT 0x0111 23 | 24 | #define GIP_HS_CONFIG_DELAY msecs_to_jiffies(1000) 25 | #define GIP_HS_POWER_ON_DELAY msecs_to_jiffies(1000) 26 | 27 | static const struct snd_pcm_hardware gip_headset_pcm_hw = { 28 | .info = SNDRV_PCM_INFO_MMAP | 29 | SNDRV_PCM_INFO_MMAP_VALID | 30 | SNDRV_PCM_INFO_BATCH | 31 | SNDRV_PCM_INFO_INTERLEAVED | 32 | SNDRV_PCM_INFO_BLOCK_TRANSFER, 33 | .formats = SNDRV_PCM_FMTBIT_S16_LE, 34 | .rates = SNDRV_PCM_RATE_CONTINUOUS, 35 | .periods_min = 2, 36 | .periods_max = GIP_HS_NUM_BUFFERS, 37 | }; 38 | 39 | struct gip_headset { 40 | struct gip_client *client; 41 | struct gip_battery battery; 42 | struct gip_auth auth; 43 | 44 | bool chat_headset; 45 | 46 | struct delayed_work work_config; 47 | struct delayed_work work_power_on; 48 | struct work_struct work_register; 49 | bool got_ready; 50 | bool got_initial_volume; 51 | bool registered; 52 | 53 | struct hrtimer timer; 54 | void *buffer; 55 | 56 | struct gip_headset_stream { 57 | struct snd_pcm_substream *substream; 58 | snd_pcm_uframes_t pointer; 59 | snd_pcm_uframes_t period; 60 | } playback, capture; 61 | 62 | struct snd_card *card; 63 | }; 64 | 65 | static int gip_headset_pcm_open(struct snd_pcm_substream *sub) 66 | { 67 | struct gip_headset *headset = snd_pcm_substream_chip(sub); 68 | struct gip_audio_config *cfg; 69 | struct snd_pcm_hardware hw = gip_headset_pcm_hw; 70 | 71 | if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) 72 | cfg = &headset->client->audio_config_out; 73 | else 74 | cfg = &headset->client->audio_config_in; 75 | 76 | hw.rate_min = cfg->sample_rate; 77 | hw.rate_max = cfg->sample_rate; 78 | hw.channels_min = cfg->channels; 79 | hw.channels_max = cfg->channels; 80 | hw.buffer_bytes_max = cfg->buffer_size * GIP_HS_NUM_BUFFERS; 81 | hw.period_bytes_min = cfg->buffer_size; 82 | hw.period_bytes_max = cfg->buffer_size; 83 | 84 | sub->runtime->hw = hw; 85 | 86 | return 0; 87 | } 88 | 89 | static int gip_headset_pcm_close(struct snd_pcm_substream *sub) 90 | { 91 | return 0; 92 | } 93 | 94 | static int gip_headset_pcm_hw_params(struct snd_pcm_substream *sub, 95 | struct snd_pcm_hw_params *params) 96 | { 97 | struct snd_pcm_runtime *runtime = sub->runtime; 98 | size_t size = params_buffer_bytes(params); 99 | 100 | if (runtime->dma_area) { 101 | if (runtime->dma_bytes >= size) 102 | return 0; /* Already large enough */ 103 | vfree(runtime->dma_area); 104 | } 105 | runtime->dma_area = vzalloc(size); 106 | if (!runtime->dma_area) 107 | return -ENOMEM; 108 | runtime->dma_bytes = size; 109 | return 1; 110 | } 111 | 112 | static int gip_headset_pcm_hw_free(struct snd_pcm_substream *sub) 113 | { 114 | struct snd_pcm_runtime *runtime = sub->runtime; 115 | 116 | vfree(runtime->dma_area); 117 | runtime->dma_area = NULL; 118 | return 0; 119 | } 120 | 121 | static struct page *gip_headset_pcm_get_page(struct snd_pcm_substream *sub, 122 | unsigned long offset) 123 | { 124 | return vmalloc_to_page(sub->runtime->dma_area + offset); 125 | } 126 | 127 | static int gip_headset_pcm_prepare(struct snd_pcm_substream *sub) 128 | { 129 | return 0; 130 | } 131 | 132 | static int gip_headset_pcm_trigger(struct snd_pcm_substream *sub, int cmd) 133 | { 134 | struct gip_headset *headset = snd_pcm_substream_chip(sub); 135 | struct gip_headset_stream *stream; 136 | 137 | if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) 138 | stream = &headset->playback; 139 | else 140 | stream = &headset->capture; 141 | 142 | stream->pointer = 0; 143 | stream->period = 0; 144 | 145 | switch (cmd) { 146 | case SNDRV_PCM_TRIGGER_START: 147 | stream->substream = sub; 148 | break; 149 | case SNDRV_PCM_TRIGGER_STOP: 150 | stream->substream = NULL; 151 | break; 152 | default: 153 | return -EINVAL; 154 | } 155 | 156 | if (!stream->substream && sub->stream == SNDRV_PCM_STREAM_PLAYBACK) 157 | memset(headset->buffer, 0, 158 | headset->client->audio_config_out.buffer_size); 159 | 160 | return 0; 161 | } 162 | 163 | static snd_pcm_uframes_t gip_headset_pcm_pointer(struct snd_pcm_substream *sub) 164 | { 165 | struct gip_headset *headset = snd_pcm_substream_chip(sub); 166 | struct gip_headset_stream *stream; 167 | 168 | if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) 169 | stream = &headset->playback; 170 | else 171 | stream = &headset->capture; 172 | 173 | return bytes_to_frames(sub->runtime, stream->pointer); 174 | } 175 | 176 | static const struct snd_pcm_ops gip_headset_pcm_ops = { 177 | .open = gip_headset_pcm_open, 178 | .close = gip_headset_pcm_close, 179 | .ioctl = snd_pcm_lib_ioctl, 180 | .hw_params = gip_headset_pcm_hw_params, 181 | .hw_free = gip_headset_pcm_hw_free, 182 | .prepare = gip_headset_pcm_prepare, 183 | .trigger = gip_headset_pcm_trigger, 184 | .pointer = gip_headset_pcm_pointer, 185 | .page = gip_headset_pcm_get_page, 186 | }; 187 | 188 | static bool gip_headset_advance_pointer(struct gip_headset_stream *stream, 189 | int len, size_t buf_size) 190 | { 191 | snd_pcm_uframes_t period = stream->substream->runtime->period_size; 192 | 193 | stream->pointer += len; 194 | if (stream->pointer >= buf_size) 195 | stream->pointer -= buf_size; 196 | 197 | stream->period += len; 198 | if (stream->period >= period) { 199 | stream->period -= period; 200 | return true; 201 | } 202 | 203 | return false; 204 | } 205 | 206 | static bool gip_headset_copy_playback(struct gip_headset_stream *stream, 207 | unsigned char *data, int len) 208 | { 209 | unsigned char *src = stream->substream->runtime->dma_area; 210 | size_t buf_size = snd_pcm_lib_buffer_bytes(stream->substream); 211 | size_t remaining = buf_size - stream->pointer; 212 | 213 | if (len <= remaining) { 214 | memcpy(data, src + stream->pointer, len); 215 | } else { 216 | memcpy(data, src + stream->pointer, remaining); 217 | memcpy(data + remaining, src, len - remaining); 218 | } 219 | 220 | return gip_headset_advance_pointer(stream, len, buf_size); 221 | } 222 | 223 | static bool gip_headset_copy_capture(struct gip_headset_stream *stream, 224 | unsigned char *data, int len) 225 | { 226 | unsigned char *dest = stream->substream->runtime->dma_area; 227 | size_t buf_size = snd_pcm_lib_buffer_bytes(stream->substream); 228 | size_t remaining = buf_size - stream->pointer; 229 | 230 | if (len <= remaining) { 231 | memcpy(dest + stream->pointer, data, len); 232 | } else { 233 | memcpy(dest + stream->pointer, data, remaining); 234 | memcpy(dest, data + remaining, len - remaining); 235 | } 236 | 237 | return gip_headset_advance_pointer(stream, len, buf_size); 238 | } 239 | 240 | static enum hrtimer_restart gip_headset_send_samples(struct hrtimer *timer) 241 | { 242 | struct gip_headset *headset = container_of(timer, typeof(*headset), 243 | timer); 244 | struct gip_audio_config *cfg = &headset->client->audio_config_out; 245 | struct snd_pcm_substream *sub = headset->playback.substream; 246 | bool elapsed = false; 247 | int err; 248 | unsigned long flags; 249 | 250 | if (sub) { 251 | snd_pcm_stream_lock_irqsave(sub, flags); 252 | 253 | if (sub->runtime && snd_pcm_running(sub)) 254 | elapsed = gip_headset_copy_playback(&headset->playback, 255 | headset->buffer, 256 | cfg->buffer_size); 257 | 258 | snd_pcm_stream_unlock_irqrestore(sub, flags); 259 | 260 | if (elapsed) 261 | snd_pcm_period_elapsed(sub); 262 | } 263 | 264 | /* retry if driver runs out of buffers */ 265 | err = gip_send_audio_samples(headset->client, headset->buffer); 266 | if (err && err != -ENOSPC) 267 | return HRTIMER_NORESTART; 268 | 269 | hrtimer_forward_now(timer, ms_to_ktime(GIP_AUDIO_INTERVAL)); 270 | 271 | return HRTIMER_RESTART; 272 | } 273 | 274 | static int gip_headset_init_pcm(struct gip_headset *headset) 275 | { 276 | struct snd_card *card; 277 | struct snd_pcm *pcm; 278 | int err; 279 | 280 | err = snd_card_new(&headset->client->dev, SNDRV_DEFAULT_IDX1, 281 | SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); 282 | if (err) 283 | return err; 284 | 285 | strscpy(card->driver, "xone-gip-headset", sizeof(card->driver)); 286 | strscpy(card->shortname, GIP_HS_NAME, sizeof(card->shortname)); 287 | snprintf(card->longname, sizeof(card->longname), "%s at %s", 288 | GIP_HS_NAME, dev_name(&headset->client->dev)); 289 | 290 | headset->card = card; 291 | 292 | err = snd_pcm_new(card, GIP_HS_NAME, 0, 1, 1, &pcm); 293 | if (err) 294 | return err; 295 | 296 | strscpy(pcm->name, GIP_HS_NAME, sizeof(pcm->name)); 297 | pcm->private_data = headset; 298 | 299 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &gip_headset_pcm_ops); 300 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &gip_headset_pcm_ops); 301 | 302 | return snd_card_register(card); 303 | } 304 | 305 | static void gip_headset_config(struct work_struct *work) 306 | { 307 | struct gip_headset *headset = container_of(to_delayed_work(work), 308 | typeof(*headset), 309 | work_config); 310 | struct gip_client *client = headset->client; 311 | struct gip_info_element *fmts = client->audio_formats; 312 | int err; 313 | 314 | dev_dbg(&client->dev, "%s: format=0x%02x/0x%02x\n", __func__, 315 | fmts->data[0], fmts->data[1]); 316 | 317 | /* suggest initial audio format */ 318 | err = gip_suggest_audio_format(client, fmts->data[0], fmts->data[1], 319 | headset->chat_headset); 320 | if (err) 321 | dev_err(&client->dev, "%s: suggest format failed: %d\n", 322 | __func__, err); 323 | } 324 | 325 | static void gip_headset_power_on(struct work_struct *work) 326 | { 327 | struct gip_headset *headset = container_of(to_delayed_work(work), 328 | typeof(*headset), 329 | work_power_on); 330 | struct gip_client *client = headset->client; 331 | int err; 332 | 333 | err = gip_set_power_mode(client, GIP_PWR_ON); 334 | if (err) { 335 | dev_err(&client->dev, "%s: set power mode failed: %d\n", 336 | __func__, err); 337 | return; 338 | } 339 | 340 | /* not a standalone headset */ 341 | if (client->id) 342 | return; 343 | 344 | err = gip_init_battery(&headset->battery, client, GIP_HS_NAME); 345 | if (err) { 346 | dev_err(&client->dev, "%s: init battery failed: %d\n", 347 | __func__, err); 348 | return; 349 | } 350 | 351 | err = gip_auth_start_handshake(&headset->auth, client); 352 | if (err) 353 | dev_err(&client->dev, "%s: start handshake failed: %d\n", 354 | __func__, err); 355 | } 356 | 357 | static void gip_headset_register(struct work_struct *work) 358 | { 359 | struct gip_headset *headset = container_of(work, typeof(*headset), 360 | work_register); 361 | struct gip_client *client = headset->client; 362 | int err; 363 | 364 | headset->buffer = devm_kzalloc(&client->dev, 365 | client->audio_config_out.buffer_size, 366 | GFP_KERNEL); 367 | if (!headset->buffer) 368 | return; 369 | 370 | err = gip_headset_init_pcm(headset); 371 | if (err) { 372 | dev_err(&client->dev, "%s: init PCM failed: %d\n", 373 | __func__, err); 374 | return; 375 | } 376 | 377 | /* set hardware volume to maximum for headset jack */ 378 | /* standalone & chat headsets have physical volume controls */ 379 | if (client->id && !headset->chat_headset) { 380 | err = gip_set_audio_volume(client, 100, 50, 100); 381 | if (err) { 382 | dev_err(&client->dev, "%s: set volume failed: %d\n", 383 | __func__, err); 384 | return; 385 | } 386 | } 387 | 388 | err = gip_init_audio_out(client); 389 | if (err) { 390 | dev_err(&client->dev, "%s: init audio out failed: %d\n", 391 | __func__, err); 392 | return; 393 | } 394 | 395 | hrtimer_start(&headset->timer, 0, HRTIMER_MODE_REL); 396 | } 397 | 398 | static int gip_headset_op_battery(struct gip_client *client, 399 | enum gip_battery_type type, 400 | enum gip_battery_level level) 401 | { 402 | struct gip_headset *headset = dev_get_drvdata(&client->dev); 403 | 404 | gip_report_battery(&headset->battery, type, level); 405 | 406 | return 0; 407 | } 408 | 409 | static int gip_headset_op_authenticate(struct gip_client *client, 410 | void *data, u32 len) 411 | { 412 | struct gip_headset *headset = dev_get_drvdata(&client->dev); 413 | 414 | return gip_auth_process_pkt(&headset->auth, data, len); 415 | } 416 | 417 | static void gip_headset_maybe_register(struct gip_headset *headset) 418 | { 419 | if (!headset->got_ready || !headset->got_initial_volume) 420 | return; 421 | 422 | /* ready signal is required as client->audio_config_out is initialized in it */ 423 | /* and used inside work_register -> gip_init_audio_out */ 424 | if (!headset->registered) { 425 | schedule_work(&headset->work_register); 426 | headset->registered = true; 427 | } 428 | } 429 | 430 | static int gip_headset_op_audio_ready(struct gip_client *client) 431 | { 432 | struct gip_headset *headset = dev_get_drvdata(&client->dev); 433 | 434 | headset->got_ready = true; 435 | /* headset reported supported audio formats */ 436 | /* (necessary for buffer size calculcations) */ 437 | gip_headset_maybe_register(headset); 438 | 439 | schedule_delayed_work(&headset->work_power_on, GIP_HS_POWER_ON_DELAY); 440 | 441 | return 0; 442 | } 443 | 444 | static int gip_headset_op_audio_volume(struct gip_client *client, 445 | u8 in, u8 out) 446 | { 447 | struct gip_headset *headset = dev_get_drvdata(&client->dev); 448 | 449 | /* headset reported initial volume, maybe start audio I/O */ 450 | headset->got_initial_volume = true; 451 | gip_headset_maybe_register(headset); 452 | 453 | /* ignore hardware volume, let software handle volume changes */ 454 | return 0; 455 | } 456 | 457 | static int gip_headset_op_audio_samples(struct gip_client *client, 458 | void *data, u32 len) 459 | { 460 | struct gip_headset *headset = dev_get_drvdata(&client->dev); 461 | struct snd_pcm_substream *sub = headset->capture.substream; 462 | bool elapsed = false; 463 | unsigned long flags; 464 | 465 | if (!sub) 466 | return 0; 467 | 468 | snd_pcm_stream_lock_irqsave(sub, flags); 469 | 470 | if (sub->runtime && snd_pcm_running(sub)) 471 | elapsed = gip_headset_copy_capture(&headset->capture, 472 | data, len); 473 | 474 | snd_pcm_stream_unlock_irqrestore(sub, flags); 475 | 476 | if (elapsed) 477 | snd_pcm_period_elapsed(sub); 478 | 479 | return 0; 480 | } 481 | 482 | static int gip_headset_probe(struct gip_client *client) 483 | { 484 | struct gip_headset *headset; 485 | struct gip_info_element *fmts = client->audio_formats; 486 | int err; 487 | 488 | if (!fmts || !fmts->count) 489 | return -ENODEV; 490 | 491 | headset = devm_kzalloc(&client->dev, sizeof(*headset), GFP_KERNEL); 492 | if (!headset) 493 | return -ENOMEM; 494 | 495 | headset->client = client; 496 | headset->chat_headset = client->hardware.vendor == GIP_VID_MICROSOFT && 497 | client->hardware.product == GIP_HS_PID_CHAT; 498 | 499 | INIT_DELAYED_WORK(&headset->work_config, gip_headset_config); 500 | INIT_DELAYED_WORK(&headset->work_power_on, gip_headset_power_on); 501 | INIT_WORK(&headset->work_register, gip_headset_register); 502 | 503 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,15,0) 504 | hrtimer_setup(&headset->timer, gip_headset_send_samples, 505 | CLOCK_MONOTONIC, HRTIMER_MODE_REL); 506 | #else 507 | hrtimer_init(&headset->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 508 | headset->timer.function = gip_headset_send_samples; 509 | #endif 510 | 511 | err = gip_enable_audio(client); 512 | if (err) 513 | return err; 514 | 515 | err = gip_init_audio_in(client); 516 | if (err) { 517 | gip_disable_audio(client); 518 | return err; 519 | } 520 | 521 | dev_set_drvdata(&client->dev, headset); 522 | 523 | /* delay to prevent response from being dropped */ 524 | schedule_delayed_work(&headset->work_config, GIP_HS_CONFIG_DELAY); 525 | 526 | return 0; 527 | } 528 | 529 | static void gip_headset_remove(struct gip_client *client) 530 | { 531 | struct gip_headset *headset = dev_get_drvdata(&client->dev); 532 | 533 | cancel_delayed_work_sync(&headset->work_config); 534 | cancel_delayed_work_sync(&headset->work_power_on); 535 | cancel_work_sync(&headset->work_register); 536 | hrtimer_cancel(&headset->timer); 537 | gip_disable_audio(client); 538 | 539 | if (headset->card) { 540 | snd_card_disconnect(headset->card); 541 | snd_card_free_when_closed(headset->card); 542 | } 543 | } 544 | 545 | static struct gip_driver gip_headset_driver = { 546 | .name = "xone-gip-headset", 547 | .class = "Windows.Xbox.Input.Headset", 548 | .ops = { 549 | .battery = gip_headset_op_battery, 550 | .authenticate = gip_headset_op_authenticate, 551 | .audio_ready = gip_headset_op_audio_ready, 552 | .audio_volume = gip_headset_op_audio_volume, 553 | .audio_samples = gip_headset_op_audio_samples, 554 | }, 555 | .probe = gip_headset_probe, 556 | .remove = gip_headset_remove, 557 | }; 558 | module_gip_driver(gip_headset_driver); 559 | 560 | MODULE_ALIAS("gip:Windows.Xbox.Input.Headset"); 561 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 562 | MODULE_DESCRIPTION("xone GIP headset driver"); 563 | MODULE_VERSION("#VERSION#"); 564 | MODULE_LICENSE("GPL"); 565 | -------------------------------------------------------------------------------- /driver/madcatz_glam.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2024 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | 8 | #include "common.h" 9 | #include "../auth/auth.h" 10 | 11 | #define GIP_GL_NAME "Mad Catz Rock Band 4 Drum Kit" 12 | 13 | enum gip_glam_button { 14 | GIP_GL_BTN_MENU = BIT(2), 15 | GIP_GL_BTN_VIEW = BIT(3), 16 | GIP_GL_BTN_A = BIT(4), 17 | GIP_GL_BTN_B = BIT(5), 18 | /* swapped X and Y buttons */ 19 | GIP_GL_BTN_X = BIT(7), 20 | GIP_GL_BTN_Y = BIT(6), 21 | GIP_GL_BTN_DPAD_U = BIT(8), 22 | GIP_GL_BTN_DPAD_D = BIT(9), 23 | GIP_GL_BTN_DPAD_L = BIT(10), 24 | GIP_GL_BTN_DPAD_R = BIT(11), 25 | GIP_GL_BTN_KICK_1 = BIT(12), 26 | GIP_GL_BTN_KICK_2 = BIT(13), 27 | }; 28 | 29 | enum gip_glam_pad { 30 | GIP_GL_PAD_YELLOW = BIT(0) | BIT(1) | BIT(2), 31 | GIP_GL_PAD_RED = BIT(4) | BIT(5) | BIT(6), 32 | GIP_GL_PAD_GREEN = BIT(8) | BIT(9) | BIT(10), 33 | GIP_GL_PAD_BLUE = BIT(12) | BIT(13) | BIT(14), 34 | }; 35 | 36 | enum gip_glam_cymbal { 37 | GIP_GL_CBL_BLUE = BIT(0) | BIT(1) | BIT(2), 38 | GIP_GL_CBL_YELLOW = BIT(4) | BIT(5) | BIT(6), 39 | GIP_GL_CBL_GREEN = BIT(12) | BIT(13) | BIT(14), 40 | }; 41 | 42 | struct gip_glam_pkt_input { 43 | __le16 buttons; 44 | __le16 pads; 45 | __le16 cymbals; 46 | } __packed; 47 | 48 | struct gip_glam { 49 | struct gip_client *client; 50 | struct gip_battery battery; 51 | struct gip_input input; 52 | }; 53 | 54 | static int gip_glam_init_input(struct gip_glam *glam) 55 | { 56 | struct input_dev *dev = glam->input.dev; 57 | int err; 58 | 59 | input_set_capability(dev, EV_KEY, BTN_MODE); 60 | input_set_capability(dev, EV_KEY, BTN_START); 61 | input_set_capability(dev, EV_KEY, BTN_SELECT); 62 | input_set_capability(dev, EV_KEY, BTN_A); 63 | input_set_capability(dev, EV_KEY, BTN_B); 64 | input_set_capability(dev, EV_KEY, BTN_X); 65 | input_set_capability(dev, EV_KEY, BTN_Y); 66 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY1); 67 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY2); 68 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY3); 69 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY4); 70 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY5); 71 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY6); 72 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY7); 73 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY8); 74 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY9); 75 | input_set_abs_params(dev, ABS_HAT0X, -1, 1, 0, 0); 76 | input_set_abs_params(dev, ABS_HAT0Y, -1, 1, 0, 0); 77 | 78 | err = input_register_device(dev); 79 | if (err) 80 | dev_err(&glam->client->dev, "%s: register failed: %d\n", 81 | __func__, err); 82 | 83 | return err; 84 | } 85 | 86 | static int gip_glam_op_battery(struct gip_client *client, 87 | enum gip_battery_type type, 88 | enum gip_battery_level level) 89 | { 90 | struct gip_glam *glam = dev_get_drvdata(&client->dev); 91 | 92 | gip_report_battery(&glam->battery, type, level); 93 | 94 | return 0; 95 | } 96 | 97 | static int gip_glam_op_guide_button(struct gip_client *client, bool down) 98 | { 99 | struct gip_glam *glam = dev_get_drvdata(&client->dev); 100 | 101 | input_report_key(glam->input.dev, BTN_MODE, down); 102 | input_sync(glam->input.dev); 103 | 104 | return 0; 105 | } 106 | 107 | static int gip_glam_op_input(struct gip_client *client, void *data, u32 len) 108 | { 109 | struct gip_glam *glam = dev_get_drvdata(&client->dev); 110 | struct gip_glam_pkt_input *pkt = data; 111 | struct input_dev *dev = glam->input.dev; 112 | u16 buttons; 113 | u16 pads; 114 | u16 cymbals; 115 | 116 | if (len < sizeof(*pkt)) 117 | return -EINVAL; 118 | 119 | buttons = le16_to_cpu(pkt->buttons); 120 | pads = le16_to_cpu(pkt->pads); 121 | cymbals = le16_to_cpu(pkt->cymbals); 122 | 123 | input_report_key(dev, BTN_START, buttons & GIP_GL_BTN_MENU); 124 | input_report_key(dev, BTN_SELECT, buttons & GIP_GL_BTN_VIEW); 125 | input_report_key(dev, BTN_A, buttons & GIP_GL_BTN_A); 126 | input_report_key(dev, BTN_B, buttons & GIP_GL_BTN_B); 127 | input_report_key(dev, BTN_X, buttons & GIP_GL_BTN_X); 128 | input_report_key(dev, BTN_Y, buttons & GIP_GL_BTN_Y); 129 | input_report_key(dev, BTN_TRIGGER_HAPPY1, buttons & GIP_GL_BTN_KICK_1); 130 | input_report_key(dev, BTN_TRIGGER_HAPPY2, buttons & GIP_GL_BTN_KICK_2); 131 | input_report_key(dev, BTN_TRIGGER_HAPPY3, pads & GIP_GL_PAD_RED); 132 | input_report_key(dev, BTN_TRIGGER_HAPPY4, pads & GIP_GL_PAD_YELLOW); 133 | input_report_key(dev, BTN_TRIGGER_HAPPY5, pads & GIP_GL_PAD_BLUE); 134 | input_report_key(dev, BTN_TRIGGER_HAPPY6, pads & GIP_GL_PAD_GREEN); 135 | input_report_key(dev, BTN_TRIGGER_HAPPY7, cymbals & GIP_GL_CBL_YELLOW); 136 | input_report_key(dev, BTN_TRIGGER_HAPPY8, cymbals & GIP_GL_CBL_BLUE); 137 | input_report_key(dev, BTN_TRIGGER_HAPPY9, cymbals & GIP_GL_CBL_GREEN); 138 | input_report_abs(dev, ABS_HAT0X, !!(buttons & GIP_GL_BTN_DPAD_R) - 139 | !!(buttons & GIP_GL_BTN_DPAD_L)); 140 | input_report_abs(dev, ABS_HAT0Y, !!(buttons & GIP_GL_BTN_DPAD_D) - 141 | !!(buttons & GIP_GL_BTN_DPAD_U)); 142 | input_sync(dev); 143 | 144 | return 0; 145 | } 146 | 147 | static int gip_glam_probe(struct gip_client *client) 148 | { 149 | struct gip_glam *glam; 150 | int err; 151 | 152 | glam = devm_kzalloc(&client->dev, sizeof(*glam), GFP_KERNEL); 153 | if (!glam) 154 | return -ENOMEM; 155 | 156 | glam->client = client; 157 | 158 | err = gip_set_power_mode(client, GIP_PWR_ON); 159 | if (err) 160 | return err; 161 | 162 | err = gip_init_battery(&glam->battery, client, GIP_GL_NAME); 163 | if (err) 164 | return err; 165 | 166 | /* 167 | * The Drum Kit sends auth chunks without specifying the 168 | * acknowledgment option while still expecting an acknowledgment. 169 | * The Windows driver handles this by sending an acknowledgment 170 | * after 100 ms when no further chunks are received. 171 | * We skip the handshake instead, as it is not required. 172 | */ 173 | err = gip_auth_send_complete(client); 174 | if (err) 175 | return err; 176 | 177 | err = gip_init_input(&glam->input, client, GIP_GL_NAME); 178 | if (err) 179 | return err; 180 | 181 | err = gip_glam_init_input(glam); 182 | if (err) 183 | return err; 184 | 185 | dev_set_drvdata(&client->dev, glam); 186 | 187 | return 0; 188 | } 189 | 190 | static struct gip_driver gip_glam_driver = { 191 | .name = "xone-gip-madcatz-glam", 192 | .class = "MadCatz.Xbox.Drums.Glam", 193 | .ops = { 194 | .battery = gip_glam_op_battery, 195 | .guide_button = gip_glam_op_guide_button, 196 | .input = gip_glam_op_input, 197 | }, 198 | .probe = gip_glam_probe, 199 | }; 200 | module_gip_driver(gip_glam_driver); 201 | 202 | MODULE_ALIAS("gip:MadCatz.Xbox.Drums.Glam"); 203 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 204 | MODULE_DESCRIPTION("xone GIP Mad Catz Drum Kit driver"); 205 | MODULE_VERSION("#VERSION#"); 206 | MODULE_LICENSE("GPL"); 207 | -------------------------------------------------------------------------------- /driver/madcatz_strat.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | 8 | #include "common.h" 9 | #include "../auth/auth.h" 10 | 11 | #define GIP_ST_NAME "Mad Catz Rock Band 4 Stratocaster" 12 | 13 | enum gip_strat_button { 14 | GIP_ST_BTN_MENU = BIT(2), 15 | GIP_ST_BTN_VIEW = BIT(3), 16 | GIP_ST_BTN_DPAD_U = BIT(8), 17 | GIP_ST_BTN_DPAD_D = BIT(9), 18 | GIP_ST_BTN_DPAD_L = BIT(10), 19 | GIP_ST_BTN_DPAD_R = BIT(11), 20 | }; 21 | 22 | enum gip_strat_fret { 23 | GIP_ST_FRET_GREEN = BIT(0), 24 | GIP_ST_FRET_RED = BIT(1), 25 | GIP_ST_FRET_YELLOW = BIT(2), 26 | GIP_ST_FRET_BLUE = BIT(3), 27 | GIP_ST_FRET_ORANGE = BIT(4), 28 | }; 29 | 30 | struct gip_strat_pkt_input { 31 | __le16 buttons; 32 | u8 tilt; 33 | u8 whammy; 34 | u8 slider; 35 | u8 fret_upper; 36 | u8 fret_lower; 37 | } __packed; 38 | 39 | struct gip_strat { 40 | struct gip_client *client; 41 | struct gip_battery battery; 42 | struct gip_input input; 43 | }; 44 | 45 | static int gip_strat_init_input(struct gip_strat *strat) 46 | { 47 | struct input_dev *dev = strat->input.dev; 48 | int err; 49 | 50 | input_set_capability(dev, EV_KEY, BTN_MODE); 51 | input_set_capability(dev, EV_KEY, BTN_START); 52 | input_set_capability(dev, EV_KEY, BTN_SELECT); 53 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY1); 54 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY2); 55 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY3); 56 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY4); 57 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY5); 58 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY6); 59 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY7); 60 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY8); 61 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY9); 62 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY10); 63 | input_set_abs_params(dev, ABS_X, 0, 64, 0, 0); 64 | input_set_abs_params(dev, ABS_Y, 0, 255, 0, 0); 65 | input_set_abs_params(dev, ABS_Z, 0, 255, 0, 0); 66 | input_set_abs_params(dev, ABS_HAT0X, -1, 1, 0, 0); 67 | input_set_abs_params(dev, ABS_HAT0Y, -1, 1, 0, 0); 68 | 69 | err = input_register_device(dev); 70 | if (err) 71 | dev_err(&strat->client->dev, "%s: register failed: %d\n", 72 | __func__, err); 73 | 74 | return err; 75 | } 76 | 77 | static int gip_strat_op_battery(struct gip_client *client, 78 | enum gip_battery_type type, 79 | enum gip_battery_level level) 80 | { 81 | struct gip_strat *strat = dev_get_drvdata(&client->dev); 82 | 83 | gip_report_battery(&strat->battery, type, level); 84 | 85 | return 0; 86 | } 87 | 88 | static int gip_strat_op_guide_button(struct gip_client *client, bool down) 89 | { 90 | struct gip_strat *strat = dev_get_drvdata(&client->dev); 91 | 92 | input_report_key(strat->input.dev, BTN_MODE, down); 93 | input_sync(strat->input.dev); 94 | 95 | return 0; 96 | } 97 | 98 | static int gip_strat_op_input(struct gip_client *client, void *data, u32 len) 99 | { 100 | struct gip_strat *strat = dev_get_drvdata(&client->dev); 101 | struct gip_strat_pkt_input *pkt = data; 102 | struct input_dev *dev = strat->input.dev; 103 | u16 buttons; 104 | 105 | if (len < sizeof(*pkt)) 106 | return -EINVAL; 107 | 108 | buttons = le16_to_cpu(pkt->buttons); 109 | 110 | input_report_key(dev, BTN_START, buttons & GIP_ST_BTN_MENU); 111 | input_report_key(dev, BTN_SELECT, buttons & GIP_ST_BTN_VIEW); 112 | input_report_key(dev, BTN_TRIGGER_HAPPY1, 113 | pkt->fret_upper & GIP_ST_FRET_GREEN); 114 | input_report_key(dev, BTN_TRIGGER_HAPPY2, 115 | pkt->fret_upper & GIP_ST_FRET_RED); 116 | input_report_key(dev, BTN_TRIGGER_HAPPY3, 117 | pkt->fret_upper & GIP_ST_FRET_YELLOW); 118 | input_report_key(dev, BTN_TRIGGER_HAPPY4, 119 | pkt->fret_upper & GIP_ST_FRET_BLUE); 120 | input_report_key(dev, BTN_TRIGGER_HAPPY5, 121 | pkt->fret_upper & GIP_ST_FRET_ORANGE); 122 | input_report_key(dev, BTN_TRIGGER_HAPPY6, 123 | pkt->fret_lower & GIP_ST_FRET_GREEN); 124 | input_report_key(dev, BTN_TRIGGER_HAPPY7, 125 | pkt->fret_lower & GIP_ST_FRET_RED); 126 | input_report_key(dev, BTN_TRIGGER_HAPPY8, 127 | pkt->fret_lower & GIP_ST_FRET_YELLOW); 128 | input_report_key(dev, BTN_TRIGGER_HAPPY9, 129 | pkt->fret_lower & GIP_ST_FRET_BLUE); 130 | input_report_key(dev, BTN_TRIGGER_HAPPY10, 131 | pkt->fret_lower & GIP_ST_FRET_ORANGE); 132 | input_report_abs(dev, ABS_X, pkt->slider); 133 | input_report_abs(dev, ABS_Y, pkt->whammy); 134 | input_report_abs(dev, ABS_Z, pkt->tilt); 135 | input_report_abs(dev, ABS_HAT0X, !!(buttons & GIP_ST_BTN_DPAD_R) - 136 | !!(buttons & GIP_ST_BTN_DPAD_L)); 137 | input_report_abs(dev, ABS_HAT0Y, !!(buttons & GIP_ST_BTN_DPAD_D) - 138 | !!(buttons & GIP_ST_BTN_DPAD_U)); 139 | input_sync(dev); 140 | 141 | return 0; 142 | } 143 | 144 | static int gip_strat_probe(struct gip_client *client) 145 | { 146 | struct gip_strat *strat; 147 | int err; 148 | 149 | strat = devm_kzalloc(&client->dev, sizeof(*strat), GFP_KERNEL); 150 | if (!strat) 151 | return -ENOMEM; 152 | 153 | strat->client = client; 154 | 155 | err = gip_set_power_mode(client, GIP_PWR_ON); 156 | if (err) 157 | return err; 158 | 159 | err = gip_init_battery(&strat->battery, client, GIP_ST_NAME); 160 | if (err) 161 | return err; 162 | 163 | /* 164 | * The Stratocaster sends auth chunks without specifying the 165 | * acknowledgment option while still expecting an acknowledgment. 166 | * The Windows driver handles this by sending an acknowledgment 167 | * after 100 ms when no further chunks are received. 168 | * We skip the handshake instead, as it is not required. 169 | */ 170 | err = gip_auth_send_complete(client); 171 | if (err) 172 | return err; 173 | 174 | err = gip_init_input(&strat->input, client, GIP_ST_NAME); 175 | if (err) 176 | return err; 177 | 178 | err = gip_strat_init_input(strat); 179 | if (err) 180 | return err; 181 | 182 | dev_set_drvdata(&client->dev, strat); 183 | 184 | return 0; 185 | } 186 | 187 | static struct gip_driver gip_strat_driver = { 188 | .name = "xone-gip-madcatz-strat", 189 | .class = "MadCatz.Xbox.Guitar.Stratocaster", 190 | .ops = { 191 | .battery = gip_strat_op_battery, 192 | .guide_button = gip_strat_op_guide_button, 193 | .input = gip_strat_op_input, 194 | }, 195 | .probe = gip_strat_probe, 196 | }; 197 | module_gip_driver(gip_strat_driver); 198 | 199 | MODULE_ALIAS("gip:MadCatz.Xbox.Guitar.Stratocaster"); 200 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 201 | MODULE_DESCRIPTION("xone GIP Mad Catz Stratocaster driver"); 202 | MODULE_VERSION("#VERSION#"); 203 | MODULE_LICENSE("GPL"); 204 | -------------------------------------------------------------------------------- /driver/pdp_jaguar.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Severin von Wnuck-Lipinski 4 | * Copyright (C) 2023 Scott K Logan 5 | */ 6 | 7 | #include 8 | 9 | #include "common.h" 10 | #include "../auth/auth.h" 11 | 12 | #define GIP_JA_NAME "PDP Rock Band 4 Jaguar" 13 | 14 | enum gip_jaguar_button { 15 | GIP_JA_BTN_MENU = BIT(2), 16 | GIP_JA_BTN_VIEW = BIT(3), 17 | GIP_JA_BTN_DPAD_U = BIT(8), 18 | GIP_JA_BTN_DPAD_D = BIT(9), 19 | GIP_JA_BTN_DPAD_L = BIT(10), 20 | GIP_JA_BTN_DPAD_R = BIT(11), 21 | }; 22 | 23 | enum gip_jaguar_fret { 24 | GIP_JA_FRET_GREEN = BIT(4), 25 | GIP_JA_FRET_RED = BIT(5), 26 | GIP_JA_FRET_BLUE = BIT(6), 27 | GIP_JA_FRET_YELLOW = BIT(7), 28 | GIP_JA_FRET_ORANGE = BIT(12), 29 | GIP_JA_FRET_LOWER = BIT(14), 30 | }; 31 | 32 | struct gip_jaguar_pkt_input { 33 | __le16 buttons; 34 | u8 tilt; 35 | u8 whammy; 36 | } __packed; 37 | 38 | struct gip_jaguar { 39 | struct gip_client *client; 40 | struct gip_battery battery; 41 | struct gip_auth auth; 42 | struct gip_input input; 43 | }; 44 | 45 | static int gip_jaguar_init_input(struct gip_jaguar *guitar) 46 | { 47 | struct input_dev *dev = guitar->input.dev; 48 | int err; 49 | 50 | input_set_capability(dev, EV_KEY, BTN_MODE); 51 | input_set_capability(dev, EV_KEY, BTN_START); 52 | input_set_capability(dev, EV_KEY, BTN_SELECT); 53 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY1); 54 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY2); 55 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY3); 56 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY4); 57 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY5); 58 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY6); 59 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY7); 60 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY8); 61 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY9); 62 | input_set_capability(dev, EV_KEY, BTN_TRIGGER_HAPPY10); 63 | input_set_abs_params(dev, ABS_Y, 0, 255, 0, 0); 64 | input_set_abs_params(dev, ABS_Z, 0, 255, 0, 0); 65 | input_set_abs_params(dev, ABS_HAT0X, -1, 1, 0, 0); 66 | input_set_abs_params(dev, ABS_HAT0Y, -1, 1, 0, 0); 67 | 68 | err = input_register_device(dev); 69 | if (err) 70 | dev_err(&guitar->client->dev, "%s: register failed: %d\n", 71 | __func__, err); 72 | 73 | return err; 74 | } 75 | 76 | static int gip_jaguar_op_battery(struct gip_client *client, 77 | enum gip_battery_type type, 78 | enum gip_battery_level level) 79 | { 80 | struct gip_jaguar *guitar = dev_get_drvdata(&client->dev); 81 | 82 | gip_report_battery(&guitar->battery, type, level); 83 | 84 | return 0; 85 | } 86 | 87 | static int gip_jaguar_op_authenticate(struct gip_client *client, 88 | void *data, u32 len) 89 | { 90 | struct gip_jaguar *guitar = dev_get_drvdata(&client->dev); 91 | 92 | return gip_auth_process_pkt(&guitar->auth, data, len); 93 | } 94 | 95 | static int gip_jaguar_op_guide_button(struct gip_client *client, bool down) 96 | { 97 | struct gip_jaguar *guitar = dev_get_drvdata(&client->dev); 98 | 99 | input_report_key(guitar->input.dev, BTN_MODE, down); 100 | input_sync(guitar->input.dev); 101 | 102 | return 0; 103 | } 104 | 105 | static int gip_jaguar_op_input(struct gip_client *client, void *data, u32 len) 106 | { 107 | struct gip_jaguar *guitar = dev_get_drvdata(&client->dev); 108 | struct gip_jaguar_pkt_input *pkt = data; 109 | struct input_dev *dev = guitar->input.dev; 110 | u16 buttons; 111 | bool lower; 112 | 113 | if (len < sizeof(*pkt)) 114 | return -EINVAL; 115 | 116 | buttons = le16_to_cpu(pkt->buttons); 117 | lower = buttons & GIP_JA_FRET_LOWER; 118 | 119 | input_report_key(dev, BTN_START, buttons & GIP_JA_BTN_MENU); 120 | input_report_key(dev, BTN_SELECT, buttons & GIP_JA_BTN_VIEW); 121 | input_report_key(dev, BTN_TRIGGER_HAPPY1, 122 | (buttons & GIP_JA_FRET_GREEN) && !lower); 123 | input_report_key(dev, BTN_TRIGGER_HAPPY2, 124 | (buttons & GIP_JA_FRET_RED) && !lower); 125 | input_report_key(dev, BTN_TRIGGER_HAPPY3, 126 | (buttons & GIP_JA_FRET_YELLOW) && !lower); 127 | input_report_key(dev, BTN_TRIGGER_HAPPY4, 128 | (buttons & GIP_JA_FRET_BLUE) && !lower); 129 | input_report_key(dev, BTN_TRIGGER_HAPPY5, 130 | (buttons & GIP_JA_FRET_ORANGE) && !lower); 131 | input_report_key(dev, BTN_TRIGGER_HAPPY6, 132 | (buttons & GIP_JA_FRET_GREEN) && lower); 133 | input_report_key(dev, BTN_TRIGGER_HAPPY7, 134 | (buttons & GIP_JA_FRET_RED) && lower); 135 | input_report_key(dev, BTN_TRIGGER_HAPPY8, 136 | (buttons & GIP_JA_FRET_YELLOW) && lower); 137 | input_report_key(dev, BTN_TRIGGER_HAPPY9, 138 | (buttons & GIP_JA_FRET_BLUE) && lower); 139 | input_report_key(dev, BTN_TRIGGER_HAPPY10, 140 | (buttons & GIP_JA_FRET_ORANGE) && lower); 141 | input_report_abs(dev, ABS_Y, pkt->whammy); 142 | input_report_abs(dev, ABS_Z, pkt->tilt); 143 | input_report_abs(dev, ABS_HAT0X, !!(buttons & GIP_JA_BTN_DPAD_R) - 144 | !!(buttons & GIP_JA_BTN_DPAD_L)); 145 | input_report_abs(dev, ABS_HAT0Y, !!(buttons & GIP_JA_BTN_DPAD_D) - 146 | !!(buttons & GIP_JA_BTN_DPAD_U)); 147 | input_sync(dev); 148 | 149 | return 0; 150 | } 151 | 152 | static int gip_jaguar_probe(struct gip_client *client) 153 | { 154 | struct gip_jaguar *guitar; 155 | int err; 156 | 157 | guitar = devm_kzalloc(&client->dev, sizeof(*guitar), GFP_KERNEL); 158 | if (!guitar) 159 | return -ENOMEM; 160 | 161 | guitar->client = client; 162 | 163 | err = gip_set_power_mode(client, GIP_PWR_ON); 164 | if (err) 165 | return err; 166 | 167 | err = gip_init_battery(&guitar->battery, client, GIP_JA_NAME); 168 | if (err) 169 | return err; 170 | 171 | err = gip_auth_start_handshake(&guitar->auth, client); 172 | if (err) 173 | return err; 174 | 175 | err = gip_init_input(&guitar->input, client, GIP_JA_NAME); 176 | if (err) 177 | return err; 178 | 179 | err = gip_jaguar_init_input(guitar); 180 | if (err) 181 | return err; 182 | 183 | dev_set_drvdata(&client->dev, guitar); 184 | 185 | return 0; 186 | } 187 | 188 | static struct gip_driver gip_jaguar_driver = { 189 | .name = "xone-gip-pdp-jaguar", 190 | .class = "PDP.Xbox.Guitar.Jaguar", 191 | .ops = { 192 | .battery = gip_jaguar_op_battery, 193 | .authenticate = gip_jaguar_op_authenticate, 194 | .guide_button = gip_jaguar_op_guide_button, 195 | .input = gip_jaguar_op_input, 196 | }, 197 | .probe = gip_jaguar_probe, 198 | }; 199 | module_gip_driver(gip_jaguar_driver); 200 | 201 | MODULE_ALIAS("gip:PDP.Xbox.Guitar.Jaguar"); 202 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 203 | MODULE_AUTHOR("Scott K Logan "); 204 | MODULE_DESCRIPTION("xone GIP PDP Jaguar driver"); 205 | MODULE_VERSION("#VERSION#"); 206 | MODULE_LICENSE("GPL"); 207 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | if [ "$(id -u)" -ne 0 ]; then 6 | echo 'This script must be run as root!' >&2 7 | exit 1 8 | fi 9 | 10 | if ! [ -x "$(command -v dkms)" ]; then 11 | echo 'This script requires DKMS!' >&2 12 | exit 1 13 | fi 14 | 15 | if [ -n "$(dkms status xone)" ]; then 16 | echo 'Driver is already installed!' >&2 17 | exit 1 18 | fi 19 | 20 | if [ -f /usr/local/bin/xow ]; then 21 | echo 'Please uninstall xow!' >&2 22 | exit 1 23 | fi 24 | 25 | if [ -n "${SUDO_USER:-}" ]; then 26 | # Run as normal user to prevent "unsafe repository" error 27 | version=$(sudo -u "$SUDO_USER" git describe --tags 2> /dev/null || echo unknown) 28 | else 29 | version=unknown 30 | fi 31 | 32 | source="/usr/src/xone-$version" 33 | log="/var/lib/dkms/xone/$version/build/make.log" 34 | 35 | echo "Installing xone $version..." 36 | cp -r . "$source" 37 | find "$source" -type f \( -name dkms.conf -o -name '*.c' \) -exec sed -i "s/#VERSION#/$version/" {} + 38 | 39 | if [ "${1:-}" != --release ]; then 40 | echo 'ccflags-y += -DDEBUG' >> "$source/Kbuild" 41 | fi 42 | 43 | if dkms install -m xone -v "$version"; then 44 | # The blacklist should be placed in /usr/local/lib/modprobe.d for kmod 29+ 45 | install -D -m 644 install/modprobe.conf /etc/modprobe.d/xone-blacklist.conf 46 | install -D -m 755 install/firmware.sh /usr/local/bin/xone-get-firmware.sh 47 | 48 | # Avoid conflicts between xpad and xone 49 | if lsmod | grep -q '^xpad'; then 50 | modprobe -r xpad 51 | fi 52 | 53 | # Avoid conflicts between mt76x2u and xone 54 | if lsmod | grep -q '^mt76x2u'; then 55 | modprobe -r mt76x2u 56 | fi 57 | else 58 | if [ -r "$log" ]; then 59 | cat "$log" >&2 60 | fi 61 | 62 | exit 1 63 | fi 64 | -------------------------------------------------------------------------------- /install/firmware.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | if [ "$(id -u)" -ne 0 ]; then 6 | echo 'This script must be run as root!' >&2 7 | exit 1 8 | fi 9 | 10 | if ! [ -x "$(command -v curl)" ]; then 11 | echo 'This script requires curl!' >&2 12 | exit 1 13 | fi 14 | 15 | if ! [ -x "$(command -v cabextract)" ]; then 16 | echo 'This script requires cabextract!' >&2 17 | exit 1 18 | fi 19 | 20 | if [ "${1:-}" != --skip-disclaimer ]; then 21 | echo "The firmware for the wireless dongle is subject to Microsoft's Terms of Use:" 22 | echo 'https://www.microsoft.com/en-us/legal/terms-of-use' 23 | echo 24 | echo 'Press enter to continue!' 25 | read -r _ 26 | fi 27 | 28 | driver_url='https://catalog.s.download.windowsupdate.com/c/msdownload/update/driver/drvs/2017/07/1cd6a87c-623f-4407-a52d-c31be49e925c_e19f60808bdcbfbd3c3df6be3e71ffc52e43261e.cab' 29 | firmware_hash='48084d9fa53b9bb04358f3bb127b7495dc8f7bb0b3ca1437bd24ef2b6eabdf66' 30 | 31 | curl -L -o driver.cab "$driver_url" 32 | cabextract -F FW_ACC_00U.bin driver.cab 33 | echo "$firmware_hash" FW_ACC_00U.bin | sha256sum -c 34 | mv FW_ACC_00U.bin /lib/firmware/xow_dongle.bin 35 | rm driver.cab 36 | 37 | driver_url='https://catalog.s.download.windowsupdate.com/d/msdownload/update/driver/drvs/2015/12/20810869_8ce2975a7fbaa06bcfb0d8762a6275a1cf7c1dd3.cab' 38 | firmware_hash='080ce4091e53a4ef3e5fe29939f51fd91f46d6a88be6d67eb6e99a5723b3a223' 39 | 40 | curl -L -o driver.cab "$driver_url" 41 | cabextract -F FW_ACC_00U.bin driver.cab 42 | echo "$firmware_hash" FW_ACC_00U.bin | sha256sum -c 43 | mv FW_ACC_00U.bin /lib/firmware/xow_dongle_045e_02e6.bin 44 | rm driver.cab 45 | -------------------------------------------------------------------------------- /install/modprobe.conf: -------------------------------------------------------------------------------- 1 | blacklist xpad 2 | blacklist mt76x2u 3 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /transport/dongle.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "mt76.h" 18 | #include "../bus/bus.h" 19 | 20 | #define XONE_DONGLE_NUM_IN_URBS 12 21 | #define XONE_DONGLE_NUM_OUT_URBS 12 22 | 23 | #define XONE_DONGLE_LEN_CMD_PKT 0x0654 24 | #define XONE_DONGLE_LEN_WLAN_PKT 0x8400 25 | 26 | #define XONE_DONGLE_MAX_CLIENTS 16 27 | 28 | /* autosuspend delay in ms */ 29 | #define XONE_DONGLE_SUSPEND_DELAY 60000 30 | 31 | #define XONE_DONGLE_PAIRING_TIMEOUT msecs_to_jiffies(30000) 32 | #define XONE_DONGLE_PWR_OFF_TIMEOUT msecs_to_jiffies(5000) 33 | 34 | enum xone_dongle_queue { 35 | XONE_DONGLE_QUEUE_DATA = 0x00, 36 | XONE_DONGLE_QUEUE_AUDIO = 0x02, 37 | }; 38 | 39 | struct xone_dongle_skb_cb { 40 | struct xone_dongle *dongle; 41 | struct urb *urb; 42 | }; 43 | 44 | struct xone_dongle_client { 45 | struct xone_dongle *dongle; 46 | u8 wcid; 47 | u8 address[ETH_ALEN]; 48 | bool encryption_enabled; 49 | 50 | struct gip_adapter *adapter; 51 | }; 52 | 53 | struct xone_dongle_event { 54 | enum xone_dongle_event_type { 55 | XONE_DONGLE_EVT_ADD_CLIENT, 56 | XONE_DONGLE_EVT_REMOVE_CLIENT, 57 | XONE_DONGLE_EVT_PAIR_CLIENT, 58 | XONE_DONGLE_EVT_ENABLE_PAIRING, 59 | XONE_DONGLE_EVT_ENABLE_ENCRYPTION, 60 | } type; 61 | 62 | struct xone_dongle *dongle; 63 | u8 address[ETH_ALEN]; 64 | u8 wcid; 65 | 66 | struct work_struct work; 67 | }; 68 | 69 | struct xone_dongle { 70 | struct xone_mt76 mt; 71 | 72 | struct usb_anchor urbs_in_idle; 73 | struct usb_anchor urbs_in_busy; 74 | struct usb_anchor urbs_out_idle; 75 | struct usb_anchor urbs_out_busy; 76 | 77 | /* serializes pairing changes */ 78 | struct mutex pairing_lock; 79 | struct delayed_work pairing_work; 80 | bool pairing; 81 | 82 | /* serializes access to clients array */ 83 | spinlock_t clients_lock; 84 | struct xone_dongle_client *clients[XONE_DONGLE_MAX_CLIENTS]; 85 | atomic_t client_count; 86 | wait_queue_head_t disconnect_wait; 87 | 88 | struct workqueue_struct *event_wq; 89 | }; 90 | 91 | static void xone_dongle_prep_packet(struct xone_dongle_client *client, 92 | struct sk_buff *skb, 93 | enum xone_dongle_queue queue) 94 | { 95 | struct ieee80211_qos_hdr hdr = {}; 96 | struct mt76_txwi txwi = {}; 97 | u8 data[] = { 98 | 0x00, 0x00, queue, client->wcid - 1, 0x00, 0x00, 0x00, 0x00, 99 | }; 100 | 101 | /* frame is sent from AP (DS) */ 102 | /* duration is the time required to transmit (in μs) */ 103 | hdr.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | 104 | IEEE80211_STYPE_QOS_DATA | 105 | IEEE80211_FCTL_FROMDS); 106 | 107 | /* encrypt frame on transmission */ 108 | if (client->encryption_enabled) 109 | hdr.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 110 | 111 | hdr.duration_id = cpu_to_le16(144); 112 | memcpy(hdr.addr1, client->address, ETH_ALEN); 113 | memcpy(hdr.addr2, client->dongle->mt.address, ETH_ALEN); 114 | memcpy(hdr.addr3, client->dongle->mt.address, ETH_ALEN); 115 | 116 | /* wait for acknowledgment */ 117 | txwi.flags = cpu_to_le16(FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, 118 | IEEE80211_HT_MPDU_DENSITY_4)); 119 | txwi.rate = cpu_to_le16(FIELD_PREP(MT_RXWI_RATE_PHY, MT_PHY_TYPE_OFDM)); 120 | txwi.ack_ctl = MT_TXWI_ACK_CTL_REQ; 121 | txwi.wcid = client->wcid - 1; 122 | txwi.len_ctl = cpu_to_le16(sizeof(hdr) + skb->len); 123 | 124 | memset(skb_push(skb, 2), 0, 2); 125 | memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); 126 | memcpy(skb_push(skb, sizeof(txwi)), &txwi, sizeof(txwi)); 127 | memcpy(skb_push(skb, sizeof(data)), data, sizeof(data)); 128 | 129 | xone_mt76_prep_command(skb, 0); 130 | } 131 | 132 | static int xone_dongle_get_buffer(struct gip_adapter *adap, 133 | struct gip_adapter_buffer *buf) 134 | { 135 | struct xone_dongle_client *client = dev_get_drvdata(&adap->dev); 136 | struct xone_dongle_skb_cb *cb; 137 | struct urb *urb; 138 | struct sk_buff *skb; 139 | 140 | urb = usb_get_from_anchor(&client->dongle->urbs_out_idle); 141 | if (!urb) 142 | return -ENOSPC; 143 | 144 | skb = xone_mt76_alloc_message(XONE_DONGLE_LEN_CMD_PKT, GFP_ATOMIC); 145 | if (!skb) 146 | return -ENOMEM; 147 | 148 | /* command header + WCID data + TXWI + QoS header + padding */ 149 | /* see xone_dongle_prep_packet and xone_mt76_prep_message */ 150 | skb_reserve(skb, MT_CMD_HDR_LEN + 8 + sizeof(struct mt76_txwi) + 151 | sizeof(struct ieee80211_qos_hdr) + 2 + MT_CMD_HDR_LEN); 152 | 153 | cb = (struct xone_dongle_skb_cb *)skb->cb; 154 | cb->dongle = client->dongle; 155 | cb->urb = urb; 156 | 157 | buf->context = skb; 158 | buf->data = skb->data; 159 | buf->length = skb_tailroom(skb); 160 | 161 | return 0; 162 | } 163 | 164 | static int xone_dongle_submit_buffer(struct gip_adapter *adap, 165 | struct gip_adapter_buffer *buf) 166 | { 167 | struct xone_dongle_client *client = dev_get_drvdata(&adap->dev); 168 | struct xone_dongle_skb_cb *cb; 169 | struct sk_buff *skb = buf->context; 170 | int err; 171 | 172 | skb_put(skb, buf->length); 173 | 174 | if (buf->type == GIP_BUF_DATA) 175 | xone_dongle_prep_packet(client, skb, XONE_DONGLE_QUEUE_DATA); 176 | else if (buf->type == GIP_BUF_AUDIO) 177 | xone_dongle_prep_packet(client, skb, XONE_DONGLE_QUEUE_AUDIO); 178 | else 179 | return -EINVAL; 180 | 181 | cb = (struct xone_dongle_skb_cb *)skb->cb; 182 | cb->urb->context = skb; 183 | cb->urb->transfer_buffer = skb->data; 184 | cb->urb->transfer_buffer_length = skb->len; 185 | usb_anchor_urb(cb->urb, &client->dongle->urbs_out_busy); 186 | 187 | err = usb_submit_urb(cb->urb, GFP_ATOMIC); 188 | if (err) { 189 | usb_unanchor_urb(cb->urb); 190 | usb_anchor_urb(cb->urb, &client->dongle->urbs_out_idle); 191 | dev_kfree_skb_any(skb); 192 | } 193 | 194 | usb_free_urb(cb->urb); 195 | 196 | return err; 197 | } 198 | 199 | static int xone_dongle_set_encryption_key(struct gip_adapter *adap, 200 | u8 *key, int len) 201 | { 202 | struct xone_dongle_client *client = dev_get_drvdata(&adap->dev); 203 | 204 | return xone_mt76_set_client_key(&client->dongle->mt, client->wcid, 205 | key, len); 206 | } 207 | 208 | static struct gip_adapter_ops xone_dongle_adapter_ops = { 209 | .get_buffer = xone_dongle_get_buffer, 210 | .submit_buffer = xone_dongle_submit_buffer, 211 | .set_encryption_key = xone_dongle_set_encryption_key, 212 | }; 213 | 214 | static int xone_dongle_toggle_pairing(struct xone_dongle *dongle, bool enable) 215 | { 216 | struct usb_interface *intf = to_usb_interface(dongle->mt.dev); 217 | enum xone_mt76_led_mode led; 218 | int err = 0; 219 | 220 | mutex_lock(&dongle->pairing_lock); 221 | 222 | /* pairing is already enabled/disabled */ 223 | if (dongle->pairing == enable) 224 | goto err_unlock; 225 | 226 | err = xone_mt76_set_pairing(&dongle->mt, enable); 227 | if (err) 228 | goto err_unlock; 229 | 230 | if (enable) 231 | led = XONE_MT_LED_BLINK; 232 | else if (atomic_read(&dongle->client_count)) 233 | led = XONE_MT_LED_ON; 234 | else 235 | led = XONE_MT_LED_OFF; 236 | 237 | err = xone_mt76_set_led_mode(&dongle->mt, led); 238 | if (err) 239 | goto err_unlock; 240 | 241 | if (enable) 242 | usb_autopm_get_interface(intf); 243 | else 244 | usb_autopm_put_interface(intf); 245 | 246 | dev_dbg(dongle->mt.dev, "%s: enabled=%d\n", __func__, enable); 247 | dongle->pairing = enable; 248 | 249 | err_unlock: 250 | mutex_unlock(&dongle->pairing_lock); 251 | 252 | return err; 253 | } 254 | 255 | static void xone_dongle_pairing_timeout(struct work_struct *work) 256 | { 257 | struct xone_dongle *dongle = container_of(to_delayed_work(work), 258 | typeof(*dongle), 259 | pairing_work); 260 | int err; 261 | 262 | err = xone_dongle_toggle_pairing(dongle, false); 263 | if (err) 264 | dev_err(dongle->mt.dev, "%s: disable pairing failed: %d\n", 265 | __func__, err); 266 | } 267 | 268 | static ssize_t xone_dongle_pairing_show(struct device *dev, 269 | struct device_attribute *attr, 270 | char *buf) 271 | { 272 | struct usb_interface *intf = to_usb_interface(dev); 273 | struct xone_dongle *dongle = usb_get_intfdata(intf); 274 | 275 | return sprintf(buf, "%d\n", dongle->pairing); 276 | } 277 | 278 | static ssize_t xone_dongle_pairing_store(struct device *dev, 279 | struct device_attribute *attr, 280 | const char *buf, size_t count) 281 | { 282 | struct usb_interface *intf = to_usb_interface(dev); 283 | struct xone_dongle *dongle = usb_get_intfdata(intf); 284 | bool enable; 285 | int err; 286 | 287 | err = kstrtobool(buf, &enable); 288 | if (err) 289 | return err; 290 | 291 | err = pm_runtime_resume_and_get(dev); 292 | if (err) 293 | return err; 294 | 295 | err = xone_dongle_toggle_pairing(dongle, enable); 296 | if (err) 297 | return err; 298 | 299 | pm_runtime_put(dev); 300 | 301 | return count; 302 | } 303 | 304 | static struct device_attribute xone_dongle_attr_pairing = 305 | __ATTR(pairing, 0644, 306 | xone_dongle_pairing_show, 307 | xone_dongle_pairing_store); 308 | 309 | static struct attribute *xone_dongle_attrs[] = { 310 | &xone_dongle_attr_pairing.attr, 311 | NULL, 312 | }; 313 | ATTRIBUTE_GROUPS(xone_dongle); 314 | 315 | static struct xone_dongle_client * 316 | xone_dongle_create_client(struct xone_dongle *dongle, u8 *addr) 317 | { 318 | struct xone_dongle_client *client; 319 | int i, err; 320 | 321 | /* find free WCID */ 322 | for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) 323 | if (!dongle->clients[i]) 324 | break; 325 | 326 | if (i == XONE_DONGLE_MAX_CLIENTS) 327 | return ERR_PTR(-ENOSPC); 328 | 329 | client = kzalloc(sizeof(*client), GFP_KERNEL); 330 | if (!client) 331 | return ERR_PTR(-ENOMEM); 332 | 333 | client->dongle = dongle; 334 | client->wcid = i + 1; 335 | memcpy(client->address, addr, ETH_ALEN); 336 | 337 | client->adapter = gip_create_adapter(dongle->mt.dev, 338 | &xone_dongle_adapter_ops, 1); 339 | if (IS_ERR(client->adapter)) { 340 | err = PTR_ERR(client->adapter); 341 | kfree(client); 342 | return ERR_PTR(err); 343 | } 344 | 345 | dev_set_drvdata(&client->adapter->dev, client); 346 | 347 | return client; 348 | } 349 | 350 | static int xone_dongle_add_client(struct xone_dongle *dongle, u8 *addr) 351 | { 352 | struct xone_dongle_client *client; 353 | int err; 354 | unsigned long flags; 355 | 356 | client = xone_dongle_create_client(dongle, addr); 357 | if (IS_ERR(client)) 358 | return PTR_ERR(client); 359 | 360 | err = xone_mt76_associate_client(&dongle->mt, client->wcid, addr); 361 | if (err) 362 | goto err_free_client; 363 | 364 | if (!dongle->pairing) { 365 | err = xone_mt76_set_led_mode(&dongle->mt, XONE_MT_LED_ON); 366 | if (err) 367 | goto err_free_client; 368 | } 369 | 370 | dev_dbg(dongle->mt.dev, "%s: wcid=%d, address=%pM\n", 371 | __func__, client->wcid, addr); 372 | 373 | spin_lock_irqsave(&dongle->clients_lock, flags); 374 | dongle->clients[client->wcid - 1] = client; 375 | spin_unlock_irqrestore(&dongle->clients_lock, flags); 376 | 377 | atomic_inc(&dongle->client_count); 378 | usb_autopm_get_interface(to_usb_interface(dongle->mt.dev)); 379 | 380 | return 0; 381 | 382 | err_free_client: 383 | gip_destroy_adapter(client->adapter); 384 | kfree(client); 385 | 386 | return err; 387 | } 388 | 389 | static int xone_dongle_remove_client(struct xone_dongle *dongle, u8 wcid) 390 | { 391 | struct xone_dongle_client *client; 392 | int err; 393 | unsigned long flags; 394 | 395 | client = dongle->clients[wcid - 1]; 396 | if (!client) 397 | return 0; 398 | 399 | dev_dbg(dongle->mt.dev, "%s: wcid=%d, address=%pM\n", 400 | __func__, wcid, client->address); 401 | 402 | spin_lock_irqsave(&dongle->clients_lock, flags); 403 | dongle->clients[wcid - 1] = NULL; 404 | spin_unlock_irqrestore(&dongle->clients_lock, flags); 405 | 406 | gip_destroy_adapter(client->adapter); 407 | kfree(client); 408 | 409 | err = xone_mt76_remove_client(&dongle->mt, wcid); 410 | if (err) 411 | dev_err(dongle->mt.dev, "%s: remove failed: %d\n", 412 | __func__, err); 413 | 414 | /* turn off LED if all clients have disconnected */ 415 | if (atomic_dec_and_test(&dongle->client_count) && !dongle->pairing) 416 | err = xone_mt76_set_led_mode(&dongle->mt, XONE_MT_LED_OFF); 417 | 418 | wake_up(&dongle->disconnect_wait); 419 | usb_autopm_put_interface(to_usb_interface(dongle->mt.dev)); 420 | 421 | return err; 422 | } 423 | 424 | static int xone_dongle_pair_client(struct xone_dongle *dongle, u8 *addr) 425 | { 426 | int err; 427 | 428 | dev_dbg(dongle->mt.dev, "%s: address=%pM\n", __func__, addr); 429 | 430 | err = xone_mt76_pair_client(&dongle->mt, addr); 431 | if (err) 432 | return err; 433 | 434 | return xone_dongle_toggle_pairing(dongle, false); 435 | } 436 | 437 | static int xone_dongle_enable_client_encryption(struct xone_dongle *dongle, 438 | u8 wcid) 439 | { 440 | struct xone_dongle_client *client; 441 | u8 data[] = { 0x00, 0x00 }; 442 | int err; 443 | 444 | client = dongle->clients[wcid - 1]; 445 | if (!client) 446 | return -EINVAL; 447 | 448 | dev_dbg(dongle->mt.dev, "%s: wcid=%d, address=%pM\n", 449 | __func__, wcid, client->address); 450 | 451 | err = xone_mt76_send_client_command(&dongle->mt, wcid, client->address, 452 | XONE_MT_CLIENT_ENABLE_ENCRYPTION, 453 | data, sizeof(data)); 454 | if (err) 455 | return err; 456 | 457 | client->encryption_enabled = true; 458 | 459 | return 0; 460 | } 461 | 462 | static void xone_dongle_handle_event(struct work_struct *work) 463 | { 464 | struct xone_dongle_event *evt = container_of(work, typeof(*evt), work); 465 | int err = 0; 466 | 467 | switch (evt->type) { 468 | case XONE_DONGLE_EVT_ADD_CLIENT: 469 | err = xone_dongle_add_client(evt->dongle, evt->address); 470 | break; 471 | case XONE_DONGLE_EVT_REMOVE_CLIENT: 472 | err = xone_dongle_remove_client(evt->dongle, evt->wcid); 473 | break; 474 | case XONE_DONGLE_EVT_PAIR_CLIENT: 475 | err = xone_dongle_pair_client(evt->dongle, evt->address); 476 | break; 477 | case XONE_DONGLE_EVT_ENABLE_PAIRING: 478 | mod_delayed_work(system_wq, &evt->dongle->pairing_work, 479 | XONE_DONGLE_PAIRING_TIMEOUT); 480 | err = xone_dongle_toggle_pairing(evt->dongle, true); 481 | break; 482 | case XONE_DONGLE_EVT_ENABLE_ENCRYPTION: 483 | err = xone_dongle_enable_client_encryption(evt->dongle, 484 | evt->wcid); 485 | break; 486 | } 487 | 488 | if (err) 489 | dev_err(evt->dongle->mt.dev, "%s: handle event failed: %d\n", 490 | __func__, err); 491 | 492 | kfree(evt); 493 | } 494 | 495 | static struct xone_dongle_event * 496 | xone_dongle_alloc_event(struct xone_dongle *dongle, 497 | enum xone_dongle_event_type type) 498 | { 499 | struct xone_dongle_event *evt; 500 | 501 | evt = kzalloc(sizeof(*evt), GFP_ATOMIC); 502 | if (!evt) 503 | return NULL; 504 | 505 | evt->type = type; 506 | evt->dongle = dongle; 507 | INIT_WORK(&evt->work, xone_dongle_handle_event); 508 | 509 | return evt; 510 | } 511 | 512 | static int xone_dongle_handle_qos_data(struct xone_dongle *dongle, 513 | struct sk_buff *skb, u8 wcid) 514 | { 515 | struct xone_dongle_client *client; 516 | int err = 0; 517 | unsigned long flags; 518 | 519 | if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) 520 | return 0; 521 | 522 | spin_lock_irqsave(&dongle->clients_lock, flags); 523 | 524 | client = dongle->clients[wcid - 1]; 525 | if (client) 526 | err = gip_process_buffer(client->adapter, skb->data, skb->len); 527 | 528 | spin_unlock_irqrestore(&dongle->clients_lock, flags); 529 | 530 | return err; 531 | } 532 | 533 | static int xone_dongle_handle_association(struct xone_dongle *dongle, u8 *addr) 534 | { 535 | struct xone_dongle_event *evt; 536 | 537 | evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_ADD_CLIENT); 538 | if (!evt) 539 | return -ENOMEM; 540 | 541 | memcpy(evt->address, addr, ETH_ALEN); 542 | 543 | queue_work(dongle->event_wq, &evt->work); 544 | 545 | return 0; 546 | } 547 | 548 | static int xone_dongle_handle_disassociation(struct xone_dongle *dongle, 549 | u8 wcid) 550 | { 551 | struct xone_dongle_event *evt; 552 | 553 | if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) 554 | return 0; 555 | 556 | evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_REMOVE_CLIENT); 557 | if (!evt) 558 | return -ENOMEM; 559 | 560 | evt->wcid = wcid; 561 | 562 | queue_work(dongle->event_wq, &evt->work); 563 | 564 | return 0; 565 | } 566 | 567 | static int xone_dongle_handle_client_command(struct xone_dongle *dongle, 568 | struct sk_buff *skb, 569 | u8 wcid, u8 *addr) 570 | { 571 | struct xone_dongle_event *evt; 572 | enum xone_dongle_event_type evt_type; 573 | 574 | if (skb->len < 2 || skb->data[0] != XONE_MT_WLAN_RESERVED) 575 | return -EINVAL; 576 | 577 | switch (skb->data[1]) { 578 | case XONE_MT_CLIENT_PAIR_REQ: 579 | evt_type = XONE_DONGLE_EVT_PAIR_CLIENT; 580 | break; 581 | case XONE_MT_CLIENT_ENABLE_ENCRYPTION: 582 | if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) 583 | return -EINVAL; 584 | 585 | evt_type = XONE_DONGLE_EVT_ENABLE_ENCRYPTION; 586 | break; 587 | default: 588 | return 0; 589 | } 590 | 591 | evt = xone_dongle_alloc_event(dongle, evt_type); 592 | if (!evt) 593 | return -ENOMEM; 594 | 595 | evt->wcid = wcid; 596 | memcpy(evt->address, addr, ETH_ALEN); 597 | 598 | queue_work(dongle->event_wq, &evt->work); 599 | 600 | return 0; 601 | } 602 | 603 | static int xone_dongle_handle_button(struct xone_dongle *dongle) 604 | { 605 | struct xone_dongle_event *evt; 606 | 607 | evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_ENABLE_PAIRING); 608 | if (!evt) 609 | return -ENOMEM; 610 | 611 | queue_work(dongle->event_wq, &evt->work); 612 | 613 | return 0; 614 | } 615 | 616 | static int xone_dongle_handle_loss(struct xone_dongle *dongle, 617 | struct sk_buff *skb) 618 | { 619 | u8 wcid; 620 | 621 | if (skb->len < sizeof(wcid)) 622 | return -EINVAL; 623 | 624 | wcid = skb->data[0]; 625 | if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) 626 | return 0; 627 | 628 | dev_dbg(dongle->mt.dev, "%s: wcid=%d\n", __func__, wcid); 629 | 630 | return xone_dongle_handle_disassociation(dongle, wcid); 631 | } 632 | 633 | static int xone_dongle_process_frame(struct xone_dongle *dongle, 634 | struct sk_buff *skb, 635 | unsigned int hdr_len, u8 wcid) 636 | { 637 | struct ieee80211_hdr_3addr *hdr = 638 | (struct ieee80211_hdr_3addr *)skb->data; 639 | u16 type; 640 | 641 | /* ignore invalid frames */ 642 | if (skb->len < hdr_len || hdr_len < sizeof(*hdr)) 643 | return 0; 644 | 645 | skb_pull(skb, hdr_len); 646 | type = le16_to_cpu(hdr->frame_control); 647 | 648 | switch (type & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) { 649 | case IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA: 650 | return xone_dongle_handle_qos_data(dongle, skb, wcid); 651 | case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ: 652 | return xone_dongle_handle_association(dongle, hdr->addr2); 653 | case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC: 654 | return xone_dongle_handle_disassociation(dongle, wcid); 655 | case IEEE80211_FTYPE_MGMT | XONE_MT_WLAN_RESERVED: 656 | return xone_dongle_handle_client_command(dongle, skb, wcid, 657 | hdr->addr2); 658 | } 659 | 660 | return 0; 661 | } 662 | 663 | static int xone_dongle_process_wlan(struct xone_dongle *dongle, 664 | struct sk_buff *skb) 665 | { 666 | struct mt76_rxwi *rxwi = (struct mt76_rxwi *)skb->data; 667 | unsigned int hdr_len; 668 | u32 ctl; 669 | 670 | if (skb->len < sizeof(*rxwi)) 671 | return -EINVAL; 672 | 673 | skb_pull(skb, sizeof(*rxwi)); 674 | hdr_len = ieee80211_get_hdrlen_from_skb(skb); 675 | 676 | /* 2 bytes of padding after 802.11 header */ 677 | if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) { 678 | if (skb->len < hdr_len + 2) 679 | return -EINVAL; 680 | 681 | memmove(skb->data + 2, skb->data, hdr_len); 682 | skb_pull(skb, 2); 683 | } 684 | 685 | ctl = le32_to_cpu(rxwi->ctl); 686 | skb_trim(skb, FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl)); 687 | 688 | return xone_dongle_process_frame(dongle, skb, hdr_len, 689 | FIELD_GET(MT_RXWI_CTL_WCID, ctl)); 690 | } 691 | 692 | static int xone_dongle_process_message(struct xone_dongle *dongle, 693 | struct sk_buff *skb) 694 | { 695 | enum mt76_dma_msg_port port; 696 | u32 info; 697 | 698 | /* command header + trailer */ 699 | if (skb->len < MT_CMD_HDR_LEN * 2) 700 | return -EINVAL; 701 | 702 | info = get_unaligned_le32(skb->data); 703 | port = FIELD_GET(MT_RX_FCE_INFO_D_PORT, info); 704 | 705 | /* ignore command reponses */ 706 | if (FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, info) == 0x01) 707 | return 0; 708 | 709 | /* remove header + trailer */ 710 | skb_pull(skb, MT_CMD_HDR_LEN); 711 | skb_trim(skb, skb->len - MT_CMD_HDR_LEN); 712 | 713 | if (port == MT_WLAN_PORT) 714 | return xone_dongle_process_wlan(dongle, skb); 715 | 716 | if (port != MT_CPU_RX_PORT) 717 | return 0; 718 | 719 | switch (FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, info)) { 720 | case XONE_MT_EVT_BUTTON: 721 | return xone_dongle_handle_button(dongle); 722 | case XONE_MT_EVT_PACKET_RX: 723 | return xone_dongle_process_wlan(dongle, skb); 724 | case XONE_MT_EVT_CLIENT_LOST: 725 | return xone_dongle_handle_loss(dongle, skb); 726 | } 727 | 728 | return 0; 729 | } 730 | 731 | static int xone_dongle_process_buffer(struct xone_dongle *dongle, 732 | void *data, int len) 733 | { 734 | struct sk_buff *skb; 735 | int err; 736 | 737 | if (!len) 738 | return 0; 739 | 740 | skb = dev_alloc_skb(len); 741 | if (!skb) 742 | return -ENOMEM; 743 | 744 | skb_put_data(skb, data, len); 745 | 746 | err = xone_dongle_process_message(dongle, skb); 747 | if (err) { 748 | dev_err(dongle->mt.dev, "%s: process failed: %d\n", 749 | __func__, err); 750 | print_hex_dump_debug("xone-dongle packet: ", DUMP_PREFIX_NONE, 751 | 16, 1, data, len, false); 752 | } 753 | 754 | dev_kfree_skb(skb); 755 | 756 | return err; 757 | } 758 | 759 | static void xone_dongle_complete_in(struct urb *urb) 760 | { 761 | struct xone_dongle *dongle = urb->context; 762 | int err; 763 | 764 | switch (urb->status) { 765 | case 0: 766 | break; 767 | case -ENOENT: 768 | case -ECONNRESET: 769 | case -ESHUTDOWN: 770 | usb_anchor_urb(urb, &dongle->urbs_in_idle); 771 | return; 772 | default: 773 | goto resubmit; 774 | } 775 | 776 | err = xone_dongle_process_buffer(dongle, urb->transfer_buffer, 777 | urb->actual_length); 778 | if (err) 779 | dev_err(dongle->mt.dev, "%s: process failed: %d\n", 780 | __func__, err); 781 | 782 | resubmit: 783 | /* can fail during USB device removal */ 784 | err = usb_submit_urb(urb, GFP_ATOMIC); 785 | if (err) { 786 | dev_dbg(dongle->mt.dev, "%s: submit failed: %d\n", 787 | __func__, err); 788 | usb_anchor_urb(urb, &dongle->urbs_in_idle); 789 | } else { 790 | usb_anchor_urb(urb, &dongle->urbs_in_busy); 791 | } 792 | } 793 | 794 | static void xone_dongle_complete_out(struct urb *urb) 795 | { 796 | struct sk_buff *skb = urb->context; 797 | struct xone_dongle_skb_cb *cb = (struct xone_dongle_skb_cb *)skb->cb; 798 | 799 | usb_anchor_urb(urb, &cb->dongle->urbs_out_idle); 800 | dev_consume_skb_any(skb); 801 | } 802 | 803 | static int xone_dongle_init_urbs_in(struct xone_dongle *dongle, 804 | int ep, int buf_len) 805 | { 806 | struct xone_mt76 *mt = &dongle->mt; 807 | struct urb *urb; 808 | void *buf; 809 | int i, err; 810 | 811 | for (i = 0; i < XONE_DONGLE_NUM_IN_URBS; i++) { 812 | urb = usb_alloc_urb(0, GFP_KERNEL); 813 | if (!urb) 814 | return -ENOMEM; 815 | 816 | usb_anchor_urb(urb, &dongle->urbs_in_busy); 817 | usb_free_urb(urb); 818 | 819 | buf = usb_alloc_coherent(mt->udev, buf_len, 820 | GFP_KERNEL, &urb->transfer_dma); 821 | if (!buf) 822 | return -ENOMEM; 823 | 824 | usb_fill_bulk_urb(urb, mt->udev, 825 | usb_rcvbulkpipe(mt->udev, ep), buf, buf_len, 826 | xone_dongle_complete_in, dongle); 827 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 828 | 829 | err = usb_submit_urb(urb, GFP_KERNEL); 830 | if (err) 831 | return err; 832 | } 833 | 834 | return 0; 835 | } 836 | 837 | static int xone_dongle_init_urbs_out(struct xone_dongle *dongle) 838 | { 839 | struct xone_mt76 *mt = &dongle->mt; 840 | struct urb *urb; 841 | int i; 842 | 843 | for (i = 0; i < XONE_DONGLE_NUM_OUT_URBS; i++) { 844 | urb = usb_alloc_urb(0, GFP_KERNEL); 845 | if (!urb) 846 | return -ENOMEM; 847 | 848 | usb_fill_bulk_urb(urb, mt->udev, 849 | usb_sndbulkpipe(mt->udev, XONE_MT_EP_OUT), 850 | NULL, 0, xone_dongle_complete_out, NULL); 851 | usb_anchor_urb(urb, &dongle->urbs_out_idle); 852 | usb_free_urb(urb); 853 | } 854 | 855 | return 0; 856 | } 857 | 858 | static int xone_dongle_init(struct xone_dongle *dongle, 859 | const struct usb_device_id *id) 860 | { 861 | struct xone_mt76 *mt = &dongle->mt; 862 | const struct firmware *fw; 863 | char fwname[25]; 864 | int err; 865 | 866 | init_usb_anchor(&dongle->urbs_out_idle); 867 | init_usb_anchor(&dongle->urbs_out_busy); 868 | init_usb_anchor(&dongle->urbs_in_idle); 869 | init_usb_anchor(&dongle->urbs_in_busy); 870 | 871 | err = xone_dongle_init_urbs_out(dongle); 872 | if (err) 873 | return err; 874 | 875 | err = xone_dongle_init_urbs_in(dongle, XONE_MT_EP_IN_CMD, 876 | XONE_DONGLE_LEN_CMD_PKT); 877 | if (err) 878 | return err; 879 | 880 | err = xone_dongle_init_urbs_in(dongle, XONE_MT_EP_IN_WLAN, 881 | XONE_DONGLE_LEN_WLAN_PKT); 882 | if (err) 883 | return err; 884 | 885 | snprintf(fwname, 25, "xow_dongle_%04x_%04x.bin", 886 | id->idVendor, id->idProduct); 887 | dev_dbg(dongle->mt.dev, "%s: trying to load firmware %s\n", 888 | __func__, fwname); 889 | err = request_firmware(&fw, fwname, mt->dev); 890 | if (err) { 891 | if (err == -ENOENT) { 892 | snprintf(fwname, 25, "xow_dongle.bin", 893 | id->idVendor, id->idProduct); 894 | dev_dbg(dongle->mt.dev, "%s: trying to load firmware %s\n", 895 | __func__, fwname); 896 | err = request_firmware(&fw, fwname, mt->dev); 897 | 898 | } 899 | } 900 | 901 | if (err) { 902 | dev_err(mt->dev, "%s: request firmware failed: %d\n", 903 | __func__, err); 904 | return err; 905 | } 906 | 907 | err = xone_mt76_load_firmware(mt, fw); 908 | release_firmware(fw); 909 | if (err) { 910 | dev_err(mt->dev, "%s: load firmware failed: %d\n", 911 | __func__, err); 912 | return err; 913 | } 914 | 915 | err = xone_mt76_init_radio(mt); 916 | if (err) 917 | dev_err(mt->dev, "%s: init radio failed: %d\n", __func__, err); 918 | 919 | return err; 920 | } 921 | 922 | static int xone_dongle_power_off_clients(struct xone_dongle *dongle) 923 | { 924 | struct xone_dongle_client *client; 925 | int i; 926 | int err = 0; 927 | unsigned long flags; 928 | 929 | spin_lock_irqsave(&dongle->clients_lock, flags); 930 | 931 | for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) { 932 | client = dongle->clients[i]; 933 | if (!client) 934 | continue; 935 | 936 | err = gip_power_off_adapter(client->adapter); 937 | if (err) 938 | break; 939 | } 940 | 941 | spin_unlock_irqrestore(&dongle->clients_lock, flags); 942 | 943 | if (err) 944 | return err; 945 | 946 | /* can time out if new client connects */ 947 | if (!wait_event_timeout(dongle->disconnect_wait, 948 | !atomic_read(&dongle->client_count), 949 | XONE_DONGLE_PWR_OFF_TIMEOUT)) 950 | return -ETIMEDOUT; 951 | 952 | return xone_dongle_toggle_pairing(dongle, false); 953 | } 954 | 955 | static void xone_dongle_destroy(struct xone_dongle *dongle) 956 | { 957 | struct xone_dongle_client *client; 958 | struct urb *urb; 959 | int i; 960 | 961 | usb_kill_anchored_urbs(&dongle->urbs_in_busy); 962 | destroy_workqueue(dongle->event_wq); 963 | cancel_delayed_work_sync(&dongle->pairing_work); 964 | 965 | for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) { 966 | client = dongle->clients[i]; 967 | if (!client) 968 | continue; 969 | 970 | gip_destroy_adapter(client->adapter); 971 | kfree(client); 972 | dongle->clients[i] = NULL; 973 | } 974 | 975 | usb_kill_anchored_urbs(&dongle->urbs_out_busy); 976 | 977 | while ((urb = usb_get_from_anchor(&dongle->urbs_out_idle))) 978 | usb_free_urb(urb); 979 | 980 | while ((urb = usb_get_from_anchor(&dongle->urbs_in_idle))) { 981 | usb_free_coherent(urb->dev, urb->transfer_buffer_length, 982 | urb->transfer_buffer, urb->transfer_dma); 983 | usb_free_urb(urb); 984 | } 985 | 986 | mutex_destroy(&dongle->pairing_lock); 987 | } 988 | 989 | static int xone_dongle_probe(struct usb_interface *intf, 990 | const struct usb_device_id *id) 991 | { 992 | struct xone_dongle *dongle; 993 | int err; 994 | 995 | dongle = devm_kzalloc(&intf->dev, sizeof(*dongle), GFP_KERNEL); 996 | if (!dongle) 997 | return -ENOMEM; 998 | 999 | dongle->mt.dev = &intf->dev; 1000 | dongle->mt.udev = interface_to_usbdev(intf); 1001 | 1002 | usb_reset_device(dongle->mt.udev); 1003 | 1004 | dongle->event_wq = alloc_ordered_workqueue("xone_dongle", 0); 1005 | if (!dongle->event_wq) 1006 | return -ENOMEM; 1007 | 1008 | mutex_init(&dongle->pairing_lock); 1009 | INIT_DELAYED_WORK(&dongle->pairing_work, xone_dongle_pairing_timeout); 1010 | spin_lock_init(&dongle->clients_lock); 1011 | init_waitqueue_head(&dongle->disconnect_wait); 1012 | 1013 | err = xone_dongle_init(dongle, id); 1014 | if (err) { 1015 | xone_dongle_destroy(dongle); 1016 | return err; 1017 | } 1018 | 1019 | usb_set_intfdata(intf, dongle); 1020 | 1021 | err = device_add_groups(&intf->dev, xone_dongle_groups); 1022 | if (err) { 1023 | xone_dongle_destroy(dongle); 1024 | return err; 1025 | } 1026 | 1027 | /* enable USB remote wakeup and autosuspend */ 1028 | intf->needs_remote_wakeup = true; 1029 | device_wakeup_enable(&dongle->mt.udev->dev); 1030 | pm_runtime_set_autosuspend_delay(&dongle->mt.udev->dev, 1031 | XONE_DONGLE_SUSPEND_DELAY); 1032 | usb_enable_autosuspend(dongle->mt.udev); 1033 | 1034 | return 0; 1035 | } 1036 | 1037 | static void xone_dongle_disconnect(struct usb_interface *intf) 1038 | { 1039 | struct xone_dongle *dongle = usb_get_intfdata(intf); 1040 | int err; 1041 | 1042 | device_remove_groups(&intf->dev, xone_dongle_groups); 1043 | 1044 | /* can fail during USB device removal */ 1045 | err = xone_dongle_power_off_clients(dongle); 1046 | if (err) 1047 | dev_dbg(dongle->mt.dev, "%s: power off failed: %d\n", 1048 | __func__, err); 1049 | 1050 | xone_dongle_destroy(dongle); 1051 | usb_set_intfdata(intf, NULL); 1052 | } 1053 | 1054 | static int xone_dongle_suspend(struct usb_interface *intf, pm_message_t message) 1055 | { 1056 | struct xone_dongle *dongle = usb_get_intfdata(intf); 1057 | int err; 1058 | 1059 | err = xone_dongle_power_off_clients(dongle); 1060 | if (err) 1061 | dev_err(dongle->mt.dev, "%s: power off failed: %d\n", 1062 | __func__, err); 1063 | 1064 | usb_kill_anchored_urbs(&dongle->urbs_in_busy); 1065 | usb_kill_anchored_urbs(&dongle->urbs_out_busy); 1066 | cancel_delayed_work_sync(&dongle->pairing_work); 1067 | 1068 | return xone_mt76_suspend_radio(&dongle->mt); 1069 | } 1070 | 1071 | static int xone_dongle_resume(struct usb_interface *intf) 1072 | { 1073 | struct xone_dongle *dongle = usb_get_intfdata(intf); 1074 | struct urb *urb; 1075 | int err; 1076 | 1077 | while ((urb = usb_get_from_anchor(&dongle->urbs_in_idle))) { 1078 | usb_anchor_urb(urb, &dongle->urbs_in_busy); 1079 | usb_free_urb(urb); 1080 | 1081 | err = usb_submit_urb(urb, GFP_KERNEL); 1082 | if (err) 1083 | return err; 1084 | } 1085 | 1086 | return xone_mt76_resume_radio(&dongle->mt); 1087 | } 1088 | 1089 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0) 1090 | static void xone_dongle_shutdown(struct device *dev) 1091 | { 1092 | struct usb_interface *intf = to_usb_interface(dev); 1093 | #else 1094 | static void xone_dongle_shutdown(struct usb_interface *intf) 1095 | { 1096 | #endif 1097 | struct xone_dongle *dongle = usb_get_intfdata(intf); 1098 | int err; 1099 | 1100 | if (system_state == SYSTEM_RESTART) 1101 | return; 1102 | 1103 | err = xone_dongle_power_off_clients(dongle); 1104 | if (err) 1105 | dev_err(dongle->mt.dev, "%s: power off failed: %d\n", 1106 | __func__, err); 1107 | } 1108 | 1109 | static const struct usb_device_id xone_dongle_id_table[] = { 1110 | { USB_DEVICE(0x045e, 0x02e6) }, /* old dongle */ 1111 | { USB_DEVICE(0x045e, 0x02fe) }, /* new dongle */ 1112 | { USB_DEVICE(0x045e, 0x02f9) }, /* built-in dongle (ASUS, Lenovo) */ 1113 | { USB_DEVICE(0x045e, 0x091e) }, /* built-in dongle (Surface Book 2) */ 1114 | { }, 1115 | }; 1116 | 1117 | static struct usb_driver xone_dongle_driver = { 1118 | .name = "xone-dongle", 1119 | .probe = xone_dongle_probe, 1120 | .disconnect = xone_dongle_disconnect, 1121 | .suspend = xone_dongle_suspend, 1122 | .resume = xone_dongle_resume, 1123 | .id_table = xone_dongle_id_table, 1124 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) 1125 | .drvwrap.driver.shutdown = xone_dongle_shutdown, 1126 | #elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0) 1127 | .driver.shutdown = xone_dongle_shutdown, 1128 | #else 1129 | .shutdown = xone_dongle_shutdown, 1130 | #endif 1131 | .supports_autosuspend = true, 1132 | .disable_hub_initiated_lpm = true, 1133 | .soft_unbind = true, 1134 | }; 1135 | 1136 | module_usb_driver(xone_dongle_driver); 1137 | 1138 | MODULE_DEVICE_TABLE(usb, xone_dongle_id_table); 1139 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 1140 | MODULE_DESCRIPTION("xone dongle driver"); 1141 | MODULE_VERSION("#VERSION#"); 1142 | MODULE_LICENSE("GPL"); 1143 | -------------------------------------------------------------------------------- /transport/mt76.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "mt76_defs.h" 9 | 10 | #define XONE_MT_EP_IN_CMD 0x05 11 | #define XONE_MT_EP_IN_WLAN 0x04 12 | #define XONE_MT_EP_OUT 0x04 13 | 14 | #define XONE_MT_NUM_CHANNELS 12 15 | 16 | /* 802.11 frame subtype: reserved */ 17 | #define XONE_MT_WLAN_RESERVED 0x70 18 | 19 | enum xone_mt76_led_mode { 20 | XONE_MT_LED_BLINK = 0x00, 21 | XONE_MT_LED_ON = 0x01, 22 | XONE_MT_LED_OFF = 0x02, 23 | }; 24 | 25 | enum xone_mt76_event { 26 | XONE_MT_EVT_BUTTON = 0x04, 27 | XONE_MT_EVT_CHANNELS = 0x0a, 28 | XONE_MT_EVT_PACKET_RX = 0x0c, 29 | XONE_MT_EVT_COREDUMP = 0x0d, 30 | XONE_MT_EVT_CLIENT_LOST = 0x0e, 31 | }; 32 | 33 | enum xone_mt76_client_command { 34 | XONE_MT_CLIENT_PAIR_REQ = 0x01, 35 | XONE_MT_CLIENT_PAIR_RESP = 0x02, 36 | XONE_MT_CLIENT_CHANGE_CHAN_REQ = 0x03, 37 | XONE_MT_CLIENT_CHANGE_CHAN_RESP = 0x04, 38 | XONE_MT_CLIENT_STATISTICS_REQ = 0x05, 39 | XONE_MT_CLIENT_STATISTICS_RESP = 0x06, 40 | XONE_MT_CLIENT_SCAN_CHAN_REQ = 0x07, 41 | XONE_MT_CLIENT_SCAN_CHAN_RESP = 0x08, 42 | XONE_MT_CLIENT_ENABLE_ENCRYPTION = 0x10, 43 | }; 44 | 45 | struct xone_mt76_channel { 46 | u8 index; 47 | u8 band; 48 | enum mt76_phy_bandwidth bandwidth; 49 | enum mt76_cal_channel_group group; 50 | bool scan; 51 | u8 power; 52 | }; 53 | 54 | struct xone_mt76 { 55 | struct device *dev; 56 | struct usb_device *udev; 57 | 58 | __le32 control_data; 59 | u8 address[ETH_ALEN]; 60 | 61 | struct xone_mt76_channel channels[XONE_MT_NUM_CHANNELS]; 62 | struct xone_mt76_channel *channel; 63 | }; 64 | 65 | struct sk_buff *xone_mt76_alloc_message(int len, gfp_t gfp); 66 | void xone_mt76_prep_command(struct sk_buff *skb, enum mt76_mcu_cmd cmd); 67 | 68 | int xone_mt76_set_led_mode(struct xone_mt76 *mt, enum xone_mt76_led_mode mode); 69 | int xone_mt76_load_firmware(struct xone_mt76 *mt, const struct firmware *fw); 70 | int xone_mt76_init_radio(struct xone_mt76 *mt); 71 | int xone_mt76_suspend_radio(struct xone_mt76 *mt); 72 | int xone_mt76_resume_radio(struct xone_mt76 *mt); 73 | int xone_mt76_set_pairing(struct xone_mt76 *mt, bool enable); 74 | 75 | int xone_mt76_pair_client(struct xone_mt76 *mt, u8 *addr); 76 | int xone_mt76_associate_client(struct xone_mt76 *mt, u8 wcid, u8 *addr); 77 | int xone_mt76_send_client_command(struct xone_mt76 *mt, u8 wcid, u8 *addr, 78 | enum xone_mt76_client_command cmd, 79 | u8 *data, int len); 80 | int xone_mt76_set_client_key(struct xone_mt76 *mt, u8 wcid, u8 *key, int len); 81 | int xone_mt76_remove_client(struct xone_mt76 *mt, u8 wcid); 82 | -------------------------------------------------------------------------------- /transport/wired.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Severin von Wnuck-Lipinski 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../bus/bus.h" 11 | 12 | #define XONE_WIRED_INTF_DATA 0 13 | #define XONE_WIRED_INTF_AUDIO 1 14 | 15 | #define XONE_WIRED_NUM_DATA_URBS 8 16 | #define XONE_WIRED_NUM_AUDIO_URBS 12 17 | #define XONE_WIRED_NUM_AUDIO_PKTS 8 18 | 19 | #define XONE_WIRED_LEN_DATA_PKT 64 20 | 21 | #define XONE_WIRED_VENDOR(vendor) \ 22 | .match_flags = USB_DEVICE_ID_MATCH_VENDOR | \ 23 | USB_DEVICE_ID_MATCH_INT_INFO | \ 24 | USB_DEVICE_ID_MATCH_INT_NUMBER, \ 25 | .idVendor = vendor, \ 26 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ 27 | .bInterfaceSubClass = 0x47, \ 28 | .bInterfaceProtocol = 0xd0, \ 29 | .bInterfaceNumber = XONE_WIRED_INTF_DATA, 30 | 31 | struct xone_wired { 32 | struct usb_device *udev; 33 | 34 | struct xone_wired_port { 35 | struct device *dev; 36 | 37 | struct usb_endpoint_descriptor *ep_in; 38 | struct usb_endpoint_descriptor *ep_out; 39 | 40 | struct urb *urb_in; 41 | struct usb_anchor urbs_out_idle; 42 | struct usb_anchor urbs_out_busy; 43 | 44 | int buffer_length_out; 45 | } data_port, audio_port; 46 | 47 | struct gip_adapter *adapter; 48 | }; 49 | 50 | static void xone_wired_complete_data_in(struct urb *urb) 51 | { 52 | struct xone_wired *wired = urb->context; 53 | struct device *dev = wired->data_port.dev; 54 | int err; 55 | 56 | switch (urb->status) { 57 | case 0: 58 | break; 59 | case -ENOENT: 60 | case -ECONNRESET: 61 | case -ESHUTDOWN: 62 | return; 63 | default: 64 | goto resubmit; 65 | } 66 | 67 | if (!urb->actual_length) 68 | goto resubmit; 69 | 70 | err = gip_process_buffer(wired->adapter, urb->transfer_buffer, 71 | urb->actual_length); 72 | if (err) { 73 | dev_err(dev, "%s: process failed: %d\n", __func__, err); 74 | print_hex_dump_debug("xone-wired packet: ", 75 | DUMP_PREFIX_NONE, 16, 1, 76 | urb->transfer_buffer, urb->actual_length, 77 | false); 78 | } 79 | 80 | resubmit: 81 | /* can fail during USB device removal */ 82 | err = usb_submit_urb(urb, GFP_ATOMIC); 83 | if (err) 84 | dev_dbg(dev, "%s: submit failed: %d\n", __func__, err); 85 | } 86 | 87 | static void xone_wired_complete_audio_in(struct urb *urb) 88 | { 89 | struct xone_wired *wired = urb->context; 90 | struct device *dev = wired->audio_port.dev; 91 | struct usb_iso_packet_descriptor *desc; 92 | int i, err; 93 | 94 | if (urb->status) 95 | return; 96 | 97 | for (i = 0; i < urb->number_of_packets; i++) { 98 | desc = &urb->iso_frame_desc[i]; 99 | 100 | /* device reset after system sleep can cause xHCI errors */ 101 | if (desc->status == -EPROTO) { 102 | dev_warn_once(dev, "%s: protocol error\n", __func__); 103 | break; 104 | } 105 | 106 | if (!desc->actual_length) 107 | continue; 108 | 109 | err = gip_process_buffer(wired->adapter, 110 | urb->transfer_buffer + desc->offset, 111 | desc->actual_length); 112 | if (err) 113 | dev_err(dev, "%s: process failed: %d\n", __func__, err); 114 | } 115 | 116 | /* can fail during USB device removal */ 117 | err = usb_submit_urb(urb, GFP_ATOMIC); 118 | if (err) 119 | dev_dbg(dev, "%s: submit failed: %d\n", __func__, err); 120 | } 121 | 122 | static void xone_wired_complete_out(struct urb *urb) 123 | { 124 | struct xone_wired_port *port = urb->context; 125 | 126 | usb_anchor_urb(urb, &port->urbs_out_idle); 127 | } 128 | 129 | static int xone_wired_init_data_in(struct xone_wired *wired) 130 | { 131 | struct xone_wired_port *port = &wired->data_port; 132 | struct urb *urb; 133 | void *buf; 134 | 135 | urb = usb_alloc_urb(0, GFP_KERNEL); 136 | if (!urb) 137 | return -ENOMEM; 138 | 139 | port->urb_in = urb; 140 | 141 | buf = usb_alloc_coherent(wired->udev, XONE_WIRED_LEN_DATA_PKT, 142 | GFP_KERNEL, &urb->transfer_dma); 143 | if (!buf) 144 | return -ENOMEM; 145 | 146 | usb_fill_int_urb(urb, wired->udev, 147 | usb_rcvintpipe(wired->udev, 148 | port->ep_in->bEndpointAddress), 149 | buf, XONE_WIRED_LEN_DATA_PKT, 150 | xone_wired_complete_data_in, wired, 151 | port->ep_in->bInterval); 152 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 153 | 154 | return usb_submit_urb(urb, GFP_KERNEL); 155 | } 156 | 157 | static int xone_wired_init_data_out(struct xone_wired *wired) 158 | { 159 | struct xone_wired_port *port = &wired->data_port; 160 | struct urb *urb; 161 | void *buf; 162 | int i; 163 | 164 | port->buffer_length_out = XONE_WIRED_LEN_DATA_PKT; 165 | 166 | for (i = 0; i < XONE_WIRED_NUM_DATA_URBS; i++) { 167 | urb = usb_alloc_urb(0, GFP_KERNEL); 168 | if (!urb) 169 | return -ENOMEM; 170 | 171 | usb_anchor_urb(urb, &port->urbs_out_idle); 172 | usb_free_urb(urb); 173 | 174 | buf = usb_alloc_coherent(wired->udev, XONE_WIRED_LEN_DATA_PKT, 175 | GFP_KERNEL, &urb->transfer_dma); 176 | if (!buf) 177 | return -ENOMEM; 178 | 179 | usb_fill_int_urb(urb, wired->udev, 180 | usb_sndintpipe(wired->udev, 181 | port->ep_out->bEndpointAddress), 182 | buf, XONE_WIRED_LEN_DATA_PKT, 183 | xone_wired_complete_out, port, 184 | port->ep_out->bInterval); 185 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 186 | } 187 | 188 | return 0; 189 | } 190 | 191 | static void xone_wired_free_urbs(struct xone_wired_port *port) 192 | { 193 | struct urb *urb = port->urb_in; 194 | 195 | if (urb) { 196 | usb_free_coherent(urb->dev, urb->transfer_buffer_length, 197 | urb->transfer_buffer, urb->transfer_dma); 198 | usb_free_urb(urb); 199 | port->urb_in = NULL; 200 | } 201 | 202 | while ((urb = usb_get_from_anchor(&port->urbs_out_idle))) { 203 | usb_free_coherent(urb->dev, port->buffer_length_out, 204 | urb->transfer_buffer, urb->transfer_dma); 205 | usb_free_urb(urb); 206 | } 207 | } 208 | 209 | static int xone_wired_get_buffer(struct gip_adapter *adap, 210 | struct gip_adapter_buffer *buf) 211 | { 212 | struct xone_wired *wired = dev_get_drvdata(&adap->dev); 213 | struct xone_wired_port *port; 214 | struct urb *urb; 215 | 216 | if (buf->type == GIP_BUF_DATA) 217 | port = &wired->data_port; 218 | else if (buf->type == GIP_BUF_AUDIO) 219 | port = &wired->audio_port; 220 | else 221 | return -EINVAL; 222 | 223 | urb = usb_get_from_anchor(&port->urbs_out_idle); 224 | if (!urb) 225 | return -ENOSPC; 226 | 227 | buf->context = urb; 228 | buf->data = urb->transfer_buffer; 229 | buf->length = port->buffer_length_out; 230 | 231 | return 0; 232 | } 233 | 234 | static int xone_wired_submit_buffer(struct gip_adapter *adap, 235 | struct gip_adapter_buffer *buf) 236 | { 237 | struct xone_wired *wired = dev_get_drvdata(&adap->dev); 238 | struct xone_wired_port *port; 239 | struct urb *urb = buf->context; 240 | int err; 241 | 242 | if (buf->type == GIP_BUF_DATA) 243 | port = &wired->data_port; 244 | else if (buf->type == GIP_BUF_AUDIO) 245 | port = &wired->audio_port; 246 | else 247 | return -EINVAL; 248 | 249 | urb->transfer_buffer_length = buf->length; 250 | usb_anchor_urb(urb, &port->urbs_out_busy); 251 | 252 | err = usb_submit_urb(urb, GFP_ATOMIC); 253 | if (err) { 254 | usb_unanchor_urb(urb); 255 | usb_anchor_urb(urb, &port->urbs_out_idle); 256 | } 257 | 258 | usb_free_urb(urb); 259 | 260 | return err; 261 | } 262 | 263 | static int xone_wired_enable_audio(struct gip_adapter *adap) 264 | { 265 | struct xone_wired *wired = dev_get_drvdata(&adap->dev); 266 | struct usb_interface *intf; 267 | 268 | if (!wired->audio_port.dev) 269 | return -ENOTSUPP; 270 | 271 | intf = to_usb_interface(wired->audio_port.dev); 272 | if (intf->cur_altsetting->desc.bAlternateSetting == 1) 273 | return -EALREADY; 274 | 275 | return usb_set_interface(wired->udev, XONE_WIRED_INTF_AUDIO, 1); 276 | } 277 | 278 | static int xone_wired_init_audio_in(struct gip_adapter *adap) 279 | { 280 | struct xone_wired *wired = dev_get_drvdata(&adap->dev); 281 | struct xone_wired_port *port = &wired->audio_port; 282 | struct urb *urb; 283 | void *buf; 284 | int len, i; 285 | 286 | if (!port->ep_in) 287 | return -ENOTSUPP; 288 | 289 | urb = usb_alloc_urb(XONE_WIRED_NUM_AUDIO_PKTS, GFP_KERNEL); 290 | if (!urb) 291 | return -ENOMEM; 292 | 293 | port->urb_in = urb; 294 | 295 | len = usb_endpoint_maxp(port->ep_in); 296 | buf = usb_alloc_coherent(wired->udev, len * XONE_WIRED_NUM_AUDIO_PKTS, 297 | GFP_KERNEL, &urb->transfer_dma); 298 | if (!buf) 299 | return -ENOMEM; 300 | 301 | urb->dev = wired->udev; 302 | urb->pipe = usb_rcvisocpipe(wired->udev, port->ep_in->bEndpointAddress); 303 | urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; 304 | urb->transfer_buffer = buf; 305 | urb->transfer_buffer_length = len * XONE_WIRED_NUM_AUDIO_PKTS; 306 | urb->number_of_packets = XONE_WIRED_NUM_AUDIO_PKTS; 307 | urb->interval = port->ep_in->bInterval; 308 | urb->context = wired; 309 | urb->complete = xone_wired_complete_audio_in; 310 | 311 | for (i = 0; i < XONE_WIRED_NUM_AUDIO_PKTS; i++) { 312 | urb->iso_frame_desc[i].offset = i * len; 313 | urb->iso_frame_desc[i].length = len; 314 | } 315 | 316 | return usb_submit_urb(urb, GFP_KERNEL); 317 | } 318 | 319 | static int xone_wired_init_audio_out(struct gip_adapter *adap, int pkt_len) 320 | { 321 | struct xone_wired *wired = dev_get_drvdata(&adap->dev); 322 | struct xone_wired_port *port = &wired->audio_port; 323 | struct urb *urb; 324 | void *buf; 325 | int i, j; 326 | 327 | if (!port->ep_out) 328 | return -ENOTSUPP; 329 | 330 | port->buffer_length_out = pkt_len * XONE_WIRED_NUM_AUDIO_PKTS; 331 | 332 | for (i = 0; i < XONE_WIRED_NUM_AUDIO_URBS; i++) { 333 | urb = usb_alloc_urb(XONE_WIRED_NUM_AUDIO_PKTS, GFP_KERNEL); 334 | if (!urb) 335 | return -ENOMEM; 336 | 337 | usb_anchor_urb(urb, &port->urbs_out_idle); 338 | usb_free_urb(urb); 339 | 340 | buf = usb_alloc_coherent(wired->udev, port->buffer_length_out, 341 | GFP_KERNEL, &urb->transfer_dma); 342 | if (!buf) 343 | return -ENOMEM; 344 | 345 | urb->dev = wired->udev; 346 | urb->pipe = usb_sndisocpipe(wired->udev, 347 | port->ep_out->bEndpointAddress); 348 | urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; 349 | urb->transfer_buffer = buf; 350 | urb->transfer_buffer_length = port->buffer_length_out; 351 | urb->number_of_packets = XONE_WIRED_NUM_AUDIO_PKTS; 352 | urb->interval = port->ep_out->bInterval; 353 | urb->context = port; 354 | urb->complete = xone_wired_complete_out; 355 | 356 | for (j = 0; j < XONE_WIRED_NUM_AUDIO_PKTS; j++) { 357 | urb->iso_frame_desc[j].offset = j * pkt_len; 358 | urb->iso_frame_desc[j].length = pkt_len; 359 | } 360 | } 361 | 362 | return 0; 363 | } 364 | 365 | static int xone_wired_disable_audio(struct gip_adapter *adap) 366 | { 367 | struct xone_wired *wired = dev_get_drvdata(&adap->dev); 368 | struct xone_wired_port *port = &wired->audio_port; 369 | struct usb_interface *intf; 370 | 371 | if (!port->dev) 372 | return -ENOTSUPP; 373 | 374 | intf = to_usb_interface(port->dev); 375 | if (!intf->cur_altsetting->desc.bAlternateSetting) 376 | return -EALREADY; 377 | 378 | usb_kill_urb(port->urb_in); 379 | usb_kill_anchored_urbs(&port->urbs_out_busy); 380 | xone_wired_free_urbs(port); 381 | 382 | return usb_set_interface(wired->udev, XONE_WIRED_INTF_AUDIO, 0); 383 | } 384 | 385 | static struct gip_adapter_ops xone_wired_adapter_ops = { 386 | .get_buffer = xone_wired_get_buffer, 387 | .submit_buffer = xone_wired_submit_buffer, 388 | .enable_audio = xone_wired_enable_audio, 389 | .init_audio_in = xone_wired_init_audio_in, 390 | .init_audio_out = xone_wired_init_audio_out, 391 | .disable_audio = xone_wired_disable_audio, 392 | }; 393 | 394 | static struct usb_driver xone_wired_driver; 395 | 396 | static int xone_wired_find_isoc_endpoints(struct usb_host_interface *alt, 397 | struct usb_endpoint_descriptor **in, 398 | struct usb_endpoint_descriptor **out) 399 | { 400 | struct usb_endpoint_descriptor *ep; 401 | int i; 402 | 403 | for (i = 0; i < alt->desc.bNumEndpoints; i++) { 404 | ep = &alt->endpoint[i].desc; 405 | if (usb_endpoint_is_isoc_in(ep)) 406 | *in = ep; 407 | else if (usb_endpoint_is_isoc_out(ep)) 408 | *out = ep; 409 | 410 | if (*in && *out) 411 | return 0; 412 | } 413 | 414 | return -ENXIO; 415 | } 416 | 417 | static int xone_wired_init_data_port(struct xone_wired *wired, 418 | struct usb_interface *intf) 419 | { 420 | struct xone_wired_port *port = &wired->data_port; 421 | int err; 422 | 423 | init_usb_anchor(&port->urbs_out_idle); 424 | init_usb_anchor(&port->urbs_out_busy); 425 | 426 | err = usb_find_common_endpoints(intf->cur_altsetting, NULL, NULL, 427 | &port->ep_in, &port->ep_out); 428 | if (err) 429 | return err; 430 | 431 | port->dev = &intf->dev; 432 | 433 | return 0; 434 | } 435 | 436 | static int xone_wired_init_audio_port(struct xone_wired *wired) 437 | { 438 | struct xone_wired_port *port = &wired->audio_port; 439 | struct usb_interface *intf; 440 | struct usb_host_interface *alt; 441 | int err; 442 | 443 | init_usb_anchor(&port->urbs_out_idle); 444 | init_usb_anchor(&port->urbs_out_busy); 445 | 446 | intf = usb_ifnum_to_if(wired->udev, XONE_WIRED_INTF_AUDIO); 447 | if (!intf) { 448 | dev_dbg(&wired->udev->dev, "%s: audio unavailable\n", __func__); 449 | return 0; 450 | } 451 | 452 | alt = usb_altnum_to_altsetting(intf, 1); 453 | if (!alt) 454 | return -ENXIO; 455 | 456 | err = usb_driver_claim_interface(&xone_wired_driver, intf, NULL); 457 | if (err) 458 | return err; 459 | 460 | /* disable the audio interface */ 461 | /* mandatory for certain third party devices */ 462 | err = usb_set_interface(wired->udev, XONE_WIRED_INTF_AUDIO, 0); 463 | if (err) 464 | return err; 465 | 466 | err = xone_wired_find_isoc_endpoints(alt, &port->ep_in, &port->ep_out); 467 | if (err) 468 | return err; 469 | 470 | port->dev = &intf->dev; 471 | 472 | return 0; 473 | } 474 | 475 | static int xone_wired_probe(struct usb_interface *intf, 476 | const struct usb_device_id *id) 477 | { 478 | struct xone_wired *wired; 479 | int err; 480 | 481 | wired = devm_kzalloc(&intf->dev, sizeof(*wired), GFP_KERNEL); 482 | if (!wired) 483 | return -ENOMEM; 484 | 485 | wired->udev = interface_to_usbdev(intf); 486 | 487 | /* newer devices require a reset after system sleep */ 488 | usb_reset_device(wired->udev); 489 | 490 | err = xone_wired_init_data_port(wired, intf); 491 | if (err) 492 | return err; 493 | 494 | err = xone_wired_init_audio_port(wired); 495 | if (err) 496 | return err; 497 | 498 | wired->adapter = gip_create_adapter(&intf->dev, &xone_wired_adapter_ops, 499 | XONE_WIRED_NUM_AUDIO_PKTS); 500 | if (IS_ERR(wired->adapter)) 501 | return PTR_ERR(wired->adapter); 502 | 503 | dev_set_drvdata(&wired->adapter->dev, wired); 504 | 505 | err = xone_wired_init_data_out(wired); 506 | if (err) 507 | goto err_free_urbs; 508 | 509 | err = xone_wired_init_data_in(wired); 510 | if (err) 511 | goto err_free_urbs; 512 | 513 | usb_set_intfdata(intf, wired); 514 | 515 | /* enable USB remote wakeup */ 516 | device_wakeup_enable(&wired->udev->dev); 517 | 518 | return 0; 519 | 520 | err_free_urbs: 521 | xone_wired_free_urbs(&wired->data_port); 522 | gip_destroy_adapter(wired->adapter); 523 | 524 | return err; 525 | } 526 | 527 | static void xone_wired_disconnect(struct usb_interface *intf) 528 | { 529 | struct xone_wired *wired = usb_get_intfdata(intf); 530 | 531 | /* ignore audio interface unbind */ 532 | if (!wired) 533 | return; 534 | 535 | usb_kill_urb(wired->data_port.urb_in); 536 | usb_kill_urb(wired->audio_port.urb_in); 537 | 538 | /* also disables the audio interface */ 539 | gip_destroy_adapter(wired->adapter); 540 | 541 | usb_kill_anchored_urbs(&wired->data_port.urbs_out_busy); 542 | xone_wired_free_urbs(&wired->data_port); 543 | 544 | usb_set_intfdata(intf, NULL); 545 | } 546 | 547 | static const struct usb_device_id xone_wired_id_table[] = { 548 | { XONE_WIRED_VENDOR(0x045e) }, /* Microsoft */ 549 | { XONE_WIRED_VENDOR(0x0738) }, /* Mad Catz */ 550 | { XONE_WIRED_VENDOR(0x0e6f) }, /* PDP */ 551 | { XONE_WIRED_VENDOR(0x0f0d) }, /* Hori */ 552 | { XONE_WIRED_VENDOR(0x1532) }, /* Razer */ 553 | { XONE_WIRED_VENDOR(0x24c6) }, /* PowerA */ 554 | { XONE_WIRED_VENDOR(0x20d6) }, /* BDA */ 555 | { XONE_WIRED_VENDOR(0x044f) }, /* Thrustmaster */ 556 | { XONE_WIRED_VENDOR(0x10f5) }, /* Turtle Beach */ 557 | { XONE_WIRED_VENDOR(0x2e24) }, /* Hyperkin */ 558 | { XONE_WIRED_VENDOR(0x3285) }, /* Nacon */ 559 | { XONE_WIRED_VENDOR(0x2dc8) }, /* 8BitDo */ 560 | { XONE_WIRED_VENDOR(0x2e95) }, /* SCUF */ 561 | { XONE_WIRED_VENDOR(0x3537) }, /* GameSir */ 562 | { XONE_WIRED_VENDOR(0x11c1) }, /* ??? */ 563 | { XONE_WIRED_VENDOR(0x294b) }, /* Snakebyte */ 564 | { XONE_WIRED_VENDOR(0x2c16) }, /* Priferential */ 565 | { XONE_WIRED_VENDOR(0x0b05) }, /* ASUS */ 566 | { XONE_WIRED_VENDOR(0x413d) }, /* BIGBIG WON */ 567 | { XONE_WIRED_VENDOR(0x046d) }, /* Logitech Astro */ 568 | { XONE_WIRED_VENDOR(0x0079) }, /* EasySMX */ 569 | { XONE_WIRED_VENDOR(0x1038) }, /* SteelSeries ApS */ 570 | { }, 571 | }; 572 | 573 | static struct usb_driver xone_wired_driver = { 574 | .name = "xone-wired", 575 | .probe = xone_wired_probe, 576 | .disconnect = xone_wired_disconnect, 577 | .id_table = xone_wired_id_table, 578 | }; 579 | 580 | module_usb_driver(xone_wired_driver); 581 | 582 | MODULE_DEVICE_TABLE(usb, xone_wired_id_table); 583 | MODULE_AUTHOR("Severin von Wnuck-Lipinski "); 584 | MODULE_DESCRIPTION("xone wired driver"); 585 | MODULE_VERSION("#VERSION#"); 586 | MODULE_LICENSE("GPL"); 587 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | if [ "$(id -u)" -ne 0 ]; then 6 | echo 'This script must be run as root!' >&2 7 | exit 1 8 | fi 9 | 10 | modules=$(lsmod | grep '^xone_' | cut -d ' ' -f 1 | tr '\n' ' ') 11 | version=$(dkms status xone | head -n 1 | tr -s ',:/' ' ' | cut -d ' ' -f 2) 12 | 13 | if [ -n "$modules" ]; then 14 | echo "Unloading modules: $modules..." 15 | # shellcheck disable=SC2086 16 | modprobe -r -a $modules 17 | fi 18 | 19 | if [ -n "$version" ]; then 20 | echo "Uninstalling xone $version..." 21 | dkms remove -m xone -v "$version" --all 22 | rm -r "/usr/src/xone-$version" 23 | rm -f /etc/modprobe.d/xone-blacklist.conf 24 | rm -f /usr/local/bin/xone-get-firmware.sh 25 | else 26 | echo 'Driver is not installed!' >&2 27 | fi 28 | --------------------------------------------------------------------------------