├── flashrom.bin ├── unload_procon.sh ├── load_procon.sh ├── README.md ├── LICENSE ├── proconcon_old.c └── proconcon.c /flashrom.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unvirus/proconcon/HEAD/flashrom.bin -------------------------------------------------------------------------------- /unload_procon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $USER != "root" ]; then 3 | echo "Root privilege is required." 4 | exit 0 5 | fi 6 | 7 | cd /sys/kernel/config/usb_gadget 8 | echo "" > UDC 9 | rm procon/configs/c.1/hid.usb0 10 | rmdir procon/configs/c.1/strings/0x409 11 | rmdir procon/configs/c.1 12 | rmdir procon/functions/hid.usb0 13 | rmdir procon/strings/0x409 14 | rmdir procon 15 | cd $HOME 16 | 17 | -------------------------------------------------------------------------------- /load_procon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $USER != "root" ]; then 3 | echo "Root privilege is required." 4 | exit 0 5 | fi 6 | 7 | cd /sys/kernel/config/usb_gadget/ 8 | mkdir -p procon 9 | cd procon 10 | echo 0x057e > idVendor 11 | echo 0x2009 > idProduct 12 | echo 0x0200 > bcdDevice 13 | echo 0x0200 > bcdUSB 14 | echo 0x00 > bDeviceClass 15 | echo 0x00 > bDeviceSubClass 16 | echo 0x00 > bDeviceProtocol 17 | 18 | mkdir -p strings/0x409 19 | echo "000000000001" > strings/0x409/serialnumber 20 | echo "Nintendo Co., Ltd." > strings/0x409/manufacturer 21 | echo "Pro Controller" > strings/0x409/product 22 | 23 | mkdir -p configs/c.1/strings/0x409 24 | echo "Nintendo Switch Pro Controller" > configs/c.1/strings/0x409/configuration 25 | echo 500 > configs/c.1/MaxPower 26 | echo 0xa0 > configs/c.1/bmAttributes 27 | 28 | mkdir -p functions/hid.usb0 29 | echo 0 > functions/hid.usb0/protocol 30 | echo 0 > functions/hid.usb0/subclass 31 | echo 64 > functions/hid.usb0/report_length 32 | echo 050115000904A1018530050105091901290A150025017501950A5500650081020509190B290E150025017501950481027501950281030B01000100A1000B300001000B310001000B320001000B35000100150027FFFF0000751095048102C00B39000100150025073500463B0165147504950181020509190F2912150025017501950481027508953481030600FF852109017508953F8103858109027508953F8103850109037508953F9183851009047508953F9183858009057508953F9183858209067508953F9183C0 | xxd -r -ps > functions/hid.usb0/report_desc 33 | 34 | ln -s functions/hid.usb0 configs/c.1/ 35 | 36 | ls /sys/class/udc > UDC 37 | cd $HOME 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## proconcon 2 | スプラトゥーン3 マウスコンバーター 3 | 4 | ## 概要 5 | スプラトゥーン3 マウスコンバーター Raspberry Pi 4B用です。 6 | 質問等はGitHubのDiscussionへお願いします。 7 | 各自カスタマイズしてこっそり使用してください。 8 | ソースコードを編集すればキー配置など好きに設定できるので 9 | ハンディキャップがあり、ゲームコントローラーをうまく扱えない方も 10 | ご利用いただけるかもしれません。 11 | 12 | ## 更新履歴 13 | Ver 0.09 2022/11/25 14 | 旧バージョン、動作にはプロコンが必要です。 15 | 16 | Ver 0.21 2024/05/31 17 | 以下の機能を追加しました。 18 | 7キー 低速連射 19 | 8キー 高速連射 20 | 21 | ## 必要な物 22 | ラズベリーパイ 4B 23 | Nintendo SWITCH、スプラトゥーン3 24 | マウス、キーボード、各機材の接続ケーブル 25 | 26 | ラズベリーパイのUSB Gadgetを利用するため、電源をUSB-C以外から得る必要があります。 27 | POE+HATとPOE対応LAN HUBで電源供給がよいでしょう。 28 | ラズベリーパイ 4Bの初期版はUSB回路に不具合があり、USBを利用しながら給電ができないものがあります。 29 | 30 | ## ラズベリーパイの準備 31 | USB gadgetが利用できるように設定する。 32 | 33 | ## ビルド方法 34 | gcc proconcon.c -o proconcon.out -l pthread -lm -O3 -Wall 35 | 36 | キーボード、マウスの選択はソースコードに記載があります。 37 | 各自のデバイス名に合わせてください。 38 | 39 | ## 接続方法 40 | ラズベリーパイにKeyboard、Mouseを接続する。 41 | ラズベリーパイをUSBケーブルでNintendo SWITCHに接続する。 42 | 43 | ![IMG_E1374](https://user-images.githubusercontent.com/83897755/204125349-ef4d7021-fbfd-4df3-9745-1a1058430d0c.jpg) 44 | 45 | ## 起動方法 46 | sudo ./load_procon.sh 47 | sudo ./proconcon.out 48 | 49 | **proconcon.outと同じ場所にflashrom.binを配置してください。** 50 | **flashrom.binはコントローラーの設定ファイルで、proconcon.out起動時に利用します。** 51 | 52 | マウス感度は800-1600 DPIをあたりで調整すると良さそうです。 53 | 本プログラムはCUI(CLI)で利用してください。 54 | デスクトップ環境では本プログラム使用中に範囲外のクリックなどが発生し、誤動作の原因になります。 55 | ![IMG_E1374](https://user-images.githubusercontent.com/83897755/204680187-3678ed45-c9b6-499e-8ff4-b0cc18fd81f5.jpg) 56 | 57 | ## センターリング 58 | 試合が始まった時、1キーを1秒ほど間隔を開けて2回押してください。 59 | この操作で、マウスのセンターリングが行われます。 60 | もし、マウスの動きがゲームに正しく反映されない場合は再度センターリングを行ってください。 61 | 62 | ## ボタン配置 63 | デフォルト状態では下記のキー配置になっています。 64 | 65 | | Key | ProCon | Comment | 66 | | ------------- | ------------- | ------------------------------------------------- | 67 | | ESC | Home | | 68 | | 1 | Y | Centering | 69 | | 2 | Capture | | 70 | | 3 | - | | 71 | | 4 | + | | 72 | | 7 | | Rapid Fire (Slow) | 73 | | 8 | | Rapid Fire (High) | 74 | | 9 | | Mouse Lの単射、連射入れ替え | 75 | | WASD | Stick L | | 76 | | SHIFT L | | Move slowly with SHIFT L + WASD | 77 | | SPACE | B | | 78 | | E | A | | 79 | | R | X | | 80 | | F | Hat Up | | 81 | | C | Hat Down | | 82 | | T | L | | 83 | | Y | R | | 84 | | G | ZL | Added in ver 0.16 | 85 | | H | ZR | Added in ver 0.16 | 86 | | U | Stick L Push | | 87 | | I | Stick R Push | | 88 | | L | | Tesla menu open | 89 | | Z | | Super jump to respawn point | 90 | | Num2 | Hat Down | | 91 | | Num4 | Hat Left | | 92 | | Num6 | Hat Right | | 93 | | Num8 | Hat Up | | 94 | | Arrow Key | Stick R | | 95 | | F5 | | X sensitivity+0.1 デバッグ用 | 96 | | F6 | | X sensitivity-0.1 デバッグ用 | 97 | | F7 | | Y sensitivity+0.1 デバッグ用 | 98 | | F8 | | Y sensitivity-0.1 デバッグ用 | 99 | | F9 | | Y following+0.1 デバッグ用 | 100 | | F10 | | Y following-0.1 デバッグ用 | 101 | | Mouse R | R | | 102 | | Mouse L | ZR | | 103 | | Mouse Side | ZL | | 104 | | Mouse Extra | ZR | Rapid Fire | 105 | | Mouse Wheel | Stick R Push | | 106 | | Mouse Middle | Stick R Push | | 107 | | Mouse move | Gyro | | 108 | 109 | 110 | ## 参考文献 111 | https://www.mzyy94.com/blog/2020/03/20/nintendo-switch-pro-controller-usb-gadget/ 112 | https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 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 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /proconcon_old.c: -------------------------------------------------------------------------------- 1 | /* 2 | スプラトゥーン3 マウスコンバーター for RaspberryPi 4b 3 | 2022/08/28 ぬこいばらき(unvirus) 4 | 5 | how to build 6 | gcc proconcon.c -o proconcon.out -l pthread -lm -O3 -Wall 7 | 8 | Version history 9 | ver.0.01 2022/08/27 First release 10 | ver.0.02 2022/08/28 マウスズレ調整 11 | ver.0.03 2022/09/03 デバイスの選択を自動化、ソース整理 12 | ver.0.04 2022/09/11 イカロールを出しやすくした 13 | ver.0.05 2022/09/20 自動ドット打ち処理に不具合があるので削除、メイン連射追加 14 | ver 0.06 2022/10/04 排他処理修正、復活地点にスーパージャンプを追加 15 | ver 0.07 2022/10/29 スティック補正を不要にした 16 | ver 0.08 2022/11/01 プロコン検出処理のバグを修正、スーパージャンプのバグを修正 17 | ver 0.09 2022/11/25 Firmware Ver4.33で、ジャイロ加速度値が変更されているので仮対応した 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /* 38 | スプラトゥーンでは、左右はジャイロの加速度で判断する 39 | 上下はジャイロの加速度と角度で判断する 40 | マウスの座標系とは異なるのでユーザー毎に調整が必要になる 41 | */ 42 | #define X_SENSITIVITY (20.0f) //マウス操作、左右感度 43 | #define Y_SENSITIVITY (23.0f) //マウス操作、上下感度 44 | #define Y_FOLLOWING (1.50f) //マウス操作、上下追従補正 45 | 46 | //スティック入力値 47 | #define AXIS_CENTER (1920) 48 | #define AXIS_MAX_INPUT (1920) 49 | 50 | /* 51 | スロー速度を設定する。0.83fならば全速力の83%になる 52 | イカ速に応じて調整が必要 53 | */ 54 | #define AXIS_HALF_INPUT_FACTOR (0.83f) 55 | 56 | #define MOUSE_READ_COUNTER (4) //指定した回数に達した場合マウス操作がされていない事を示す 57 | #define Y_ANGLE_UPPPER_LIMIT (3000) //Y上限 58 | #define Y_ANGLE_LOWER_LIMIT (-1500) //Y下限 59 | 60 | /* 61 | キーボードの特性上、進行方向と逆方向に移行する場合、最速(15ms)で行わないと 62 | 無入力期間が入りイカダッシュの停止と判定されイカロールが失敗する 63 | このため、方向入力を指定分だけ延長することで対処する 64 | 値が4なら4*15msの延長となる 65 | 値を大きくすれば、イカロールが出しやすくなるがイカダッシュの停止が遅れる 66 | */ 67 | #define DIR_FOLLOWING (4) //イカロールを行いやすくする 68 | 69 | #define MAX_NAME_LEN (256) 70 | #define MAX_PACKET_LEN (64) 71 | #define MAX_BUFFER_LEN (512) 72 | 73 | #define GADGET_NAME "/dev/hidg0" 74 | 75 | #define GADGET_DETACH "echo "" > /sys/kernel/config/usb_gadget/procon/UDC" 76 | #define GADGET_ATTACH "ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC" 77 | 78 | #define DEV_KEYBOARD (0) 79 | #define DEV_MOUSE (1) 80 | 81 | /* 82 | 各自で利用するキーボードとマウスを指定する 83 | 指定する名称は以下で確認する 84 | ls /dev/input/by-id 85 | 86 | 例 87 | ls /dev/input/by-id 88 | usb-Logitech_G403_Prodigy_Gaming_Mouse_148B38643831-event-mouse 89 | usb-Logitech_G403_Prodigy_Gaming_Mouse_148B38643831-if01-event-kbd 90 | usb-Logitech_G403_Prodigy_Gaming_Mouse_148B38643831-mouse 91 | usb-Nintendo_Co.__Ltd._Pro_Controller_000000000001-event-joystick 92 | usb-Nintendo_Co.__Ltd._Pro_Controller_000000000001-joystick 93 | usb-SIGMACHIP_USB_Keyboard-event-if01 94 | usb-SIGMACHIP_USB_Keyboard-event-kbd 95 | usb-Topre_Corporation_Realforce_108-event-kbd 96 | */ 97 | #define KEYBOARD_NAME "Topre_Corporation_Realforce_108" 98 | //#define KEYBOARD_NAME "usb-SIGMACHIP_USB_Keyboard" 99 | 100 | //#define MOUSE_NAME "Logitech_G403_Prodigy_Gaming_Mouse" 101 | #define MOUSE_NAME "usb-Logitech_G403_HERO_Gaming_Mouse" 102 | 103 | #define PROCON_NAME "Nintendo Co., Ltd. Pro Controller" //Procon名は固定 104 | 105 | typedef struct { 106 | short Y_Angle; //約4200から約-4200、平置き時約-668 107 | short X_Angle; //約4200から約-4200、平置き時約-28 108 | short Z_Angle; //解析情報をあさるとZ角度だが何かが変、平置き時約4075 109 | short X_Accel; //約16000から約-16000 110 | short Y_Accel; //約16000から約-16000 111 | short Z_Accel; //約16000から約-16000 112 | } ProconGyroData; 113 | 114 | typedef struct { 115 | //0 116 | unsigned char ReportId; //Report ID. value is 0x30. 117 | //1 118 | unsigned char TimeStamp; //Time stamp increase moderately. 119 | //2 120 | unsigned char ConnectNo:4; //Controller number? 121 | unsigned char BatteryLevel:4; //Battery Level? 122 | //3 123 | unsigned char Y:1; 124 | unsigned char X:1; 125 | unsigned char B:1; 126 | unsigned char A:1; 127 | unsigned char SR_R:1; //not use 128 | unsigned char SL_R:1; //not use 129 | unsigned char R:1; 130 | unsigned char ZR:1; 131 | //4 132 | unsigned char Minus:1; 133 | unsigned char Plus:1; 134 | unsigned char StickR:1; //Right stick push. 135 | unsigned char StickL:1; //Left stick push. 136 | unsigned char Home:1; 137 | unsigned char Capture:1; 138 | unsigned char None:1; //not use 139 | unsigned char Grip:1; //not use 140 | //5 141 | unsigned char Down:1; 142 | unsigned char Up:1; 143 | unsigned char Right:1; 144 | unsigned char Left:1; 145 | unsigned char SR_L:1; //not use 146 | unsigned char SL_L:1; //not use 147 | unsigned char L:1; 148 | unsigned char ZL:1; 149 | //6 - 8 150 | unsigned char L_Axis[3]; //12Bit単位でX値、Y値が入っている 151 | //9 - 11 152 | unsigned char R_Axis[3]; //12Bit単位でX値、Y値が入っている 153 | //12 154 | unsigned char Reserved; //unknown value (Vibrator_input_report) 155 | //13 - 63 156 | unsigned char GyroData[51]; //Gyro sensor data is repeated 3 times. Each with 5ms sampling. 157 | } ProconData; 158 | 159 | typedef struct { 160 | int X; 161 | int Y; 162 | int Wheel; 163 | unsigned char L; 164 | unsigned char R; 165 | unsigned char Middle; 166 | unsigned char Side; //Side button 1 167 | unsigned char Extra; //Side button 2 168 | } MouseData; 169 | 170 | int GamePadMode; 171 | int Processing; 172 | int fProcon; 173 | int fGadget; 174 | int fKeyboard; 175 | int fMouse; 176 | int thKeyboardCreated; 177 | int thMouseCreated; 178 | int thOutputReportCreated; 179 | int thInputReportCreated; 180 | int thBannerCreated; 181 | int MouseReadCnt; 182 | int YTotal; 183 | int Slow; 184 | int IkaToggle; 185 | int Straight; 186 | int StraightHalf; 187 | int Diagonal; 188 | int DiagonalHalf; 189 | int RappidFire; 190 | int MWButtonToggle; 191 | int ReturnToBase; 192 | int ReturnToBaseCnt; 193 | float XSensitivity; 194 | float YSensitivity; 195 | float YFollowing; 196 | double CircleAngle; 197 | pthread_t thKeyboard; 198 | pthread_t thMouse; 199 | pthread_t thOutputReport; 200 | pthread_t thInputReport; 201 | pthread_mutex_t MouseMtx; 202 | MouseData MouseMap; 203 | unsigned char DirPrev; 204 | unsigned char DirPrevCnt; 205 | unsigned char KeyMap[KEY_WIMAX]; 206 | 207 | int ReadCheck(int Fd) 208 | { 209 | int ret; 210 | fd_set rfds; 211 | struct timeval tv; 212 | 213 | if (Processing == 0) 214 | { 215 | return -1; 216 | } 217 | 218 | tv.tv_sec = 1; 219 | tv.tv_usec = 000000; 220 | 221 | FD_ZERO(&rfds); 222 | FD_SET(Fd, &rfds); 223 | 224 | ret = select(Fd + 1, &rfds, NULL, NULL, &tv); 225 | if (ret < 0) 226 | { 227 | Processing = 0; 228 | } 229 | 230 | if (Processing == 0) 231 | { 232 | ret = -1; 233 | } 234 | 235 | return ret; 236 | } 237 | 238 | unsigned short XValGet(unsigned char *pBuf) 239 | { 240 | unsigned short ret; 241 | 242 | ret = ((short)pBuf[1] & 0x0F) << 8; 243 | ret |= (short)pBuf[0]; 244 | 245 | return ret; 246 | } 247 | 248 | unsigned short YValGet(unsigned char *pBuf) 249 | { 250 | unsigned short ret; 251 | 252 | ret = (short)pBuf[1] >> 4; 253 | ret |= (short)pBuf[2] << 4; 254 | 255 | return ret; 256 | } 257 | 258 | void XValSet(unsigned char *pBuf, unsigned short X) 259 | { 260 | pBuf[0] = (unsigned char)(X & 0x00FF); 261 | pBuf[1] &= 0xF0; 262 | pBuf[1] |= (unsigned char)((X >> 8) & 0x000F); 263 | } 264 | 265 | void YValSet(unsigned char *pBuf, unsigned short Y) 266 | { 267 | pBuf[1] &= 0x0F; 268 | pBuf[1] |= (unsigned char)((Y << 4) & 0x00F0); 269 | pBuf[2] = (unsigned char)((Y >> 4) & 0x00FF); 270 | } 271 | 272 | void* KeybordThread(void *p) 273 | { 274 | int ret; 275 | struct input_event event; 276 | 277 | printf("KeybordThread start.\n"); 278 | 279 | while (Processing) 280 | { 281 | ret = ReadCheck(fKeyboard); 282 | if (ret <= 0) 283 | { 284 | continue; 285 | } 286 | 287 | ret = read(fKeyboard, &event, sizeof(event)); 288 | if (ret != sizeof(event)) 289 | { 290 | printf("Keybord read error %d.\n", errno); 291 | Processing = 0; 292 | continue; 293 | } 294 | 295 | if (event.type == EV_KEY) 296 | { 297 | //printf("code=0x%04x value=0x%08x.\n", event.code, event.value); 298 | //event.valueの値は、0=Off、1=On、2=Repeat 299 | 300 | if (event.value == 2) 301 | { 302 | //リピート時は何もしない 303 | continue; 304 | } 305 | 306 | KeyMap[event.code] = event.value; 307 | 308 | //設定処理 309 | if (KeyMap[KEY_Z]) 310 | { 311 | //復活地点へスーパージャンプ 312 | ReturnToBase = 1; 313 | } 314 | 315 | if (KeyMap[KEY_8]) 316 | { 317 | if (IkaToggle) 318 | { 319 | IkaToggle = 0; 320 | } 321 | else 322 | { 323 | IkaToggle = 1; 324 | } 325 | printf("IkaToggle=%d\n", IkaToggle); 326 | } 327 | 328 | if (KeyMap[KEY_9]) 329 | { 330 | //メイン単発、連射ボタンを入れ替える 331 | if (MWButtonToggle) 332 | { 333 | MWButtonToggle = 0; 334 | } 335 | else 336 | { 337 | MWButtonToggle = 1; 338 | } 339 | printf("MWButtonToggle=%d\n", MWButtonToggle); 340 | } 341 | 342 | Slow = KeyMap[KEY_LEFTSHIFT]; 343 | 344 | if (KeyMap[KEY_F5]) 345 | { 346 | XSensitivity += 0.1f; 347 | printf("X_SENSITIVITY=%f\n", XSensitivity); 348 | } 349 | 350 | if (KeyMap[KEY_F6]) 351 | { 352 | XSensitivity -= 0.1f; 353 | printf("X_SENSITIVITY=%f\n", XSensitivity); 354 | } 355 | 356 | if (KeyMap[KEY_F7]) 357 | { 358 | YSensitivity += 0.1f; 359 | printf("Y_SENSITIVITY=%f\n", YSensitivity); 360 | } 361 | 362 | if (KeyMap[KEY_F8]) 363 | { 364 | YSensitivity -= 0.1f; 365 | printf("Y_SENSITIVITY=%f\n", YSensitivity); 366 | } 367 | 368 | if (KeyMap[KEY_F9]) 369 | { 370 | YFollowing += 0.1f; 371 | printf("Y_FOLLOWING=%f\n", YFollowing); 372 | } 373 | 374 | if (KeyMap[KEY_F10]) 375 | { 376 | YFollowing -= 0.1f; 377 | printf("Y_FOLLOWING=%f\n", YFollowing); 378 | } 379 | 380 | if (KeyMap[KEY_F11] == 1) 381 | { 382 | printf("Keybord mode.\n"); 383 | GamePadMode = 0; 384 | } 385 | 386 | if (KeyMap[KEY_F12] == 1) 387 | { 388 | printf("GamePad mode.\n"); 389 | GamePadMode = 1; 390 | } 391 | } 392 | } 393 | 394 | printf("KeybordThread exit.\n"); 395 | return NULL; 396 | } 397 | 398 | void* MouseThread(void *p) 399 | { 400 | int ret; 401 | struct input_event event; 402 | 403 | printf("MouseThread start.\n"); 404 | 405 | while (Processing) 406 | { 407 | ret = ReadCheck(fMouse); 408 | if (ret <= 0) 409 | { 410 | continue; 411 | } 412 | 413 | ret = read(fMouse, &event, sizeof(event)); 414 | if (ret != sizeof(event)) 415 | { 416 | printf("Mouse read error %d.\n", errno); 417 | Processing = 0; 418 | continue; 419 | } 420 | 421 | if (event.type == EV_KEY) 422 | { 423 | //printf("code=0x%04x value=0x%08x.\n", event.code, event.value); 424 | //event.valueの値は、0=Off、1=On、2=Repeat 425 | 426 | if (event.value == 2) 427 | { 428 | //リピート時は何もしない 429 | continue; 430 | } 431 | 432 | pthread_mutex_lock(&MouseMtx); 433 | 434 | switch (event.code) 435 | { 436 | case BTN_LEFT: 437 | MouseMap.L = event.value; 438 | break; 439 | case BTN_RIGHT: 440 | MouseMap.R = event.value; 441 | break; 442 | case BTN_MIDDLE: 443 | MouseMap.Middle = event.value; 444 | break; 445 | case BTN_SIDE: 446 | MouseMap.Side = event.value; 447 | break; 448 | case BTN_EXTRA: 449 | MouseMap.Extra = event.value; 450 | break; 451 | default: 452 | break; 453 | } 454 | 455 | MouseReadCnt = 0; 456 | pthread_mutex_unlock(&MouseMtx); 457 | } 458 | else if (event.type == EV_REL) 459 | { 460 | //printf("code=0x%04x value=0x%08x.\n", event.code, event.value); 461 | 462 | pthread_mutex_lock(&MouseMtx); 463 | 464 | switch (event.code) 465 | { 466 | case REL_X: 467 | MouseMap.X = event.value; 468 | break; 469 | case REL_Y: 470 | MouseMap.Y = event.value; 471 | break; 472 | case REL_WHEEL: 473 | MouseMap.Wheel = event.value; 474 | break; 475 | default: 476 | break; 477 | } 478 | 479 | MouseReadCnt = 0; 480 | pthread_mutex_unlock(&MouseMtx); 481 | } 482 | } 483 | 484 | printf("MouseThread exit.\n"); 485 | return NULL; 486 | } 487 | 488 | void* OutputReportThread(void *p) 489 | { 490 | int ret; 491 | int len; 492 | unsigned char buf[MAX_PACKET_LEN]; 493 | 494 | printf("OutputReportThread start.\n"); 495 | 496 | while (Processing) 497 | { 498 | ret = ReadCheck(fGadget); 499 | if (ret <= 0) 500 | { 501 | continue; 502 | } 503 | 504 | ret = read(fGadget, buf, sizeof(buf)); 505 | if (ret == -1) 506 | { 507 | printf("Gadget OutputReport read error %d.\n", errno); 508 | Processing = 0; 509 | continue; 510 | } 511 | 512 | if (ret == 0) 513 | { 514 | continue; 515 | } 516 | 517 | len = ret; 518 | ret = write(fProcon, &buf, len); 519 | if (ret == -1) 520 | { 521 | printf("Procon OutputReport write error %d.\n", errno); 522 | Processing = 0; 523 | continue; 524 | } 525 | } 526 | 527 | printf("OutputReportThread exit.\n"); 528 | return NULL; 529 | } 530 | 531 | void StickDrawCircle(ProconData *pPad) 532 | { 533 | double c; 534 | unsigned short Lx, Ly, Rx, Ry; 535 | 536 | CircleAngle += 0.05; 537 | 538 | c = sin(CircleAngle) * (double)AXIS_CENTER; 539 | Lx = (unsigned short)(c + (double)AXIS_CENTER); 540 | 541 | c = cos(CircleAngle) * (double)AXIS_CENTER; 542 | Ly = (unsigned short)(c + (double)AXIS_CENTER); 543 | 544 | c = sin(CircleAngle) * (double)AXIS_CENTER; 545 | Rx = (unsigned short)(c + (double)AXIS_CENTER); 546 | 547 | c = cos(CircleAngle) * (double)AXIS_CENTER; 548 | Ry = (unsigned short)(c + (double)AXIS_CENTER); 549 | 550 | XValSet(pPad->L_Axis, Lx); 551 | YValSet(pPad->L_Axis, Ly); 552 | XValSet(pPad->R_Axis, Rx); 553 | YValSet(pPad->R_Axis, Ry); 554 | } 555 | 556 | void StickInput(unsigned char *pAxis, unsigned char Dir) 557 | { 558 | if (Dir == 0x08) 559 | { 560 | //上 561 | if (Slow) 562 | { 563 | XValSet(pAxis, AXIS_CENTER); 564 | YValSet(pAxis, AXIS_CENTER + StraightHalf); 565 | } 566 | else 567 | { 568 | XValSet(pAxis, AXIS_CENTER); 569 | YValSet(pAxis, AXIS_CENTER + Straight); 570 | } 571 | } 572 | else if (Dir == 0x0c) 573 | { 574 | //右上 575 | if (Slow) 576 | { 577 | XValSet(pAxis, AXIS_CENTER + DiagonalHalf); 578 | YValSet(pAxis, AXIS_CENTER + DiagonalHalf); 579 | } 580 | else 581 | { 582 | XValSet(pAxis, AXIS_CENTER + Diagonal); 583 | YValSet(pAxis, AXIS_CENTER + Diagonal); 584 | } 585 | } 586 | else if (Dir == 0x04) 587 | { 588 | //右 589 | if (Slow) 590 | { 591 | XValSet(pAxis, AXIS_CENTER + StraightHalf); 592 | YValSet(pAxis, AXIS_CENTER); 593 | } 594 | else 595 | { 596 | XValSet(pAxis, AXIS_CENTER + Straight); 597 | YValSet(pAxis, AXIS_CENTER); 598 | } 599 | 600 | } 601 | else if (Dir == 0x06) 602 | { 603 | //右下 604 | if (Slow) 605 | { 606 | XValSet(pAxis, AXIS_CENTER + DiagonalHalf); 607 | YValSet(pAxis, AXIS_CENTER - DiagonalHalf); 608 | } 609 | else 610 | { 611 | XValSet(pAxis, AXIS_CENTER + Diagonal); 612 | YValSet(pAxis, AXIS_CENTER - Diagonal); 613 | } 614 | } 615 | else if (Dir == 0x02) 616 | { 617 | //下 618 | if (Slow) 619 | { 620 | XValSet(pAxis, AXIS_CENTER); 621 | YValSet(pAxis, AXIS_CENTER - StraightHalf); 622 | } 623 | else 624 | { 625 | XValSet(pAxis, AXIS_CENTER); 626 | YValSet(pAxis, AXIS_CENTER - Straight); 627 | } 628 | } 629 | else if (Dir == 0x03) 630 | { 631 | //左下 632 | if (Slow) 633 | { 634 | XValSet(pAxis, AXIS_CENTER - DiagonalHalf); 635 | YValSet(pAxis, AXIS_CENTER - DiagonalHalf); 636 | } 637 | else 638 | { 639 | XValSet(pAxis, AXIS_CENTER - Diagonal); 640 | YValSet(pAxis, AXIS_CENTER - Diagonal); 641 | } 642 | } 643 | else if (Dir == 0x01) 644 | { 645 | //左 646 | if (Slow) 647 | { 648 | XValSet(pAxis, AXIS_CENTER - StraightHalf); 649 | YValSet(pAxis, AXIS_CENTER); 650 | } 651 | else 652 | { 653 | XValSet(pAxis, AXIS_CENTER - Straight); 654 | YValSet(pAxis, AXIS_CENTER); 655 | } 656 | } 657 | else if (Dir == 0x09) 658 | { 659 | //左上 660 | if (Slow) 661 | { 662 | XValSet(pAxis, AXIS_CENTER - DiagonalHalf); 663 | YValSet(pAxis, AXIS_CENTER + DiagonalHalf); 664 | } 665 | else 666 | { 667 | XValSet(pAxis, AXIS_CENTER - Diagonal); 668 | YValSet(pAxis, AXIS_CENTER + Diagonal); 669 | } 670 | } 671 | else 672 | { 673 | //入力無し 674 | XValSet(pAxis, AXIS_CENTER); 675 | YValSet(pAxis, AXIS_CENTER); 676 | } 677 | } 678 | 679 | void GyroEmurate(ProconData *pPad) 680 | { 681 | ProconGyroData gyro; 682 | 683 | memset(&gyro, 0, sizeof(gyro)); 684 | 685 | //X角度、Z角度変化しない 686 | gyro.Z_Angle = 4096; 687 | 688 | //Y角度合算 689 | YTotal += (int32_t)((float)MouseMap.Y * YFollowing * -1); 690 | 691 | if (YTotal > Y_ANGLE_UPPPER_LIMIT) 692 | { 693 | //これ以上進まないようにする 694 | YTotal = Y_ANGLE_UPPPER_LIMIT; 695 | MouseMap.Y = 0; 696 | } 697 | 698 | if (YTotal < Y_ANGLE_LOWER_LIMIT) 699 | { 700 | //これ以上進まないようにする 701 | YTotal = Y_ANGLE_LOWER_LIMIT; 702 | MouseMap.Y = 0; 703 | } 704 | 705 | gyro.Y_Angle = YTotal; 706 | //printf("YTotal=%d\n", YTotal); 707 | 708 | //上下 709 | gyro.Y_Accel = (short)((float)MouseMap.Y * YSensitivity); 710 | 711 | //左右 712 | gyro.Z_Accel = (short)((float)MouseMap.X * XSensitivity); 713 | //加速方向がマウスと逆なので逆転させる 714 | gyro.Z_Accel *= -1; 715 | 716 | //ジャイロデータは3サンプル分(1サンプル5ms)のデータを格納する 717 | //コンバータでは同じデータを3つ格納する 718 | memcpy(&pPad->GyroData[0], &gyro, sizeof(gyro)); 719 | memcpy(&pPad->GyroData[12], &gyro, sizeof(gyro)); 720 | memcpy(&pPad->GyroData[24], &gyro, sizeof(gyro)); 721 | 722 | MouseReadCnt++; 723 | if (MouseReadCnt > MOUSE_READ_COUNTER) 724 | { 725 | //マウス操作無し、XYを0にする 726 | //マウスは変化があった場合にデータが来る、よってXYに値が残っている 727 | MouseMap.X = 0; 728 | MouseMap.Y = 0; 729 | MouseReadCnt = 0; 730 | } 731 | } 732 | 733 | /* 734 | マクロサンプル 735 | スタート地点へスーパージャンプする 736 | ProconInputの呼び出し間隔は15msなのでProconInput内で呼ぶReturnToBaseMacroも 737 | 15ms間隔で呼ばれることになる 738 | */ 739 | void ReturnToBaseMacro(ProconData *pPad) 740 | { 741 | if (ReturnToBase) 742 | { 743 | switch (ReturnToBaseCnt) 744 | { 745 | case 0: 746 | case 1: 747 | case 2: 748 | pPad->R = 0; 749 | pPad->L = 0; 750 | pPad->ZL = 0; 751 | pPad->ZR = 0; 752 | 753 | pPad->A = 0; 754 | pPad->B = 0; 755 | pPad->X = 1; 756 | pPad->Y = 0; 757 | 758 | pPad->Up = 0; 759 | pPad->Down = 0; 760 | pPad->Left = 0; 761 | pPad->Right = 0; 762 | 763 | ReturnToBaseCnt++; 764 | break; 765 | case 3: 766 | case 4: 767 | case 5: 768 | pPad->R = 0; 769 | pPad->L = 0; 770 | pPad->ZL = 0; 771 | pPad->ZR = 0; 772 | 773 | pPad->A = 0; 774 | pPad->B = 0; 775 | pPad->X = 1; 776 | pPad->Y = 0; 777 | 778 | pPad->Up = 0; 779 | pPad->Down = 1; 780 | pPad->Left = 0; 781 | pPad->Right = 0; 782 | 783 | ReturnToBaseCnt++; 784 | break; 785 | case 6: 786 | case 7: 787 | case 8: 788 | pPad->R = 0; 789 | pPad->L = 0; 790 | pPad->ZL = 0; 791 | pPad->ZR = 0; 792 | 793 | pPad->A = 1; 794 | pPad->B = 0; 795 | pPad->X = 1; 796 | pPad->Y = 0; 797 | 798 | pPad->Up = 0; 799 | pPad->Down = 1; 800 | pPad->Left = 0; 801 | pPad->Right = 0; 802 | 803 | ReturnToBaseCnt++; 804 | break; 805 | case 9: 806 | case 10: 807 | case 11: 808 | pPad->R = 0; 809 | pPad->L = 0; 810 | pPad->ZL = 0; 811 | pPad->ZR = 0; 812 | 813 | pPad->A = 1; 814 | pPad->B = 0; 815 | pPad->X = 1; 816 | pPad->Y = 0; 817 | 818 | pPad->Up = 0; 819 | pPad->Down = 0; 820 | pPad->Left = 0; 821 | pPad->Right = 0; 822 | 823 | ReturnToBaseCnt++; 824 | break; 825 | default: 826 | ReturnToBaseCnt = 0; 827 | ReturnToBase = 0; 828 | break; 829 | } 830 | } 831 | } 832 | 833 | void ProconInput(ProconData *pPad) 834 | { 835 | unsigned char dir; 836 | 837 | //key 838 | if (KeyMap[KEY_1]) 839 | { 840 | //視点リセット 841 | pPad->Y = 1; 842 | YTotal = 0; 843 | } 844 | 845 | if (KeyMap[KEY_2] == 1) 846 | { 847 | //キャプチャー 848 | pPad->Capture = 1; 849 | } 850 | 851 | if (KeyMap[KEY_3] == 1) 852 | { 853 | //マイナス 854 | pPad->Minus = 1; 855 | } 856 | 857 | if (KeyMap[KEY_4] == 1) 858 | { 859 | //プラス 860 | pPad->Plus = 1; 861 | } 862 | 863 | if (KeyMap[KEY_ESC] == 1) 864 | { 865 | //ホーム 866 | pPad->Home = 1; 867 | } 868 | 869 | if (KeyMap[KEY_E] == 1) 870 | { 871 | //スーパージャンプ決定 872 | //アサリ 873 | pPad->A = 1; 874 | } 875 | 876 | if (KeyMap[KEY_R] == 1) 877 | { 878 | //マップ 879 | pPad->X = 1; 880 | } 881 | 882 | if (KeyMap[KEY_F] == 1) 883 | { 884 | //カモン 885 | pPad->Up = 1; 886 | } 887 | 888 | if (KeyMap[KEY_C] == 1) 889 | { 890 | //ナイス 891 | pPad->Down = 1; 892 | } 893 | 894 | if (KeyMap[KEY_SPACE] == 1) 895 | { 896 | //ジャンプ 897 | pPad->B = 1; 898 | } 899 | 900 | if (KeyMap[KEY_Q] == 1) 901 | { 902 | //視点センターリング 903 | YTotal = 0; 904 | } 905 | 906 | if (KeyMap[KEY_T] == 1) 907 | { 908 | //L 909 | pPad->L = 1; 910 | } 911 | 912 | if (KeyMap[KEY_Y] == 1) 913 | { 914 | //R 915 | pPad->R = 1; 916 | } 917 | 918 | if (KeyMap[KEY_U] == 1) 919 | { 920 | //L Stick 921 | pPad->StickL = 1; 922 | } 923 | 924 | if (KeyMap[KEY_KP8] == 1) 925 | { 926 | pPad->Up = 1; 927 | } 928 | 929 | if (KeyMap[KEY_KP2] == 1) 930 | { 931 | pPad->Down = 1; 932 | } 933 | 934 | if (KeyMap[KEY_KP4] == 1) 935 | { 936 | pPad->Left = 1; 937 | } 938 | 939 | if (KeyMap[KEY_KP6] == 1) 940 | { 941 | pPad->Right = 1; 942 | } 943 | 944 | //printf("StickL X=%d, Y=%d\n", XValGet(pPad->L_Axis), YValGet(pPad->L_Axis)); 945 | //printf("StickR X=%d, Y=%d\n", XValGet(pPad->R_Axis), YValGet(pPad->R_Axis)); 946 | 947 | //左スティック 948 | dir = KeyMap[KEY_W] << 3; 949 | dir |= KeyMap[KEY_D] << 2; 950 | dir |= KeyMap[KEY_S] << 1; 951 | dir |= KeyMap[KEY_A]; 952 | 953 | if ((dir == 0) && (DirPrevCnt <= DIR_FOLLOWING)) 954 | { 955 | DirPrevCnt++; 956 | StickInput(pPad->L_Axis, DirPrev); 957 | } 958 | else 959 | { 960 | StickInput(pPad->L_Axis, dir); 961 | DirPrev = dir; 962 | DirPrevCnt = 0; 963 | } 964 | 965 | //右スティック 966 | dir = KeyMap[KEY_UP] << 3; 967 | dir |= KeyMap[KEY_RIGHT] << 2; 968 | dir |= KeyMap[KEY_DOWN] << 1; 969 | dir |= KeyMap[KEY_LEFT]; 970 | StickInput(pPad->R_Axis, dir); 971 | 972 | if (KeyMap[KEY_0] == 1) 973 | { 974 | //スティック補正のとき、0キーを押し続けてスティックぐるぐるをおこなう。 975 | StickDrawCircle(pPad); 976 | } 977 | 978 | pthread_mutex_lock(&MouseMtx); 979 | 980 | //mouse 981 | GyroEmurate(pPad); 982 | 983 | if (MouseMap.R) 984 | { 985 | //サブ 986 | pPad->R = 1; 987 | } 988 | 989 | if (MouseMap.L) 990 | { 991 | 992 | if (MWButtonToggle == 0) 993 | { 994 | //メイン単発 995 | pPad->ZR = 1; 996 | } 997 | else 998 | { 999 | //メイン連射 1000 | if (RappidFire != 0) 1001 | { 1002 | pPad->ZR = 1; 1003 | RappidFire = 0; 1004 | } 1005 | else 1006 | { 1007 | RappidFire = 1; 1008 | } 1009 | } 1010 | } 1011 | 1012 | if (IkaToggle) 1013 | { 1014 | pPad->ZL = 1; 1015 | } 1016 | 1017 | if (MouseMap.Side) 1018 | { 1019 | //イカ 1020 | if (IkaToggle == 0) 1021 | { 1022 | pPad->ZL = 1; 1023 | } 1024 | else 1025 | { 1026 | pPad->ZL = 0; 1027 | } 1028 | } 1029 | 1030 | if (MouseMap.Extra) 1031 | { 1032 | if (MWButtonToggle == 0) 1033 | { 1034 | //メイン連射 1035 | if (RappidFire != 0) 1036 | { 1037 | pPad->ZR = 1; 1038 | RappidFire = 0; 1039 | } 1040 | else 1041 | { 1042 | RappidFire = 1; 1043 | } 1044 | } 1045 | else 1046 | { 1047 | //メイン単発 1048 | pPad->ZR = 1; 1049 | } 1050 | } 1051 | 1052 | if (MouseMap.Wheel) 1053 | { 1054 | //スペシャル、マウスホイールを動かす 1055 | pPad->StickR = 1; 1056 | MouseMap.Wheel = 0; 1057 | } 1058 | 1059 | if (MouseMap.Middle) 1060 | { 1061 | //スペシャル 1062 | pPad->StickR = 1; 1063 | } 1064 | 1065 | pthread_mutex_unlock(&MouseMtx); 1066 | 1067 | ReturnToBaseMacro(pPad); 1068 | } 1069 | 1070 | void* InputReportThread(void *p) 1071 | { 1072 | int ret; 1073 | int len; 1074 | unsigned char buf[MAX_PACKET_LEN]; 1075 | 1076 | printf("InputReportThread start.\n"); 1077 | 1078 | while (Processing) 1079 | { 1080 | ret = ReadCheck(fProcon); 1081 | if (ret <= 0) 1082 | { 1083 | continue; 1084 | } 1085 | 1086 | ret = read(fProcon, buf, sizeof(buf)); 1087 | if (ret == -1) 1088 | { 1089 | printf("fProcon InputReport read error %d.\n", errno); 1090 | Processing = 0; 1091 | continue; 1092 | } 1093 | len = ret; 1094 | 1095 | if (len == 0) 1096 | { 1097 | continue; 1098 | } 1099 | 1100 | 1101 | if (buf[0] == 0x30) 1102 | { 1103 | if (GamePadMode == 0) 1104 | { 1105 | ProconInput((ProconData *)buf); 1106 | } 1107 | else 1108 | { 1109 | #if 0 1110 | ProconGyroData gyro; 1111 | memcpy(&gyro, ((ProconData *)buf)->GyroData, sizeof(gyro)); 1112 | printf("x1=%d y1=%d z1=%d x2=%d y2=%d z2=%d.\n", 1113 | gyro.X_Angle,gyro.Y_Angle, gyro.Z_Angle, 1114 | gyro.X_Accel, gyro.Y_Accel, gyro.Z_Accel); 1115 | #endif 1116 | } 1117 | } 1118 | else 1119 | { 1120 | if (buf[0] == 0x21) 1121 | { 1122 | if ((buf[13] == 0x90) && (buf[14] == 0x10)) 1123 | { 1124 | if ((buf[15] == 0x10) && (buf[16] == 0x80)) 1125 | { 1126 | //スティック補正情報を変更する 1127 | 1128 | //SPI address 0x8010, Magic 0xxB2 0xxA1 for user available calibration 1129 | buf[20] = 0xB2; 1130 | buf[21] = 0xA1; 1131 | 1132 | //SPI address 0x8012, Actual User Left Stick Calibration data 1133 | 1134 | XValSet(&buf[22], AXIS_MAX_INPUT - 1); 1135 | YValSet(&buf[22], AXIS_MAX_INPUT - 1); 1136 | XValSet(&buf[25], AXIS_CENTER); 1137 | YValSet(&buf[25], AXIS_CENTER); 1138 | XValSet(&buf[28], AXIS_MAX_INPUT); 1139 | YValSet(&buf[28], AXIS_MAX_INPUT); 1140 | 1141 | //SPI address 0x801B, Magic 0xB2 0xA1 for user available calibration 1142 | buf[31] = 0xB2; 1143 | buf[32] = 0xA1; 1144 | 1145 | //SPI address 0x801D, Actual user Right Stick Calibration data 1146 | XValSet(&buf[33], AXIS_CENTER); 1147 | YValSet(&buf[33], AXIS_CENTER); 1148 | XValSet(&buf[36], AXIS_MAX_INPUT); 1149 | YValSet(&buf[36], AXIS_MAX_INPUT); 1150 | XValSet(&buf[39], AXIS_MAX_INPUT - 1); 1151 | YValSet(&buf[39], AXIS_MAX_INPUT - 1); 1152 | 1153 | buf[42] = 0xB2; 1154 | buf[43] = 0xA1; 1155 | } 1156 | } 1157 | 1158 | if ((buf[13] == 0x82) && (buf[14] == 0x02)) 1159 | { 1160 | //restore previous firmware version 1161 | printf("FirmVer=%d.%d\n", buf[15], buf[16]); 1162 | buf[15] = 0x03; 1163 | buf[16] = 0x48; 1164 | } 1165 | } 1166 | } 1167 | 1168 | /* 1169 | USB給電対応のためUSBFがVBUS切断検知を行わない 1170 | このため、Nintendo Switch <-> Raspberry Pi間のUSBケーブルを切断するタイミングで 1171 | write()を行うと処理が戻らない 1172 | USB Gadget Driverを改造すれば解消される 1173 | 無改造の場合、再接続時のBusResetのときにエラーが返る 1174 | */ 1175 | ret = write(fGadget, buf, len); 1176 | if (ret == -1) 1177 | { 1178 | printf("fGadget InputReport write error %d.\n", errno); 1179 | Processing = 0; 1180 | continue; 1181 | } 1182 | } 1183 | 1184 | printf("InputReportThread exit.\n"); 1185 | return NULL; 1186 | } 1187 | 1188 | int InputDevNameGet(int DevType, char *pSearchName, char *pDevName) 1189 | { 1190 | int found; 1191 | DIR *dir; 1192 | struct dirent *dp; 1193 | char *p; 1194 | 1195 | dir = opendir("/dev/input/by-id"); 1196 | if (dir == NULL) 1197 | { 1198 | printf("opendir error %d", errno); 1199 | return -1; 1200 | } 1201 | 1202 | found = -1; 1203 | dp = readdir(dir); 1204 | while (dp != NULL) 1205 | { 1206 | if (DevType == DEV_KEYBOARD) 1207 | { 1208 | //Keyboard 1209 | p = strstr(dp->d_name, "event-kbd"); 1210 | if (p != NULL) 1211 | { 1212 | p = strstr(dp->d_name, pSearchName); 1213 | if (p != NULL) 1214 | { 1215 | sprintf(pDevName, "/dev/input/by-id/%s", dp->d_name); 1216 | printf("Keyboard:%s\n", pDevName); 1217 | found = 0; 1218 | break; 1219 | } 1220 | } 1221 | } 1222 | else 1223 | { 1224 | //Mouse 1225 | p = strstr(dp->d_name, "event-mouse"); 1226 | if (p != NULL) 1227 | { 1228 | p = strstr(dp->d_name, pSearchName); 1229 | if (p != NULL) 1230 | { 1231 | sprintf(pDevName, "/dev/input/by-id/%s", dp->d_name); 1232 | printf("Mouse:%s\n", pDevName); 1233 | found = 0; 1234 | break; 1235 | } 1236 | } 1237 | } 1238 | 1239 | dp = readdir(dir); 1240 | } 1241 | 1242 | closedir(dir); 1243 | return found; 1244 | } 1245 | 1246 | int ProconHidrawNameGet(char *HidrawName) 1247 | { 1248 | int ret; 1249 | int found; 1250 | int fd; 1251 | DIR *dir; 1252 | struct dirent *dp; 1253 | char *p; 1254 | char buf[MAX_BUFFER_LEN]; 1255 | 1256 | dir = opendir("/sys/class/hidraw"); 1257 | if (dir == NULL) 1258 | { 1259 | printf("opendir error %d", errno); 1260 | return -1; 1261 | } 1262 | 1263 | found = -1; 1264 | dp = readdir(dir); 1265 | while (dp != NULL) 1266 | { 1267 | if (dp->d_type == DT_LNK) 1268 | { 1269 | sprintf(buf, "/sys/class/hidraw/%s/device/uevent", dp->d_name); 1270 | fd = open(buf, O_RDONLY); 1271 | if (fd != -1) 1272 | { 1273 | ret = read(fd, buf, MAX_BUFFER_LEN); 1274 | close(fd); 1275 | 1276 | if (ret > 0) 1277 | { 1278 | p = strstr(buf, PROCON_NAME); 1279 | if (p) 1280 | { 1281 | sprintf(HidrawName, "/dev/%s", dp->d_name); 1282 | printf("Procon:%s\n", HidrawName); 1283 | found = 0; 1284 | break; 1285 | } 1286 | } 1287 | } 1288 | } 1289 | 1290 | dp = readdir(dir); 1291 | } 1292 | 1293 | closedir(dir); 1294 | return found; 1295 | } 1296 | 1297 | int main(int argc, char *argv[]) 1298 | { 1299 | int ret; 1300 | char devName[MAX_NAME_LEN]; 1301 | 1302 | printf("Procon Converter start.\n"); 1303 | 1304 | /* 1305 | 初期値 1306 | マウス操作は画面に対して、横がX、縦がYとする 1307 | Proconのジャイロとは座標軸が異なるので注意すること 1308 | Proconの座標は平置きして上から見たときに 1309 | https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/imu_sensor_notes.md 1310 | のLeft Jyo-Con図と合わせてある 1311 | */ 1312 | XSensitivity = X_SENSITIVITY; 1313 | YSensitivity = Y_SENSITIVITY; 1314 | YFollowing = Y_FOLLOWING; 1315 | Processing = 1; 1316 | fKeyboard = -1; 1317 | fMouse = -1; 1318 | fProcon = -1; 1319 | fGadget = -1; 1320 | YTotal = 0; 1321 | Straight = AXIS_MAX_INPUT; 1322 | StraightHalf = (int)((float)Straight * AXIS_HALF_INPUT_FACTOR); 1323 | Diagonal = (int)(0.7071f * (float)AXIS_MAX_INPUT); //0.7071 is cos 45 1324 | DiagonalHalf = (int)((float)Diagonal * AXIS_HALF_INPUT_FACTOR); 1325 | 1326 | pthread_mutex_init(&MouseMtx, NULL); 1327 | 1328 | ret = InputDevNameGet(DEV_KEYBOARD, KEYBOARD_NAME, devName); 1329 | if (ret == -1) 1330 | { 1331 | printf("Keybord is not found.\n"); 1332 | Processing = 0; 1333 | goto EXIT; 1334 | } 1335 | 1336 | fKeyboard = open(devName, O_RDONLY); 1337 | if (fKeyboard == -1) 1338 | { 1339 | printf("Keybord open error %d.\n", errno); 1340 | Processing = 0; 1341 | goto EXIT; 1342 | } 1343 | 1344 | ret = InputDevNameGet(DEV_MOUSE, MOUSE_NAME, devName); 1345 | if (ret == -1) 1346 | { 1347 | printf("Mouse is not found.\n"); 1348 | Processing = 0; 1349 | goto EXIT; 1350 | } 1351 | 1352 | fMouse = open(devName, O_RDONLY); 1353 | if (fMouse == -1) 1354 | { 1355 | printf("Mouse open error %d.\n", errno); 1356 | Processing = 0; 1357 | goto EXIT; 1358 | } 1359 | 1360 | ret = ProconHidrawNameGet(devName); 1361 | if (ret == -1) 1362 | { 1363 | printf("Procon is not found.\n"); 1364 | Processing = 0; 1365 | goto EXIT; 1366 | } 1367 | 1368 | fProcon = open(devName, O_RDWR); 1369 | if (fProcon == -1) 1370 | { 1371 | printf("Procon open error %d.\n", errno); 1372 | Processing = 0; 1373 | goto EXIT; 1374 | } 1375 | 1376 | system(GADGET_DETACH); 1377 | sleep(2); 1378 | system(GADGET_ATTACH); 1379 | 1380 | fGadget = open(GADGET_NAME, O_RDWR); 1381 | if (fGadget == -1) 1382 | { 1383 | printf("Gadget open error %d.\n", errno); 1384 | Processing = 0; 1385 | goto EXIT; 1386 | } 1387 | 1388 | ret = pthread_create(&thInputReport, NULL, InputReportThread, NULL); 1389 | if (ret != 0) 1390 | { 1391 | printf("InputReport thread create error %d.\n", ret); 1392 | Processing = 0; 1393 | goto EXIT; 1394 | } 1395 | thInputReportCreated = 1; 1396 | 1397 | ret = pthread_create(&thOutputReport, NULL, OutputReportThread, NULL); 1398 | if (ret != 0) 1399 | { 1400 | printf("OutputReport thread create error %d.\n", ret); 1401 | Processing = 0; 1402 | goto EXIT; 1403 | } 1404 | thOutputReportCreated = 1; 1405 | 1406 | ret = pthread_create(&thKeyboard, NULL, KeybordThread, NULL); 1407 | if (ret != 0) 1408 | { 1409 | printf("Keybord thread create error %d.\n", ret); 1410 | Processing = 0; 1411 | goto EXIT; 1412 | } 1413 | thKeyboardCreated = 1; 1414 | 1415 | ret = pthread_create(&thMouse, NULL, MouseThread, NULL); 1416 | if (ret != 0) 1417 | { 1418 | printf("Mouse thread create error %d.\n", ret); 1419 | Processing = 0; 1420 | goto EXIT; 1421 | } 1422 | thMouseCreated = 1; 1423 | 1424 | EXIT: 1425 | while (Processing) 1426 | { 1427 | sleep(3); 1428 | } 1429 | 1430 | if (thKeyboardCreated) 1431 | { 1432 | pthread_join(thKeyboard, NULL); 1433 | } 1434 | 1435 | if (thMouseCreated) 1436 | { 1437 | pthread_join(thMouse, NULL); 1438 | } 1439 | 1440 | if (thOutputReportCreated) 1441 | { 1442 | pthread_join(thOutputReport, NULL); 1443 | } 1444 | 1445 | if (thInputReportCreated) 1446 | { 1447 | pthread_join(thInputReport, NULL); 1448 | } 1449 | 1450 | if (fKeyboard != -1) 1451 | { 1452 | close(fKeyboard); 1453 | } 1454 | 1455 | if (fMouse != -1) 1456 | { 1457 | close(fMouse); 1458 | } 1459 | 1460 | if (fGadget != -1) 1461 | { 1462 | close(fGadget); 1463 | } 1464 | 1465 | if (fProcon != -1) 1466 | { 1467 | close(fProcon); 1468 | } 1469 | 1470 | pthread_mutex_destroy(&MouseMtx); 1471 | 1472 | printf("Procon Converter exit.\n"); 1473 | return 0; 1474 | } 1475 | 1476 | -------------------------------------------------------------------------------- /proconcon.c: -------------------------------------------------------------------------------- 1 | /* 2 | スプラトゥーン3 マウスコンバーター for RaspberryPi 4b 3 | 2022/08/28 ぬこいばらき(unvirus) 4 | 5 | how to build 6 | gcc proconcon.c -o proconcon.out -l pthread -lm -O3 -Wall 7 | 8 | Version history 9 | ver.0.01 2022/08/27 First release 10 | ver.0.02 2022/08/28 マウスズレ調整 11 | ver.0.03 2022/09/03 デバイスの選択を自動化、ソース整理 12 | ver.0.04 2022/09/11 イカロールを出しやすくした 13 | ver.0.05 2022/09/20 自動ドット打ち処理に不具合があるので削除、メイン連射追加 14 | ver 0.06 2022/10/04 排他処理修正、復活地点にスーパージャンプを追加 15 | ver 0.07 2022/10/29 スティック補正を不要にした 16 | ver 0.08 2022/11/01 プロコン検出処理のバグを修正、スーパージャンプのバグを修正 17 | ver 0.09 2022/11/25 Firmware Ver4.33で、ジャイロ加速度値が変更されているので仮対応した 18 | ver 0.10 2022/11/27 プロコン接続を不要にした 19 | ver 0.11 2022/12/02 Swicthのサスペンド時のプロコンコマンドに対応、コメント追加 20 | ver 0.12 2022/12/11 人イカ逆転モードを廃止、サブ慣性キャンセル機能を追加 21 | ver 0.13 2022/12/16 センタリング時、少し上を向くので微調整した 22 | ver 0.14 2023/01/08 マウスを左右に振った時の追従性を向上 23 | ver 0.15 2023/02/12 自動イカロール機能を追加、反対方向入力で自動でイカロールする 24 | ver 0.16 2023/05/06 マウスを上下に強く動かすと座標が変になる不具合を修正、センターリングホールドモードを追加 25 | ver 0.17 2023/07/08 冗長なソースコードを整理しました。センターリングホールドモードは使いにくいので削除した 26 | ver 0.18 2023/10/17 SHIFTキーを押している間、ゆっくり動作が中断されない不具合を修正した 27 | ver 0.19 2024/01/28 操作中にターミナルで余計な文字が出ないようにした、64BitOSで動作確認した 28 | ver 0.20 2024/02/03 プログラムの終了処理を調整した 29 | ver 0.21 2024/05/31 低速連射モード追加 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | //debug 51 | #define ENUM_HID_DEVICE //list up hid input device 52 | 53 | /* 54 | スプラトゥーンでは、左右はジャイロの加速度で判断する 55 | 上下はジャイロの加速度と角度で判断する 56 | マウスの座標系とは異なるのでユーザー毎に調整が必要になる 57 | */ 58 | #define X_SENSITIVITY (17.2f) //マウス操作、左右感度 59 | #define Y_SENSITIVITY (20.5f) //マウス操作、上下感度 60 | #define Y_FOLLOWING (1.00f) //マウス操作、上下追従補正 61 | 62 | //スティック入力値 63 | #define AXIS_CENTER (1920) 64 | #define AXIS_MAX_INPUT (1920) 65 | 66 | /* 67 | スロー速度を設定する。0.83fならば全速力の83%になる 68 | イカ速に応じて調整が必要 69 | */ 70 | #define AXIS_HALF_INPUT_FACTOR (0.65f) 71 | 72 | #define Y_ANGLE_UPPPER_LIMIT (3000) //Y angle imit 73 | #define Y_ANGLE_LOWER_LIMIT (-1500) //Y angle imit 74 | #define Y_ACCEL_UPPPER_LIMIT (16000) //Y acceleration limit 75 | #define Y_ACCEL_LOWER_LIMIT (-16000) //Y acceleration limit 76 | 77 | #define MAX_NAME_LEN (256) 78 | #define MAX_PACKET_LEN (64) 79 | #define MAX_BUFFER_LEN (512) 80 | #define MAC_ADDRESS_LEN (6) 81 | 82 | #define GADGET_NAME "/dev/hidg0" 83 | 84 | #define GADGET_DETACH "echo "" > /sys/kernel/config/usb_gadget/procon/UDC" 85 | #define GADGET_ATTACH "ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC" 86 | 87 | #define DEV_KEYBOARD (0) 88 | #define DEV_MOUSE (1) 89 | 90 | #define PAD_INPUT_WAIT (16) //コントローラーの入力間隔(ms) 91 | #define PAD_INPUT_WAIT_MARGIN (500000) 92 | 93 | #define INERTIA_CANCEL_ENABLE //自動サブ慣性キャンセル機能を無効にする場合はコメントアウトする 94 | //#define SQUID_ROLL_ENABLE //自動イカロール機能を無効にする場合はコメントアウトする 95 | 96 | #define DELEY_FOR_AFTER_JUMP (50) //ジャンプ後、慣性キャンセルを行うようになるまでの時間、16ms単位 97 | #define DELEY_FOR_AFTER_MAIN_WP (12) //メイン攻撃後、慣性キャンセルを行うようになるまでの時間、16ms単位 98 | #define DELEY_FOR_AFTER_SUB_WP (12) //サブ攻撃後、慣性キャンセルを行うようになるまでの時間、16ms単位 99 | #define MOVE_STOP_TIME (12) //動作停止までの時間、16ms単位 100 | #define ROLL_INPUT_TIME (15) //イカロール受付時間、16ms単位 101 | #define ROLL_JUMP_TIME (25) //イカロールジャンプ入力時間、16ms単位 102 | 103 | #define STICK_DIR_UP (0x08) 104 | #define STICK_DIR_RIGHT_UP (0x0C) 105 | #define STICK_DIR_RIGHT (0x04) 106 | #define STICK_DIR_RIGHT_DOWN (0x06) 107 | #define STICK_DIR_DOWN (0x02) 108 | #define STICK_DIR_LEFT_DOWN (0x03) 109 | #define STICK_DIR_LEFT (0x01) 110 | #define STICK_DIR_LEFT_UP (0x09) 111 | 112 | /* 113 | 各自で利用するキーボードとマウスを指定する 114 | 指定する名称は以下で確認する 115 | ls /dev/input/by-id 116 | 117 | 例 118 | ls /dev/input/by-id 119 | usb-Logitech_G403_Prodigy_Gaming_Mouse_148B38643831-event-mouse 120 | usb-Logitech_G403_Prodigy_Gaming_Mouse_148B38643831-if01-event-kbd 121 | usb-Logitech_G403_Prodigy_Gaming_Mouse_148B38643831-mouse 122 | usb-Nintendo_Co.__Ltd._Pro_Controller_000000000001-event-joystick 123 | usb-Nintendo_Co.__Ltd._Pro_Controller_000000000001-joystick 124 | usb-SIGMACHIP_USB_Keyboard-event-if01 125 | usb-SIGMACHIP_USB_Keyboard-event-kbd 126 | usb-Topre_Corporation_Realforce_108-event-kbd 127 | */ 128 | //#define KEYBOARD_NAME "Topre_Corporation_Realforce_108" 129 | //#define KEYBOARD_NAME "usb-SIGMACHIP_USB_Keyboard" 130 | #define KEYBOARD_NAME "usb-SINO_WEALTH_Gaming_KB" 131 | 132 | //#define MOUSE_NAME "Logitech_G403_Prodigy_Gaming_Mouse" 133 | #define MOUSE_NAME "usb-Logitech_G403_HERO_Gaming_Mouse" 134 | 135 | #define ROM_FILE_NAME "./flashrom.bin" 136 | 137 | typedef struct { 138 | short Y_Angle; //約4200から約-4200、平置き時約-668 139 | short X_Angle; //約4200から約-4200、平置き時約-28 140 | short Z_Angle; //解析情報をあさるとZ角度だが何かが変、平置き時約4075 141 | short X_Accel; //約16000から約-16000 142 | short Y_Accel; //約16000から約-16000 143 | short Z_Accel; //約16000から約-16000 144 | } ProconGyroData; 145 | 146 | typedef struct { 147 | //0 148 | unsigned char ReportId; //Report ID. value is 0x30. 149 | //1 150 | unsigned char TimeStamp; //Time stamp increase moderately. 151 | //2 152 | unsigned char ConnectNo:4; //Controller number? 153 | unsigned char BatteryLevel:4; //Battery Level? 154 | //3 155 | unsigned char Y:1; 156 | unsigned char X:1; 157 | unsigned char B:1; 158 | unsigned char A:1; 159 | unsigned char SR_R:1; //not use 160 | unsigned char SL_R:1; //not use 161 | unsigned char R:1; 162 | unsigned char ZR:1; 163 | //4 164 | unsigned char Minus:1; 165 | unsigned char Plus:1; 166 | unsigned char StickR:1; //Right stick push. 167 | unsigned char StickL:1; //Left stick push. 168 | unsigned char Home:1; 169 | unsigned char Capture:1; 170 | unsigned char None:1; //not use 171 | unsigned char Grip:1; //not use 172 | //5 173 | unsigned char Down:1; 174 | unsigned char Up:1; 175 | unsigned char Right:1; 176 | unsigned char Left:1; 177 | unsigned char SR_L:1; //not use 178 | unsigned char SL_L:1; //not use 179 | unsigned char L:1; 180 | unsigned char ZL:1; 181 | //6 - 8 182 | unsigned char L_Axis[3]; //12Bit単位でX値、Y値が入っている 183 | //9 - 11 184 | unsigned char R_Axis[3]; //12Bit単位でX値、Y値が入っている 185 | //12 186 | unsigned char Reserved; //unknown value (Vibrator_input_report) 187 | //13 - 63 188 | unsigned char GyroData[51]; //Gyro sensor data is repeated 3 times. Each with 5ms sampling. 189 | } ProconData; 190 | 191 | typedef struct { 192 | int X; 193 | int Y; 194 | int Wheel; 195 | unsigned char L; 196 | unsigned char R; 197 | unsigned char Middle; 198 | unsigned char Side; //Side button 1 199 | unsigned char Extra; //Side button 2 200 | } MouseData; 201 | 202 | int Processing; 203 | int fGadget; 204 | int fKeyboard; 205 | int fMouse; 206 | int thKeyboardCreated; 207 | int thMouseCreated; 208 | int thOutputReportCreated; 209 | int thInputReportCreated; 210 | int YTotal; 211 | int Slow; 212 | int Straight; 213 | int StraightHalf; 214 | int Diagonal; 215 | int DiagonalHalf; 216 | int RapidFireCnt; 217 | int RapidFireWait; 218 | int MWBtnToggle; 219 | int ReturnToBase; 220 | int ReturnToBaseCnt; 221 | int HidMode; 222 | int GyroEnable; 223 | int InertiaCancelCnt; 224 | unsigned int RomSize; 225 | unsigned int MainWpTick; 226 | unsigned int SubWpTick; 227 | unsigned int JumpTick; 228 | unsigned int InputTick; 229 | unsigned int RollKeyTick; 230 | unsigned int RollTick; 231 | unsigned int RollOn; 232 | float XSensitivity; 233 | float YSensitivity; 234 | float YFollowing; 235 | pthread_t thKeyboard; 236 | pthread_t thMouse; 237 | pthread_t thOutputReport; 238 | pthread_t thInputReport; 239 | pthread_mutex_t MouseMtx; 240 | pthread_mutex_t UsbMtx; 241 | MouseData MouseMap; 242 | unsigned char *pRomBuf; 243 | unsigned char DirPrev; 244 | unsigned char DirPrevCnt; 245 | unsigned char RollDirPrev; 246 | unsigned char KeyMap[KEY_WIMAX]; 247 | unsigned char BakupProconData[11]; 248 | 249 | int ReadCheck(int Fd) 250 | { 251 | int ret; 252 | fd_set rfds; 253 | struct timeval tv; 254 | 255 | if (Processing == 0) 256 | { 257 | return -1; 258 | } 259 | 260 | tv.tv_sec = 1; 261 | tv.tv_usec = 000000; 262 | 263 | FD_ZERO(&rfds); 264 | FD_SET(Fd, &rfds); 265 | 266 | ret = select(Fd + 1, &rfds, NULL, NULL, &tv); 267 | if (ret < 0) 268 | { 269 | Processing = 0; 270 | } 271 | 272 | if (Processing == 0) 273 | { 274 | ret = -1; 275 | } 276 | 277 | //if read data incoming, returns 1. 278 | //0 is timeout. 279 | return ret; 280 | } 281 | 282 | void* KeybordThread(void *p) 283 | { 284 | int ret; 285 | struct input_event event; 286 | 287 | printf("KeybordThread start.\n"); 288 | 289 | while (Processing) 290 | { 291 | ret = ReadCheck(fKeyboard); 292 | if (ret <= 0) 293 | { 294 | continue; 295 | } 296 | 297 | ret = read(fKeyboard, &event, sizeof(event)); 298 | if (ret != sizeof(event)) 299 | { 300 | printf("Keybord read error %d.\n", errno); 301 | Processing = 0; 302 | continue; 303 | } 304 | 305 | if (event.type == EV_KEY) 306 | { 307 | //printf("code=0x%04x value=0x%08x.\n", event.code, event.value); 308 | //event.value is 0=Off, 1=On, 2=Repeat 309 | 310 | if (event.value == 2) 311 | { 312 | //do nothing 313 | continue; 314 | } 315 | 316 | //update keyboard data 317 | KeyMap[event.code] = event.value; 318 | 319 | if (KeyMap[KEY_Z]) 320 | { 321 | //super jump to base 322 | ReturnToBase = 1; 323 | } 324 | 325 | if (KeyMap[KEY_7]) 326 | { 327 | RapidFireWait = 4; 328 | } 329 | 330 | if (KeyMap[KEY_8]) 331 | { 332 | RapidFireWait = 1; 333 | } 334 | 335 | if (KeyMap[KEY_9]) 336 | { 337 | if (MWBtnToggle) 338 | { 339 | //main weapon button(Mouse L) is single shot mode 340 | MWBtnToggle = 0; 341 | RapidFireCnt = 0; 342 | } 343 | else 344 | { 345 | //main weapon button(Mouse L) is rapid fire mode 346 | MWBtnToggle = 1; 347 | RapidFireCnt = 0; 348 | } 349 | printf("MWBtnToggle=%d\n", MWBtnToggle); 350 | } 351 | 352 | Slow = KeyMap[KEY_LEFTSHIFT]; 353 | 354 | //debug 355 | //Adjust mouse sensitivity 356 | if (KeyMap[KEY_F5]) 357 | { 358 | XSensitivity += 0.1f; 359 | printf("X_SENSITIVITY=%f\n", XSensitivity); 360 | } 361 | 362 | if (KeyMap[KEY_F6]) 363 | { 364 | XSensitivity -= 0.1f; 365 | printf("X_SENSITIVITY=%f\n", XSensitivity); 366 | } 367 | 368 | if (KeyMap[KEY_F7]) 369 | { 370 | YSensitivity += 0.1f; 371 | printf("Y_SENSITIVITY=%f\n", YSensitivity); 372 | } 373 | 374 | if (KeyMap[KEY_F8]) 375 | { 376 | YSensitivity -= 0.1f; 377 | printf("Y_SENSITIVITY=%f\n", YSensitivity); 378 | } 379 | 380 | if (KeyMap[KEY_F9]) 381 | { 382 | YFollowing += 0.1f; 383 | printf("Y_FOLLOWING=%f\n", YFollowing); 384 | } 385 | 386 | if (KeyMap[KEY_F10]) 387 | { 388 | YFollowing -= 0.1f; 389 | printf("Y_FOLLOWING=%f\n", YFollowing); 390 | } 391 | } 392 | } 393 | 394 | printf("KeybordThread exit.\n"); 395 | return NULL; 396 | } 397 | 398 | void* MouseThread(void *p) 399 | { 400 | int ret; 401 | struct input_event event; 402 | 403 | printf("MouseThread start.\n"); 404 | 405 | while (Processing) 406 | { 407 | ret = ReadCheck(fMouse); 408 | if (ret <= 0) 409 | { 410 | continue; 411 | } 412 | 413 | ret = read(fMouse, &event, sizeof(event)); 414 | if (ret != sizeof(event)) 415 | { 416 | printf("Mouse read error %d.\n", errno); 417 | Processing = 0; 418 | continue; 419 | } 420 | 421 | if (event.type == EV_KEY) 422 | { 423 | //printf("code=0x%04x value=0x%08x.\n", event.code, event.value); 424 | //event.value is 0=Off, 1=On, 2=Repeat 425 | 426 | if (event.value == 2) 427 | { 428 | //do nothing 429 | continue; 430 | } 431 | 432 | pthread_mutex_lock(&MouseMtx); 433 | 434 | switch (event.code) 435 | { 436 | case BTN_LEFT: 437 | MouseMap.L = event.value; 438 | break; 439 | case BTN_RIGHT: 440 | MouseMap.R = event.value; 441 | break; 442 | case BTN_MIDDLE: 443 | MouseMap.Middle = event.value; 444 | break; 445 | case BTN_SIDE: 446 | MouseMap.Side = event.value; 447 | break; 448 | case BTN_EXTRA: 449 | MouseMap.Extra = event.value; 450 | break; 451 | default: 452 | break; 453 | } 454 | 455 | pthread_mutex_unlock(&MouseMtx); 456 | } 457 | else if (event.type == EV_REL) 458 | { 459 | //printf("code=0x%04x value=0x%08x.\n", event.code, event.value); 460 | 461 | pthread_mutex_lock(&MouseMtx); 462 | 463 | switch (event.code) 464 | { 465 | case REL_X: 466 | MouseMap.X += event.value; 467 | break; 468 | case REL_Y: 469 | MouseMap.Y += event.value; 470 | break; 471 | case REL_WHEEL: 472 | MouseMap.Wheel = event.value; 473 | break; 474 | default: 475 | break; 476 | } 477 | 478 | pthread_mutex_unlock(&MouseMtx); 479 | } 480 | } 481 | 482 | printf("MouseThread exit.\n"); 483 | return NULL; 484 | } 485 | 486 | unsigned short XValGet(unsigned char *pBuf) 487 | { 488 | unsigned short ret; 489 | 490 | ret = ((short)pBuf[1] & 0x0F) << 8; 491 | ret |= (short)pBuf[0]; 492 | 493 | return ret; 494 | } 495 | 496 | unsigned short YValGet(unsigned char *pBuf) 497 | { 498 | unsigned short ret; 499 | 500 | ret = (short)pBuf[1] >> 4; 501 | ret |= (short)pBuf[2] << 4; 502 | 503 | return ret; 504 | } 505 | 506 | void XValSet(unsigned char *pBuf, unsigned short X) 507 | { 508 | pBuf[0] = (unsigned char)(X & 0x00FF); 509 | pBuf[1] &= 0xF0; 510 | pBuf[1] |= (unsigned char)((X >> 8) & 0x000F); 511 | } 512 | 513 | void YValSet(unsigned char *pBuf, unsigned short Y) 514 | { 515 | pBuf[1] &= 0x0F; 516 | pBuf[1] |= (unsigned char)((Y << 4) & 0x00F0); 517 | pBuf[2] = (unsigned char)((Y >> 4) & 0x00FF); 518 | } 519 | 520 | void* OutputReportThread(void *p) 521 | { 522 | int ret; 523 | int len; 524 | int i; 525 | unsigned int spiAddr; 526 | unsigned char timStamp; 527 | unsigned char rd[MAX_PACKET_LEN]; 528 | unsigned char wt[MAX_PACKET_LEN]; 529 | 530 | printf("OutputReportThread start.\n"); 531 | 532 | timStamp = 0; 533 | while (Processing) 534 | { 535 | ret = ReadCheck(fGadget); 536 | if (ret <= 0) 537 | { 538 | continue; 539 | } 540 | 541 | ret = read(fGadget, rd, sizeof(rd)); 542 | if (ret == -1) 543 | { 544 | printf("Gadget OutputReport read error %d.\n", errno); 545 | Processing = 0; 546 | continue; 547 | } 548 | 549 | if (ret == 0) 550 | { 551 | continue; 552 | } 553 | 554 | memset(wt, 0, sizeof(wt)); 555 | len = 0; 556 | 557 | switch (rd[0]) 558 | { 559 | case 0x00: 560 | //do nothing 561 | break; 562 | case 0x01: 563 | //https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_subcommands_notes.md 564 | 565 | if (rd[10] == 0x01) 566 | { 567 | //Subcommand 0x01: Bluetooth manual pairing 568 | wt[0] = 0x21; 569 | wt[1] = timStamp++; 570 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 571 | wt[13] = 0x81; 572 | wt[14] = rd[10]; 573 | wt[15] = 0x03; //saves pairing info in Joy-Con 574 | len = MAX_PACKET_LEN; 575 | } 576 | else if (rd[10] == 0x02) 577 | { 578 | //Subcommand 0x02: Request device info 579 | wt[0] = 0x21; 580 | wt[1] = timStamp++; 581 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 582 | wt[13] = 0x82; 583 | wt[14] = rd[10]; 584 | wt[15] = 0x03; //firm ver 3.89 585 | wt[16] = 0x48; //firm ver 3.89 586 | wt[17] = 0x03; //Pro Controller 587 | wt[18] = 0x02; //always 0x02 588 | //Gyro data must be encrypted for firmware version 4.00 and above 589 | 590 | //MAC address in Big Endian 591 | memcpy(&wt[19], &pRomBuf[21], MAC_ADDRESS_LEN); 592 | wt[25] = 0x01; //always 0x01 593 | wt[26] = 0x01; //If 0x01, colors in SPI are used 594 | 595 | len = MAX_PACKET_LEN; 596 | } 597 | else if (rd[10] == 0x03) 598 | { 599 | //Subcommand 0x03: Set input report mode 600 | wt[0] = 0x21; 601 | wt[1] = timStamp++; 602 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 603 | wt[13] = 0x80; 604 | wt[14] = rd[10]; 605 | wt[15] = 0x00; 606 | len = MAX_PACKET_LEN; 607 | } 608 | else if (rd[10] == 0x04) 609 | { 610 | //Subcommand 0x04: Trigger buttons elapsed time 611 | wt[0] = 0x21; 612 | wt[1] = timStamp++; 613 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 614 | wt[13] = 0x83; 615 | wt[14] = rd[10]; 616 | len = MAX_PACKET_LEN; 617 | } 618 | else if (rd[10] == 0x06) 619 | { 620 | //Subcommand 0x06: Set HCI state (disconnect/page/pair/turn off) 621 | wt[0] = 0x21; 622 | wt[1] = timStamp++; 623 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 624 | wt[13] = 0x80; 625 | wt[14] = rd[10]; 626 | wt[15] = 0x00; 627 | len = MAX_PACKET_LEN; 628 | } 629 | else if (rd[10] == 0x08) 630 | { 631 | //Subcommand 0x08: Set shipment low power state 632 | wt[0] = 0x21; 633 | wt[1] = timStamp++; 634 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 635 | wt[13] = 0x80; 636 | wt[14] = rd[10]; 637 | wt[15] = 0x00; 638 | len = MAX_PACKET_LEN; 639 | } 640 | else if (rd[10] == 0x10) 641 | { 642 | //Subcommand 0x10: SPI flash read 643 | wt[0] = 0x21; 644 | wt[1] = timStamp++; 645 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 646 | wt[13] = 0x90; //subcommand reply 647 | wt[14] = rd[10]; //subcommand reply 648 | 649 | memcpy(&spiAddr, &rd[11], 4); 650 | memcpy(&wt[15], &spiAddr, sizeof(spiAddr)); //spi address 651 | 652 | wt[19] = rd[15]; //length 653 | memcpy(&wt[20], &pRomBuf[spiAddr], wt[19]); 654 | len = MAX_PACKET_LEN; 655 | } 656 | else if (rd[10] == 0x11) 657 | { 658 | //Subcommand 0x11: SPI flash Write 659 | wt[0] = 0x21; 660 | wt[1] = timStamp++; 661 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 662 | wt[13] = 0x80; //subcommand reply 663 | wt[14] = rd[10]; //subcommand reply 664 | wt[15] = 0x00; //success=0x00, write protect=0x01 665 | len = MAX_PACKET_LEN; 666 | 667 | //write data 668 | memcpy(&spiAddr, &rd[11], 4); 669 | memcpy(&pRomBuf[spiAddr], &rd[16], rd[15]); 670 | } 671 | else if (rd[10] == 0x12) 672 | { 673 | //Subcommand 0x12: SPI sector erase 674 | wt[0] = 0x21; 675 | wt[1] = timStamp++; 676 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 677 | wt[13] = 0x80; //subcommand reply 678 | wt[14] = rd[10]; //subcommand reply 679 | wt[15] = 0x00; //success=0x00, write protect=0x01 680 | len = MAX_PACKET_LEN; 681 | 682 | //erase data 683 | memcpy(&spiAddr, &rd[11], 4); 684 | memset(&pRomBuf[spiAddr], 0xFF, rd[15]); 685 | } 686 | else if (rd[10] == 0x21) 687 | { 688 | //Subcommand 0x21: Set NFC/IR MCU configuration 689 | wt[0] = 0x21; 690 | wt[1] = timStamp++; 691 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 692 | wt[13] = 0x80; 693 | wt[14] = rd[10]; 694 | wt[15] = rd[11]; 695 | len = MAX_PACKET_LEN; 696 | } 697 | else if (rd[10] == 0x22) 698 | { 699 | //Subcommand 0x22: Set NFC/IR MCU state 700 | wt[0] = 0x21; 701 | wt[1] = timStamp++; 702 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 703 | wt[13] = 0x80; 704 | wt[14] = rd[10]; 705 | wt[15] = 0x00; //suspend=0x00, resume=0x01, resume for update=0x02 706 | len = MAX_PACKET_LEN; 707 | } 708 | else if (rd[10] == 0x30) 709 | { 710 | //Subcommand 0x30: Set player lights 711 | wt[0] = 0x21; 712 | wt[1] = timStamp++; 713 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 714 | wt[13] = 0x80; 715 | wt[14] = rd[10]; 716 | wt[15] = 0x00; 717 | len = MAX_PACKET_LEN; 718 | } 719 | else if (rd[10] == 0x33) 720 | { 721 | //https://greggman.github.io/html5-gamepad-test/ 722 | wt[0] = 0x21; 723 | wt[1] = timStamp++; 724 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 725 | wt[13] = 0x80; 726 | wt[14] = rd[10]; 727 | wt[15] = 0x03; 728 | len = MAX_PACKET_LEN; 729 | } 730 | else if (rd[10] == 0x40) 731 | { 732 | //Subcommand 0x40: Enable IMU (6-Axis sensor) 733 | wt[0] = 0x21; 734 | wt[1] = timStamp++; 735 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 736 | wt[13] = 0x80; 737 | wt[14] = rd[10]; 738 | wt[15] = 0x00; 739 | len = MAX_PACKET_LEN; 740 | 741 | GyroEnable = 1; 742 | } 743 | else if (rd[10] == 0x41) 744 | { 745 | //https://greggman.github.io/html5-gamepad-test/ 746 | wt[0] = 0x21; 747 | wt[1] = timStamp++; 748 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 749 | wt[13] = 0x80; 750 | wt[14] = rd[10]; 751 | wt[15] = 0x00; 752 | len = MAX_PACKET_LEN; 753 | } 754 | else if (rd[10] == 0x48) 755 | { 756 | //Subcommand 0x48: Enable vibration 757 | wt[0] = 0x21; 758 | wt[1] = timStamp++; 759 | memcpy(&wt[2], BakupProconData, sizeof(BakupProconData)); 760 | wt[13] = 0x80; 761 | wt[14] = rd[10]; 762 | wt[15] = 0x00; 763 | len = MAX_PACKET_LEN; 764 | } 765 | else 766 | { 767 | //Add commands if needed 768 | printf("Output Report=[0]:0x%02x [10]:0x%02x\n", rd[0], rd[10]); 769 | } 770 | break; 771 | case 0x10: 772 | //do noting 773 | break; 774 | case 0x80: 775 | //https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/USB-HID-Notes.md 776 | 777 | if (rd[1] == 0x01) 778 | { 779 | //get mac address 780 | wt[0] = 0x81; 781 | wt[1] = rd[1]; 782 | wt[2] = 0x00; 783 | wt[3] = 0x03; 784 | 785 | //FlashRom store MacAddress in reverse order. 786 | for (i = 0; i < MAC_ADDRESS_LEN; i++) 787 | { 788 | wt[4 + i] = pRomBuf[26 - i]; 789 | } 790 | 791 | len = MAX_PACKET_LEN; 792 | } 793 | else if (rd[1] == 0x02) 794 | { 795 | //hand shake 796 | wt[0] = 0x81; 797 | wt[1] = rd[1]; 798 | len = MAX_PACKET_LEN; 799 | } 800 | else if (rd[1] == 0x03) 801 | { 802 | //baudrate to 3Mbit 803 | wt[0] = 0x81; 804 | wt[1] = rd[1]; 805 | len = MAX_PACKET_LEN; 806 | } 807 | else if (rd[1] == 0x04) 808 | { 809 | //hid mode 810 | HidMode = 1; 811 | //no response 812 | } 813 | else if (rd[1] == 0x05) 814 | { 815 | //bt mode 816 | HidMode = 0; 817 | //no response 818 | } 819 | else 820 | { 821 | //Add commands if needed 822 | printf("Output Report=[0]:0x%02x [1]:0x%02x\n", rd[0], rd[1]); 823 | } 824 | break; 825 | default: 826 | //Add commands if needed 827 | printf("Output Report=[0]:0x%02x\n", rd[0]); 828 | break; 829 | } 830 | 831 | if (len) 832 | { 833 | pthread_mutex_lock(&UsbMtx); 834 | ret = write(fGadget, &wt, len); 835 | pthread_mutex_unlock(&UsbMtx); 836 | 837 | if (ret == -1) 838 | { 839 | printf("Gadget OutputReport write error %d.\n", errno); 840 | Processing = 0; 841 | continue; 842 | } 843 | } 844 | } 845 | 846 | printf("OutputReportThread exit.\n"); 847 | return NULL; 848 | } 849 | 850 | void StickInputL(unsigned char *pAxis, unsigned char Dir) 851 | { 852 | int stopping = 0; 853 | 854 | if ((Dir == 0) && (MouseMap.Side == 1)) 855 | { 856 | //Player is not pressing the L stick and Squid condition 857 | if (DirPrevCnt <= MOVE_STOP_TIME) 858 | { 859 | //停止まで方向入力を維持 860 | DirPrevCnt++; 861 | Dir = DirPrev; 862 | stopping = 1; 863 | } 864 | else 865 | { 866 | //動きを止める 867 | DirPrev = 0; 868 | RollKeyTick = 0; 869 | } 870 | } 871 | else if ((Dir) && (MouseMap.Side == 1)) 872 | { 873 | //イカダッシュ中 874 | DirPrevCnt = 0; 875 | RollKeyTick++; 876 | } 877 | 878 | if (Slow) 879 | { 880 | //ゆっくりイカ移動 881 | DirPrevCnt = MOVE_STOP_TIME + 1; 882 | RollKeyTick = 0; 883 | } 884 | 885 | if (Dir == STICK_DIR_UP) 886 | { 887 | if (Slow) 888 | { 889 | XValSet(pAxis, AXIS_CENTER); 890 | YValSet(pAxis, AXIS_CENTER + StraightHalf); 891 | } 892 | else 893 | { 894 | if (stopping) 895 | { 896 | XValSet(pAxis, AXIS_CENTER); 897 | YValSet(pAxis, AXIS_CENTER + (StraightHalf / 2)); 898 | } 899 | else 900 | { 901 | XValSet(pAxis, AXIS_CENTER); 902 | YValSet(pAxis, AXIS_CENTER + Straight); 903 | } 904 | } 905 | } 906 | else if (Dir == STICK_DIR_RIGHT_UP) 907 | { 908 | if (Slow) 909 | { 910 | XValSet(pAxis, AXIS_CENTER + DiagonalHalf); 911 | YValSet(pAxis, AXIS_CENTER + DiagonalHalf); 912 | } 913 | else 914 | { 915 | if (stopping) 916 | { 917 | XValSet(pAxis, AXIS_CENTER + (DiagonalHalf / 2)); 918 | YValSet(pAxis, AXIS_CENTER + (DiagonalHalf / 2)); 919 | } 920 | else 921 | { 922 | XValSet(pAxis, AXIS_CENTER + Diagonal); 923 | YValSet(pAxis, AXIS_CENTER + Diagonal); 924 | } 925 | } 926 | } 927 | else if (Dir == STICK_DIR_RIGHT) 928 | { 929 | if (Slow) 930 | { 931 | XValSet(pAxis, AXIS_CENTER + StraightHalf); 932 | YValSet(pAxis, AXIS_CENTER); 933 | } 934 | else 935 | { 936 | if (stopping) 937 | { 938 | XValSet(pAxis, AXIS_CENTER + (StraightHalf / 2)); 939 | YValSet(pAxis, AXIS_CENTER); 940 | } 941 | else 942 | { 943 | XValSet(pAxis, AXIS_CENTER + Straight); 944 | YValSet(pAxis, AXIS_CENTER); 945 | } 946 | } 947 | 948 | } 949 | else if (Dir == STICK_DIR_RIGHT_DOWN) 950 | { 951 | if (Slow) 952 | { 953 | XValSet(pAxis, AXIS_CENTER + DiagonalHalf); 954 | YValSet(pAxis, AXIS_CENTER - DiagonalHalf); 955 | } 956 | else 957 | { 958 | if (stopping) 959 | { 960 | XValSet(pAxis, AXIS_CENTER + (DiagonalHalf / 2)); 961 | YValSet(pAxis, AXIS_CENTER - (DiagonalHalf / 2)); 962 | } 963 | else 964 | { 965 | XValSet(pAxis, AXIS_CENTER + Diagonal); 966 | YValSet(pAxis, AXIS_CENTER - Diagonal); 967 | } 968 | } 969 | } 970 | else if (Dir == STICK_DIR_DOWN) 971 | { 972 | if (Slow) 973 | { 974 | XValSet(pAxis, AXIS_CENTER); 975 | YValSet(pAxis, AXIS_CENTER - StraightHalf); 976 | } 977 | else 978 | { 979 | if (stopping) 980 | { 981 | XValSet(pAxis, AXIS_CENTER); 982 | YValSet(pAxis, AXIS_CENTER - (StraightHalf / 2)); 983 | } 984 | else 985 | { 986 | XValSet(pAxis, AXIS_CENTER); 987 | YValSet(pAxis, AXIS_CENTER - Straight); 988 | } 989 | } 990 | } 991 | else if (Dir == STICK_DIR_LEFT_DOWN) 992 | { 993 | if (Slow) 994 | { 995 | XValSet(pAxis, AXIS_CENTER - DiagonalHalf); 996 | YValSet(pAxis, AXIS_CENTER - DiagonalHalf); 997 | } 998 | else 999 | { 1000 | if (stopping) 1001 | { 1002 | XValSet(pAxis, AXIS_CENTER - (DiagonalHalf / 2)); 1003 | YValSet(pAxis, AXIS_CENTER - (DiagonalHalf / 2)); 1004 | } 1005 | else 1006 | { 1007 | XValSet(pAxis, AXIS_CENTER - Diagonal); 1008 | YValSet(pAxis, AXIS_CENTER - Diagonal); 1009 | } 1010 | } 1011 | } 1012 | else if (Dir == STICK_DIR_LEFT) 1013 | { 1014 | if (Slow) 1015 | { 1016 | XValSet(pAxis, AXIS_CENTER - StraightHalf); 1017 | YValSet(pAxis, AXIS_CENTER); 1018 | } 1019 | else 1020 | { 1021 | if (stopping) 1022 | { 1023 | XValSet(pAxis, AXIS_CENTER - (StraightHalf / 2)); 1024 | YValSet(pAxis, AXIS_CENTER); 1025 | } 1026 | else 1027 | { 1028 | XValSet(pAxis, AXIS_CENTER - Straight); 1029 | YValSet(pAxis, AXIS_CENTER); 1030 | } 1031 | } 1032 | } 1033 | else if (Dir == STICK_DIR_LEFT_UP) 1034 | { 1035 | if (Slow) 1036 | { 1037 | XValSet(pAxis, AXIS_CENTER - DiagonalHalf); 1038 | YValSet(pAxis, AXIS_CENTER + DiagonalHalf); 1039 | } 1040 | else 1041 | { 1042 | if (stopping) 1043 | { 1044 | XValSet(pAxis, AXIS_CENTER - (DiagonalHalf / 2)); 1045 | YValSet(pAxis, AXIS_CENTER + (DiagonalHalf / 2)); 1046 | } 1047 | else 1048 | { 1049 | XValSet(pAxis, AXIS_CENTER - Diagonal); 1050 | YValSet(pAxis, AXIS_CENTER + Diagonal); 1051 | } 1052 | } 1053 | } 1054 | else 1055 | { 1056 | //no input 1057 | XValSet(pAxis, AXIS_CENTER); 1058 | YValSet(pAxis, AXIS_CENTER); 1059 | } 1060 | 1061 | #ifdef SQUID_ROLL_ENABLE 1062 | if (MouseMap.Side == 0) 1063 | { 1064 | RollKeyTick = 0; 1065 | } 1066 | 1067 | if (KeyMap[KEY_SPACE] == 1) 1068 | { 1069 | RollKeyTick = 0; 1070 | } 1071 | 1072 | if (MouseMap.L == 1) 1073 | { 1074 | RollKeyTick = 0; 1075 | } 1076 | 1077 | if (MouseMap.R == 1) 1078 | { 1079 | RollKeyTick = 0; 1080 | } 1081 | 1082 | if (MouseMap.Extra == 1) 1083 | { 1084 | RollKeyTick = 0; 1085 | } 1086 | 1087 | if (RollKeyTick >= ROLL_INPUT_TIME) 1088 | { 1089 | //printf("rool tick=%d\n", RollKeyTick); 1090 | 1091 | //イカロール方向を指定する 1092 | 1093 | if (RollDirPrev == STICK_DIR_UP) 1094 | { 1095 | //前 1096 | if ((Dir == STICK_DIR_DOWN) || (Dir == STICK_DIR_LEFT_DOWN) || (Dir == STICK_DIR_RIGHT_DOWN)) 1097 | { 1098 | //下、右下、左下、イカロール 1099 | RollOn = 1; 1100 | RollTick = 0; 1101 | } 1102 | } 1103 | 1104 | if (RollDirPrev == STICK_DIR_RIGHT_UP) 1105 | { 1106 | //右上 1107 | if ((Dir == STICK_DIR_LEFT) || (Dir == STICK_DIR_DOWN) || (Dir == STICK_DIR_LEFT_DOWN)) 1108 | { 1109 | //左、下、左下、イカロール 1110 | RollOn = 1; 1111 | RollTick = 0; 1112 | } 1113 | } 1114 | 1115 | if (RollDirPrev == STICK_DIR_RIGHT) 1116 | { 1117 | //右 1118 | if ((Dir == STICK_DIR_LEFT) || (Dir == STICK_DIR_LEFT_DOWN) || (Dir == STICK_DIR_LEFT_UP)) 1119 | { 1120 | //左、左下、左上、イカロール 1121 | RollOn = 1; 1122 | RollTick = 0; 1123 | } 1124 | } 1125 | 1126 | if (RollDirPrev == STICK_DIR_RIGHT_DOWN) 1127 | { 1128 | //右下 1129 | if ((Dir == STICK_DIR_LEFT) || (Dir == STICK_DIR_UP) || (Dir == STICK_DIR_LEFT_UP)) 1130 | { 1131 | //左、上、右上、イカロール 1132 | RollOn = 1; 1133 | RollTick = 0; 1134 | } 1135 | } 1136 | 1137 | if (RollDirPrev == STICK_DIR_DOWN) 1138 | { 1139 | //下 1140 | if ((Dir == STICK_DIR_UP) || (Dir == STICK_DIR_LEFT_UP) || (Dir == STICK_DIR_RIGHT_UP)) 1141 | { 1142 | //上、左上、右上、イカロール 1143 | RollOn = 1; 1144 | RollTick = 0; 1145 | } 1146 | } 1147 | 1148 | if (RollDirPrev == STICK_DIR_LEFT_DOWN) 1149 | { 1150 | //左下 1151 | if ((Dir == STICK_DIR_RIGHT) || (Dir == STICK_DIR_UP) || (Dir == STICK_DIR_RIGHT_UP)) 1152 | { 1153 | //右、上、右上、イカロール 1154 | RollOn = 1; 1155 | RollTick = 0; 1156 | } 1157 | } 1158 | 1159 | if (RollDirPrev == STICK_DIR_LEFT) 1160 | { 1161 | //左 1162 | if ((Dir == STICK_DIR_RIGHT) || (Dir == STICK_DIR_RIGHT_DOWN) || (Dir == STICK_DIR_RIGHT_UP)) 1163 | { 1164 | //右、右下、右上、イカロール 1165 | RollOn = 1; 1166 | RollTick = 0; 1167 | } 1168 | } 1169 | 1170 | if (RollDirPrev == STICK_DIR_LEFT_UP) 1171 | { 1172 | //左上 1173 | if ((Dir == STICK_DIR_DOWN) || (Dir == STICK_DIR_RIGHT) || (Dir == STICK_DIR_RIGHT_DOWN)) 1174 | { 1175 | //下、右、右下、イカロール 1176 | RollOn = 1; 1177 | RollTick = 0; 1178 | } 1179 | } 1180 | } 1181 | #endif 1182 | 1183 | RollDirPrev = Dir; 1184 | DirPrev = Dir; 1185 | } 1186 | 1187 | void StickInputR(unsigned char *pAxis, unsigned char Dir) 1188 | { 1189 | if (Dir == STICK_DIR_UP) 1190 | { 1191 | if (Slow) 1192 | { 1193 | XValSet(pAxis, AXIS_CENTER); 1194 | YValSet(pAxis, AXIS_CENTER + StraightHalf); 1195 | } 1196 | else 1197 | { 1198 | XValSet(pAxis, AXIS_CENTER); 1199 | YValSet(pAxis, AXIS_CENTER + Straight); 1200 | } 1201 | } 1202 | else if (Dir == STICK_DIR_RIGHT_UP) 1203 | { 1204 | if (Slow) 1205 | { 1206 | XValSet(pAxis, AXIS_CENTER + DiagonalHalf); 1207 | YValSet(pAxis, AXIS_CENTER + DiagonalHalf); 1208 | } 1209 | else 1210 | { 1211 | XValSet(pAxis, AXIS_CENTER + Diagonal); 1212 | YValSet(pAxis, AXIS_CENTER + Diagonal); 1213 | } 1214 | } 1215 | else if (Dir == STICK_DIR_RIGHT) 1216 | { 1217 | if (Slow) 1218 | { 1219 | XValSet(pAxis, AXIS_CENTER + StraightHalf); 1220 | YValSet(pAxis, AXIS_CENTER); 1221 | } 1222 | else 1223 | { 1224 | XValSet(pAxis, AXIS_CENTER + Straight); 1225 | YValSet(pAxis, AXIS_CENTER); 1226 | } 1227 | 1228 | } 1229 | else if (Dir == STICK_DIR_RIGHT_DOWN) 1230 | { 1231 | if (Slow) 1232 | { 1233 | XValSet(pAxis, AXIS_CENTER + DiagonalHalf); 1234 | YValSet(pAxis, AXIS_CENTER - DiagonalHalf); 1235 | } 1236 | else 1237 | { 1238 | XValSet(pAxis, AXIS_CENTER + Diagonal); 1239 | YValSet(pAxis, AXIS_CENTER - Diagonal); 1240 | } 1241 | } 1242 | else if (Dir == STICK_DIR_DOWN) 1243 | { 1244 | if (Slow) 1245 | { 1246 | XValSet(pAxis, AXIS_CENTER); 1247 | YValSet(pAxis, AXIS_CENTER - StraightHalf); 1248 | } 1249 | else 1250 | { 1251 | XValSet(pAxis, AXIS_CENTER); 1252 | YValSet(pAxis, AXIS_CENTER - Straight); 1253 | } 1254 | } 1255 | else if (Dir == STICK_DIR_LEFT_DOWN) 1256 | { 1257 | if (Slow) 1258 | { 1259 | XValSet(pAxis, AXIS_CENTER - DiagonalHalf); 1260 | YValSet(pAxis, AXIS_CENTER - DiagonalHalf); 1261 | } 1262 | else 1263 | { 1264 | XValSet(pAxis, AXIS_CENTER - Diagonal); 1265 | YValSet(pAxis, AXIS_CENTER - Diagonal); 1266 | } 1267 | } 1268 | else if (Dir == STICK_DIR_LEFT) 1269 | { 1270 | if (Slow) 1271 | { 1272 | XValSet(pAxis, AXIS_CENTER - StraightHalf); 1273 | YValSet(pAxis, AXIS_CENTER); 1274 | } 1275 | else 1276 | { 1277 | XValSet(pAxis, AXIS_CENTER - Straight); 1278 | YValSet(pAxis, AXIS_CENTER); 1279 | } 1280 | } 1281 | else if (Dir == STICK_DIR_LEFT_UP) 1282 | { 1283 | if (Slow) 1284 | { 1285 | XValSet(pAxis, AXIS_CENTER - DiagonalHalf); 1286 | YValSet(pAxis, AXIS_CENTER + DiagonalHalf); 1287 | } 1288 | else 1289 | { 1290 | XValSet(pAxis, AXIS_CENTER - Diagonal); 1291 | YValSet(pAxis, AXIS_CENTER + Diagonal); 1292 | } 1293 | } 1294 | else 1295 | { 1296 | //no input 1297 | XValSet(pAxis, AXIS_CENTER); 1298 | YValSet(pAxis, AXIS_CENTER); 1299 | } 1300 | } 1301 | 1302 | void GyroEmurate(ProconData *pPad) 1303 | { 1304 | ProconGyroData gyro; 1305 | 1306 | memset(&gyro, 0, sizeof(gyro)); 1307 | 1308 | //Z Angle do not change. 1309 | gyro.Z_Angle = 4096; 1310 | 1311 | //Add Y Angle 1312 | YTotal += (int32_t)((float)MouseMap.Y * YFollowing * -1); 1313 | 1314 | if (YTotal > Y_ANGLE_UPPPER_LIMIT) 1315 | { 1316 | //upper limit 1317 | YTotal = Y_ANGLE_UPPPER_LIMIT; 1318 | } 1319 | 1320 | if (YTotal < Y_ANGLE_LOWER_LIMIT) 1321 | { 1322 | //lower limit 1323 | YTotal = Y_ANGLE_LOWER_LIMIT; 1324 | } 1325 | 1326 | gyro.Y_Angle = YTotal; 1327 | //printf("YTotal=%d\n", YTotal); 1328 | 1329 | //Up,down 1330 | if ((gyro.Y_Angle != Y_ANGLE_UPPPER_LIMIT) && (gyro.Y_Angle != Y_ANGLE_LOWER_LIMIT)) 1331 | { 1332 | gyro.Y_Accel = (short)((float)MouseMap.Y * YSensitivity); 1333 | } 1334 | 1335 | gyro.Z_Accel = (short)((float)MouseMap.X * XSensitivity); 1336 | gyro.Z_Accel *= -1; 1337 | 1338 | if (gyro.Z_Accel < Y_ACCEL_LOWER_LIMIT) 1339 | { 1340 | //lower limit 1341 | gyro.Z_Accel = -Y_ACCEL_LOWER_LIMIT; 1342 | } 1343 | 1344 | if (gyro.Z_Accel > Y_ACCEL_UPPPER_LIMIT) 1345 | { 1346 | //upper limmit 1347 | gyro.Z_Accel = Y_ACCEL_UPPPER_LIMIT; 1348 | } 1349 | 1350 | //ジャイロデータは3サンプル分(1サンプル5ms)のデータを格納する 1351 | //コンバータでは同じデータを3つ格納する 1352 | 1353 | memcpy(&pPad->GyroData[0], &gyro, sizeof(gyro)); 1354 | memcpy(&pPad->GyroData[12], &gyro, sizeof(gyro)); 1355 | memcpy(&pPad->GyroData[24], &gyro, sizeof(gyro)); 1356 | 1357 | //マウスは変化があった場合にデータが来る、よってXYに値が残っている 1358 | MouseMap.X = 0; 1359 | MouseMap.Y = 0; 1360 | } 1361 | 1362 | //super jump 1363 | void ReturnToBaseMacro(ProconData *pPad) 1364 | { 1365 | if (ReturnToBase) 1366 | { 1367 | pPad->R = 0; 1368 | pPad->L = 0; 1369 | pPad->ZL = 0; 1370 | pPad->ZR = 0; 1371 | 1372 | pPad->A = 0; 1373 | pPad->B = 0; 1374 | pPad->X = 0; 1375 | pPad->Y = 0; 1376 | 1377 | pPad->Up = 0; 1378 | pPad->Down = 0; 1379 | pPad->Left = 0; 1380 | pPad->Right = 0; 1381 | 1382 | switch (ReturnToBaseCnt) 1383 | { 1384 | case 0: 1385 | case 1: 1386 | case 2: 1387 | pPad->X = 1; 1388 | ReturnToBaseCnt++; 1389 | break; 1390 | case 3: 1391 | case 4: 1392 | case 5: 1393 | pPad->X = 1; 1394 | pPad->Down = 1; 1395 | ReturnToBaseCnt++; 1396 | break; 1397 | case 6: 1398 | case 7: 1399 | case 8: 1400 | pPad->A = 1; 1401 | pPad->X = 1; 1402 | pPad->Down = 1; 1403 | ReturnToBaseCnt++; 1404 | break; 1405 | case 9: 1406 | case 10: 1407 | case 11: 1408 | pPad->A = 1; 1409 | pPad->X = 1; 1410 | ReturnToBaseCnt++; 1411 | break; 1412 | default: 1413 | ReturnToBaseCnt = 0; 1414 | ReturnToBase = 0; 1415 | break; 1416 | } 1417 | } 1418 | } 1419 | 1420 | #ifdef INERTIA_CANCEL_ENABLE 1421 | void InertiaCancel(ProconData *pPad) 1422 | { 1423 | switch (InertiaCancelCnt) 1424 | { 1425 | case 0: 1426 | case 1: 1427 | case 2: 1428 | pPad->R = 1; 1429 | InertiaCancelCnt++; 1430 | break; 1431 | case 3: 1432 | case 4: 1433 | case 5: 1434 | case 6: 1435 | case 7: 1436 | case 8: 1437 | pPad->R = 1; 1438 | pPad->ZL = 1; 1439 | InertiaCancelCnt++; 1440 | break; 1441 | default: 1442 | pPad->ZL = 1; 1443 | } 1444 | } 1445 | #endif 1446 | 1447 | void DoSquidRoll(ProconData *pPad) 1448 | { 1449 | if (RollOn == 0) 1450 | { 1451 | return; 1452 | } 1453 | 1454 | if (RollTick < ROLL_JUMP_TIME) 1455 | { 1456 | //イカロールのジャンプ入力 1457 | pPad->B = 1; 1458 | RollTick++; 1459 | } 1460 | else 1461 | { 1462 | RollOn = 0; 1463 | RollTick = 0; 1464 | RollKeyTick = 0; 1465 | } 1466 | } 1467 | 1468 | unsigned char DoRapidFire(void) 1469 | { 1470 | RapidFireCnt ++; 1471 | 1472 | if(RapidFireCnt <= RapidFireWait) 1473 | { 1474 | return 1; 1475 | } 1476 | else 1477 | { 1478 | if (RapidFireCnt >= (RapidFireWait << 1)) 1479 | { 1480 | RapidFireCnt = 0; 1481 | } 1482 | } 1483 | return 0; 1484 | } 1485 | 1486 | void ProconInput(ProconData *pPad) 1487 | { 1488 | unsigned char dir; 1489 | 1490 | //通常利用の範囲では桁あふれしない 1491 | InputTick++; 1492 | 1493 | //常にON 1494 | pPad->Grip = 1; 1495 | 1496 | //key 1497 | if (KeyMap[KEY_1]) 1498 | { 1499 | //視点リセット 1500 | pPad->Y = 1; 1501 | YTotal = 0; 1502 | } 1503 | 1504 | if (KeyMap[KEY_2] == 1) 1505 | { 1506 | //キャプチャー 1507 | pPad->Capture = 1; 1508 | } 1509 | 1510 | if (KeyMap[KEY_3] == 1) 1511 | { 1512 | //マイナス 1513 | pPad->Minus = 1; 1514 | } 1515 | 1516 | if (KeyMap[KEY_4] == 1) 1517 | { 1518 | //プラス 1519 | pPad->Plus = 1; 1520 | } 1521 | 1522 | if (KeyMap[KEY_ESC] == 1) 1523 | { 1524 | //ホーム 1525 | pPad->Home = 1; 1526 | } 1527 | 1528 | if (KeyMap[KEY_E] == 1) 1529 | { 1530 | //スーパージャンプ決定 1531 | //アサリ 1532 | pPad->A = 1; 1533 | } 1534 | 1535 | if (KeyMap[KEY_R] == 1) 1536 | { 1537 | //マップ 1538 | pPad->X = 1; 1539 | } 1540 | 1541 | if (KeyMap[KEY_F] == 1) 1542 | { 1543 | //カモン 1544 | pPad->Up = 1; 1545 | } 1546 | 1547 | if (KeyMap[KEY_C] == 1) 1548 | { 1549 | //ナイス 1550 | pPad->Down = 1; 1551 | } 1552 | 1553 | if (KeyMap[KEY_SPACE] == 1) 1554 | { 1555 | //ジャンプ 1556 | pPad->B = 1; 1557 | JumpTick = InputTick; 1558 | } 1559 | 1560 | if (KeyMap[KEY_T] == 1) 1561 | { 1562 | //L 1563 | pPad->L = 1; 1564 | } 1565 | 1566 | if (KeyMap[KEY_Y] == 1) 1567 | { 1568 | //R 1569 | pPad->R = 1; 1570 | } 1571 | 1572 | if (KeyMap[KEY_G] == 1) 1573 | { 1574 | //ZL 1575 | pPad->ZL = 1; 1576 | } 1577 | 1578 | if (KeyMap[KEY_H] == 1) 1579 | { 1580 | //ZR 1581 | pPad->ZR = 1; 1582 | } 1583 | 1584 | if (KeyMap[KEY_U] == 1) 1585 | { 1586 | //L Stick 1587 | pPad->StickL = 1; 1588 | } 1589 | 1590 | if (KeyMap[KEY_I] == 1) 1591 | { 1592 | //R Stick 1593 | pPad->StickR = 1; 1594 | } 1595 | 1596 | if (KeyMap[KEY_L] == 1) 1597 | { 1598 | //tesla menu 1599 | pPad->L = 1; 1600 | pPad->Down = 1; 1601 | pPad->StickR = 1; 1602 | } 1603 | 1604 | if (KeyMap[KEY_KP8] == 1) 1605 | { 1606 | pPad->Up = 1; 1607 | } 1608 | 1609 | if (KeyMap[KEY_KP2] == 1) 1610 | { 1611 | pPad->Down = 1; 1612 | } 1613 | 1614 | if ((KeyMap[KEY_KP4] == 1) || (KeyMap[KEY_COMMA] == 1)) 1615 | { 1616 | pPad->Left = 1; 1617 | } 1618 | 1619 | if ((KeyMap[KEY_KP6] == 1) || (KeyMap[KEY_DOT] == 1)) 1620 | { 1621 | pPad->Right = 1; 1622 | } 1623 | 1624 | //printf("StickL X=%d, Y=%d\n", XValGet(pPad->L_Axis), YValGet(pPad->L_Axis)); 1625 | //printf("StickR X=%d, Y=%d\n", XValGet(pPad->R_Axis), YValGet(pPad->R_Axis)); 1626 | 1627 | //L Stick assert WASD 1628 | dir = KeyMap[KEY_W] << 3; 1629 | dir |= KeyMap[KEY_D] << 2; 1630 | dir |= KeyMap[KEY_S] << 1; 1631 | dir |= KeyMap[KEY_A]; 1632 | 1633 | StickInputL(pPad->L_Axis, dir); 1634 | #ifdef SQUID_ROLL_ENABLE 1635 | DoSquidRoll(pPad); 1636 | #endif 1637 | 1638 | //R Stick assert arrow UP,DOWN,LEFT,RIGHT 1639 | dir = KeyMap[KEY_UP] << 3; 1640 | dir |= KeyMap[KEY_RIGHT] << 2; 1641 | dir |= KeyMap[KEY_DOWN] << 1; 1642 | dir |= KeyMap[KEY_LEFT]; 1643 | 1644 | StickInputR(pPad->R_Axis, dir); 1645 | 1646 | //printf("StickL X=%d, Y=%d\n", XValGet(pPad->L_Axis), YValGet(pPad->L_Axis)); 1647 | //printf("StickR X=%d, Y=%d\n", XValGet(pPad->R_Axis), YValGet(pPad->R_Axis)); 1648 | 1649 | pthread_mutex_lock(&MouseMtx); 1650 | 1651 | //mouse 1652 | if (GyroEnable) 1653 | { 1654 | GyroEmurate(pPad); 1655 | } 1656 | 1657 | if (MouseMap.R) 1658 | { 1659 | //サブ 1660 | pPad->R = 1; 1661 | SubWpTick = InputTick; 1662 | } 1663 | 1664 | if (MouseMap.L) 1665 | { 1666 | 1667 | if (MWBtnToggle == 0) 1668 | { 1669 | //メイン単発 1670 | pPad->ZR = 1; 1671 | } 1672 | else 1673 | { 1674 | //メイン連射 1675 | pPad->ZR = DoRapidFire(); 1676 | } 1677 | 1678 | MainWpTick = InputTick; 1679 | } 1680 | 1681 | #ifdef INERTIA_CANCEL_ENABLE 1682 | if (MouseMap.Side) 1683 | { 1684 | if (((JumpTick + DELEY_FOR_AFTER_JUMP) < InputTick) && 1685 | ((MainWpTick + DELEY_FOR_AFTER_MAIN_WP) < InputTick) && 1686 | ((SubWpTick + DELEY_FOR_AFTER_SUB_WP) < InputTick)) 1687 | { 1688 | RollOn = 0; 1689 | InertiaCancel(pPad); 1690 | } 1691 | else 1692 | { 1693 | //ジャンプ、メイン、サブ実施後は慣性キャンセル無しでイカになる 1694 | pPad->ZL = 1; 1695 | InertiaCancelCnt = 0xFF; 1696 | } 1697 | } 1698 | else 1699 | { 1700 | InertiaCancelCnt = 0; 1701 | } 1702 | #else 1703 | if (MouseMap.Side) 1704 | { 1705 | pPad->ZL = 1; 1706 | } 1707 | #endif 1708 | 1709 | if (MouseMap.Extra) 1710 | { 1711 | if (MWBtnToggle == 0) 1712 | { 1713 | //メイン連射 1714 | pPad->ZR = DoRapidFire(); 1715 | } 1716 | else 1717 | { 1718 | //メイン単発 1719 | pPad->ZR = 1; 1720 | } 1721 | } 1722 | 1723 | if (MouseMap.Wheel) 1724 | { 1725 | //スペシャル、マウスホイールを動かす 1726 | pPad->StickR = 1; 1727 | MouseMap.Wheel = 0; 1728 | } 1729 | 1730 | if (MouseMap.Middle) 1731 | { 1732 | //スペシャル 1733 | pPad->StickR = 1; 1734 | } 1735 | 1736 | pthread_mutex_unlock(&MouseMtx); 1737 | 1738 | ReturnToBaseMacro(pPad); 1739 | } 1740 | 1741 | void* InputReportThread(void *p) 1742 | { 1743 | int ret; 1744 | unsigned char *ptr; 1745 | ProconData procon; 1746 | unsigned char timStamp; 1747 | struct timespec wait; 1748 | 1749 | printf("InputReportThread start.\n"); 1750 | 1751 | timStamp = 0; 1752 | wait.tv_sec = 0; 1753 | wait.tv_nsec = PAD_INPUT_WAIT * 1000000; 1754 | 1755 | while (Processing) 1756 | { 1757 | nanosleep(&wait, NULL); 1758 | 1759 | if (HidMode) 1760 | { 1761 | memset(&procon, 0, sizeof(procon)); 1762 | timStamp += (PAD_INPUT_WAIT / 8); 1763 | 1764 | procon.ReportId = 0x30; 1765 | procon.TimeStamp = timStamp; 1766 | procon.ConnectNo = 1; 1767 | procon.BatteryLevel = 9; 1768 | ProconInput(&procon); 1769 | 1770 | ptr = (unsigned char *)&procon; 1771 | memcpy(BakupProconData, &ptr[2], sizeof(BakupProconData)); 1772 | 1773 | pthread_mutex_lock(&UsbMtx); 1774 | ret = write(fGadget, &procon, sizeof(procon)); 1775 | pthread_mutex_unlock(&UsbMtx); 1776 | 1777 | if (ret == -1) 1778 | { 1779 | printf("fGadget InputReport write error %d.\n", errno); 1780 | Processing = 0; 1781 | continue; 1782 | } 1783 | } 1784 | } 1785 | 1786 | printf("InputReportThread exit.\n"); 1787 | return NULL; 1788 | } 1789 | 1790 | int InputDevNameGet(int DevType, char *pSearchName, char *pDevName) 1791 | { 1792 | int found; 1793 | DIR *dir; 1794 | struct dirent *dp; 1795 | char *p; 1796 | 1797 | dir = opendir("/dev/input/by-id"); 1798 | if (dir == NULL) 1799 | { 1800 | printf("opendir error %d", errno); 1801 | return -1; 1802 | } 1803 | 1804 | found = -1; 1805 | dp = readdir(dir); 1806 | while (dp != NULL) 1807 | { 1808 | if (DevType == DEV_KEYBOARD) 1809 | { 1810 | //Keyboard 1811 | p = strstr(dp->d_name, "event-kbd"); 1812 | if (p != NULL) 1813 | { 1814 | #ifdef ENUM_HID_DEVICE 1815 | printf("enum keyboard:%s\n", dp->d_name); 1816 | #endif 1817 | p = strstr(dp->d_name, pSearchName); 1818 | if (p != NULL) 1819 | { 1820 | sprintf(pDevName, "/dev/input/by-id/%s", dp->d_name); 1821 | printf("Keyboard:%s\n", pDevName); 1822 | found = 0; 1823 | break; 1824 | } 1825 | } 1826 | } 1827 | else 1828 | { 1829 | //Mouse 1830 | p = strstr(dp->d_name, "event-mouse"); 1831 | if (p != NULL) 1832 | { 1833 | #ifdef ENUM_HID_DEVICE 1834 | printf("enum mouse:%s\n", dp->d_name); 1835 | #endif 1836 | p = strstr(dp->d_name, pSearchName); 1837 | if (p != NULL) 1838 | { 1839 | sprintf(pDevName, "/dev/input/by-id/%s", dp->d_name); 1840 | printf("Mouse:%s\n", pDevName); 1841 | found = 0; 1842 | break; 1843 | } 1844 | } 1845 | } 1846 | 1847 | dp = readdir(dir); 1848 | } 1849 | 1850 | closedir(dir); 1851 | return found; 1852 | } 1853 | 1854 | void Echo(int enable) 1855 | { 1856 | struct termios term; 1857 | 1858 | tcgetattr(STDIN_FILENO, &term); 1859 | 1860 | if (enable) 1861 | { 1862 | term.c_lflag |= ECHO; 1863 | } 1864 | else 1865 | { 1866 | term.c_lflag &= ~ECHO; 1867 | } 1868 | tcsetattr(STDIN_FILENO, TCSANOW, &term); 1869 | } 1870 | 1871 | void SigIntHandler() 1872 | { 1873 | printf("SIGINT detect.\n"); 1874 | system(GADGET_DETACH); 1875 | Processing = 0; 1876 | } 1877 | 1878 | int main(int argc, char *argv[]) 1879 | { 1880 | int ret; 1881 | int fd; 1882 | struct stat fst; 1883 | char devName[MAX_NAME_LEN]; 1884 | 1885 | printf("Procon Converter start.\n"); 1886 | 1887 | XSensitivity = X_SENSITIVITY; 1888 | YSensitivity = Y_SENSITIVITY; 1889 | YFollowing = Y_FOLLOWING; 1890 | Processing = 1; 1891 | fKeyboard = -1; 1892 | fMouse = -1; 1893 | fGadget = -1; 1894 | YTotal = 0; 1895 | Straight = AXIS_MAX_INPUT; 1896 | StraightHalf = (int)((float)Straight * AXIS_HALF_INPUT_FACTOR); 1897 | Diagonal = (int)(0.7071f * (float)AXIS_MAX_INPUT); //0.7071 is cos 45 1898 | DiagonalHalf = (int)((float)Diagonal * AXIS_HALF_INPUT_FACTOR); 1899 | 1900 | HidMode = 0; 1901 | GyroEnable = 0; 1902 | memset(BakupProconData, 0, sizeof(BakupProconData)); 1903 | 1904 | RapidFireCnt = 0; 1905 | RapidFireWait = 1; 1906 | 1907 | Echo(0); 1908 | signal(SIGINT, SigIntHandler); 1909 | 1910 | pthread_mutex_init(&MouseMtx, NULL); 1911 | pthread_mutex_init(&UsbMtx, NULL); 1912 | 1913 | if (stat(ROM_FILE_NAME, &fst) < 0) 1914 | { 1915 | printf("%s not found.\n", ROM_FILE_NAME); 1916 | Processing = 0; 1917 | goto EXIT; 1918 | } 1919 | RomSize = fst.st_size; 1920 | 1921 | pRomBuf = malloc(RomSize); 1922 | if (pRomBuf == NULL) 1923 | { 1924 | printf("malloc error.\n"); 1925 | Processing = 0; 1926 | goto EXIT; 1927 | } 1928 | 1929 | fd = open(ROM_FILE_NAME, O_RDONLY); 1930 | if (fd < 0) 1931 | { 1932 | printf("open error.\n"); 1933 | Processing = 0; 1934 | goto EXIT; 1935 | } 1936 | 1937 | //https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/spi_flash_notes.md 1938 | if (read(fd, pRomBuf, (size_t)RomSize) <= 0) 1939 | { 1940 | printf("read error.\n"); 1941 | close(fd); 1942 | Processing = 0; 1943 | goto EXIT; 1944 | } 1945 | 1946 | close(fd); 1947 | 1948 | ret = InputDevNameGet(DEV_KEYBOARD, KEYBOARD_NAME, devName); 1949 | if (ret == -1) 1950 | { 1951 | printf("Keybord not found.\n"); 1952 | Processing = 0; 1953 | goto EXIT; 1954 | } 1955 | 1956 | fKeyboard = open(devName, O_RDONLY); 1957 | if (fKeyboard == -1) 1958 | { 1959 | printf("Keybord open error %d.\n", errno); 1960 | Processing = 0; 1961 | goto EXIT; 1962 | } 1963 | 1964 | ret = InputDevNameGet(DEV_MOUSE, MOUSE_NAME, devName); 1965 | if (ret == -1) 1966 | { 1967 | printf("Mouse not found.\n"); 1968 | Processing = 0; 1969 | goto EXIT; 1970 | } 1971 | 1972 | fMouse = open(devName, O_RDONLY); 1973 | if (fMouse == -1) 1974 | { 1975 | printf("Mouse open error %d.\n", errno); 1976 | Processing = 0; 1977 | goto EXIT; 1978 | } 1979 | 1980 | system(GADGET_DETACH); 1981 | sleep(2); 1982 | system(GADGET_ATTACH); 1983 | 1984 | fGadget = open(GADGET_NAME, O_RDWR); 1985 | if (fGadget == -1) 1986 | { 1987 | printf("Gadget open error %d.\n", errno); 1988 | Processing = 0; 1989 | goto EXIT; 1990 | } 1991 | 1992 | ret = pthread_create(&thInputReport, NULL, InputReportThread, NULL); 1993 | if (ret != 0) 1994 | { 1995 | printf("InputReport thread create error %d.\n", ret); 1996 | Processing = 0; 1997 | goto EXIT; 1998 | } 1999 | thInputReportCreated = 1; 2000 | 2001 | ret = pthread_create(&thOutputReport, NULL, OutputReportThread, NULL); 2002 | if (ret != 0) 2003 | { 2004 | printf("OutputReport thread create error %d.\n", ret); 2005 | Processing = 0; 2006 | goto EXIT; 2007 | } 2008 | thOutputReportCreated = 1; 2009 | 2010 | ret = pthread_create(&thKeyboard, NULL, KeybordThread, NULL); 2011 | if (ret != 0) 2012 | { 2013 | printf("Keybord thread create error %d.\n", ret); 2014 | Processing = 0; 2015 | goto EXIT; 2016 | } 2017 | thKeyboardCreated = 1; 2018 | 2019 | ret = pthread_create(&thMouse, NULL, MouseThread, NULL); 2020 | if (ret != 0) 2021 | { 2022 | printf("Mouse thread create error %d.\n", ret); 2023 | Processing = 0; 2024 | goto EXIT; 2025 | } 2026 | thMouseCreated = 1; 2027 | 2028 | EXIT: 2029 | 2030 | if (thKeyboardCreated) 2031 | { 2032 | pthread_join(thKeyboard, NULL); 2033 | } 2034 | 2035 | if (thMouseCreated) 2036 | { 2037 | pthread_join(thMouse, NULL); 2038 | } 2039 | 2040 | if (thOutputReportCreated) 2041 | { 2042 | pthread_join(thOutputReport, NULL); 2043 | } 2044 | 2045 | if (thInputReportCreated) 2046 | { 2047 | pthread_join(thInputReport, NULL); 2048 | } 2049 | 2050 | if (fKeyboard != -1) 2051 | { 2052 | close(fKeyboard); 2053 | } 2054 | 2055 | if (fMouse != -1) 2056 | { 2057 | close(fMouse); 2058 | } 2059 | 2060 | if (fGadget != -1) 2061 | { 2062 | close(fGadget); 2063 | } 2064 | 2065 | if (pRomBuf) 2066 | { 2067 | free(pRomBuf); 2068 | } 2069 | 2070 | pthread_mutex_destroy(&MouseMtx); 2071 | pthread_mutex_destroy(&UsbMtx); 2072 | 2073 | Echo(1); 2074 | 2075 | printf("Procon Converter exit.\n"); 2076 | return 0; 2077 | } 2078 | 2079 | --------------------------------------------------------------------------------