From a98640300d63856c7849f25f5cb64dd538489f94 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Sat, 27 Apr 2019 23:27:27 +0300 Subject: Updated clovercon, fixed config --- Makefile | 2 +- arch/arm/configs/sun_nontendocm_defconfig | 3 - build_all.sh | 11 +- clovercon/LICENSE | 339 +++++ clovercon/Makefile | 26 +- clovercon/clovercon.c | 1811 ++++++++++++++------------- clovercon/mod/etc/preinit.d/pc000_clovercon | 35 + clovercon/mod/install | 6 + clovercon/mod/readme.txt | 7 + include/linux/compiler-gcc7.h | 63 + 10 files changed, 1375 insertions(+), 928 deletions(-) create mode 100755 clovercon/LICENSE create mode 100755 clovercon/mod/etc/preinit.d/pc000_clovercon create mode 100755 clovercon/mod/install create mode 100755 clovercon/mod/readme.txt create mode 100644 include/linux/compiler-gcc7.h diff --git a/Makefile b/Makefile index 9782addc..29584b19 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 4 SUBLEVEL = 113 -EXTRAVERSION = .23-madmonkey +EXTRAVERSION = .24-madmonkey NAME = Saber-toothed Squirrel # *DOCUMENTATION* diff --git a/arch/arm/configs/sun_nontendocm_defconfig b/arch/arm/configs/sun_nontendocm_defconfig index fa29f860..c09d02f8 100644 --- a/arch/arm/configs/sun_nontendocm_defconfig +++ b/arch/arm/configs/sun_nontendocm_defconfig @@ -160,9 +160,6 @@ CONFIG_RT2800USB=m CONFIG_RT2800USB_RT53XX=y CONFIG_RTL8192CU=m # CONFIG_RTLWIFI_DEBUG is not set -CONFIG_RTL8188EU=m -CONFIG_RTL8192EU=m -CONFIG_RTL8812AU=m CONFIG_INPUT_POLLDEV=y # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_MOUSEDEV_SCREEN_X=1280 diff --git a/build_all.sh b/build_all.sh index 7a36d82e..e3aca082 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,7 +1,7 @@ #!/bin/sh -e MAKE(){ - make ARCH=arm "CROSS_COMPILE=$CROSS_COMPILE" ${1+"$@"} 1>/dev/null + make ARCH=arm "CROSS_COMPILE=$CROSS_COMPILE" CFLAGS_MODULE=-fno-pic ${1+"$@"} 1>/dev/null } CROSS_COMPILE=arm-linux-gnueabihf- @@ -43,14 +43,11 @@ find "$instdir" -type l -delete mkdir "$instdir/lib/modules/$KVERS/extra" cp -f "modules/mali/mali.ko" "$instdir/lib/modules/$KVERS/extra/" cp -f "clovercon/clovercon.ko" "$instdir/lib/modules/$KVERS/extra/" +cp -Rf clovercon/mod/* modules-hmod echo "return 0" > "$instdir/uninstall" echo "no-uninstall" >> "$instdir/uninstall" find "$instdir" -type f -name "*.ko" -print0 | xargs -0 -n1 "${CROSS_COMPILE}strip" --strip-unneeded -echo "somebody set up us the bomb" -exit 0 -makepack "$instdir" -rm -rf "$instdir" -rm -f "modules-$KVERS.hmod" -mv "$instdir.hmod.tgz" "modules-$KVERS.hmod" +cd "$instdir" +tar -czvf ../modules-$KVERS.hmod * diff --git a/clovercon/LICENSE b/clovercon/LICENSE new file mode 100755 index 00000000..d159169d --- /dev/null +++ b/clovercon/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/clovercon/Makefile b/clovercon/Makefile index 816d8e47..aa420b23 100644 --- a/clovercon/Makefile +++ b/clovercon/Makefile @@ -1,36 +1,24 @@ CURRENT = $(shell uname -r) -HMOD = ../mods/hmods/clovercon.hmod TARGET = clovercon -TARGET_PATH = mod/lib/modules/3.4.112/extra -TARGET2 = clvcon -TARGET_PATH2 = mod/lib/modules/3.4.113/extra OBJS = clovercon.o -PWD = $(shell pwd) +PWD = $(shell pwd) +KDIR ?= .. +obj-m := $(TARGET).o -obj-m := $(TARGET).o - -all: pack +all: module module: - $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- CFLAGS_MODULE=-fno-pic /sbin/modinfo $(TARGET).ko arm-linux-gnueabihf-strip --strip-unneeded $(TARGET).ko -pack: module - mkdir -p $(TARGET_PATH) - mkdir -p $(TARGET_PATH2) - cp -f $(TARGET).ko $(TARGET_PATH)/$(TARGET).ko - hexdump -ve '1/1 "%.2X"' $(TARGET).ko | sed "s/332E342E313132/332E342E313133/g" | xxd -r -p > $(TARGET_PATH2)/$(TARGET2).ko - /sbin/modinfo $(TARGET_PATH2)/$(TARGET2).ko - cd mod && tar -czvf ../$(HMOD) * - $(TARGET).o: $(OBJS) $(LD) $(LD_RFLAG) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -r -o $@ $(OBJS) clean: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean - rm -f $(TARGET_PATH)/$(TARGET).ko $(TARGET_PATH2)/$(TARGET2).ko $(HMOD) + rm -f $(TARGET).ko -.PHONY: all module modules pack clean +.PHONY: all module clean -include $(KDIR)/Rules.make diff --git a/clovercon/clovercon.c b/clovercon/clovercon.c index 46e60db9..e43b72fb 100644 --- a/clovercon/clovercon.c +++ b/clovercon/clovercon.c @@ -37,13 +37,10 @@ #include static unsigned short home_combination = 0xffff; -static char autofire = 0; -static char autofire_xy = 0; -static unsigned char autofire_interval = 8; -static char fc_start = 0; - -volatile static char debug_buff[30000]; -volatile static long int debug_pos = 0; +static unsigned short autofire = 0; +static unsigned short autofire_xy = 0; +static unsigned short autofire_interval = 8; +static unsigned short fc_start = 0; MODULE_AUTHOR("Christophe Aguettaz , mod by Cluster 0 - #define ERR(m, ...) {int dbg_len = snprintf((void*)(debug_buff+debug_pos), sizeof(debug_buff)-1-debug_pos, "Clovercon error: " m "\n", ##__VA_ARGS__); if (dbg_len>0) debug_pos+=dbg_len;} - #define INF(m, ...) {int dbg_len = snprintf((void*)(debug_buff+debug_pos), sizeof(debug_buff)-1-debug_pos, "Clovercon info: " m "\n", ##__VA_ARGS__); if (dbg_len>0) debug_pos+=dbg_len;} + #define ERR(m, ...) printk(KERN_ERR m, ##__VA_ARGS__) + #define INF(m, ...) printk(KERN_INFO m, ##__VA_ARGS__) #else - #define ERR(m, ...) - #define INF(m, ...) + #define ERR(m, ...) + #define INF(m, ...) #endif #if VERBOSITY > 1 - #define DBG(m, ...) {int dbg_len = snprintf((void*)(debug_buff+debug_pos), sizeof(debug_buff)-1-debug_pos, "Clovercon: " m "\n", ##__VA_ARGS__); if (dbg_len>0) debug_pos+=dbg_len;} - #define FAST_ERR(m, ...) ERR(m, ##__VA_ARGS__) - #define FAST_DBG(m, ...) DBG(m, ##__VA_ARGS__) + #define DBG(m, ...) printk(KERN_DEBUG m, ##__VA_ARGS__) + #define FAST_ERR(m, ...) ERR(m, ##__VA_ARGS__) + #define FAST_DBG(m, ...) DBG(m, ##__VA_ARGS__) #else - #define DBG(m, ...) - #define FAST_ERR(m, ...) - #define FAST_DBG(m, ...) + #define DBG(m, ...) + #define FAST_ERR(m, ...) + #define FAST_DBG(m, ...) #endif -void hex_dump(char* str, char* buf, int len) -{ #if VERBOSITY > 2 - int dbg_len = snprintf((void*)(debug_buff+debug_pos), sizeof(debug_buff)-1-debug_pos, "%s", str); - if (dbg_len>0) debug_pos+=dbg_len; - while (len) - { - dbg_len = snprintf((void*)(debug_buff+debug_pos), sizeof(debug_buff)-1-debug_pos, " %02X", *buf); - if (dbg_len>0) debug_pos+=dbg_len; - buf++; - len--; - } - dbg_len = snprintf((void*)(debug_buff+debug_pos), sizeof(debug_buff)-1-debug_pos, "\n"); - if (dbg_len>0) debug_pos+=dbg_len; -#endif -} - -#if VERBOSITY > 0 -static ssize_t clovercon_debug_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos) -{ - size_t l = MAX(MIN(count, debug_pos - *pos), 0); - memcpy(buf, (void*)(debug_buff + *pos), l); - if (l > 0) *pos += l; - return l; -} + #define HEXDUMP(prefix, data, len) print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_NONE, 16, 256, data, len, true) +#else + #define HEXDUMP(prefix, data, len) #endif #if (VERBOSITY > 0) || STATE_DEVICES static ssize_t device_dumb_write(struct file *fp, const char __user *buf, - size_t count, loff_t *pos) + size_t count, loff_t *pos) { - return count; // a-la /dev/null + return count; // a-la /dev/null } static long device_dumb_ioctl(struct file *fp, unsigned code, unsigned long value) { - long ret = 0; - switch (code) { + long ret = 0; + switch (code) { default: - ret = -EINVAL; - break; - } + ret = -EINVAL; + break; + } - return ret; + return ret; } static int device_dumb_open(struct inode *ip, struct file *fp) { - return 0; + return 0; } static int device_dumb_release(struct inode *ip, struct file *fp) { - return 0; + return 0; } #endif -#if VERBOSITY > 0 -/* file operations for /dev/clovercon_debug */ -static const struct file_operations clovercon_debug_fops = { - .owner = THIS_MODULE, - .read = clovercon_debug_read, - .write = device_dumb_write, - .unlocked_ioctl = device_dumb_ioctl, - .open = device_dumb_open, - .release = device_dumb_release, -}; - -static struct miscdevice clovercon_debug_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "clovercon_debug", - .fops = &clovercon_debug_fops, -}; -#endif - #if STATE_DEVICES static ssize_t clovercon_state_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos); + size_t count, loff_t *pos); /* file operations for /dev/clovercon* */ static const struct file_operations clovercon_state_fops = { - .owner = THIS_MODULE, - .read = clovercon_state_read, - .write = device_dumb_write, - .unlocked_ioctl = device_dumb_ioctl, - .open = device_dumb_open, - .release = device_dumb_release, + .owner = THIS_MODULE, + .read = clovercon_state_read, + .write = device_dumb_write, + .unlocked_ioctl = device_dumb_ioctl, + .open = device_dumb_open, + .release = device_dumb_release, }; #endif enum ControllerState { - CS_OK, - CS_RETRY_1, - CS_RETRY_2, - CS_ERR + CS_OK, + CS_RETRY_1, + CS_RETRY_2, + CS_ERR }; static bool get_bit(u8 data, int bitnum) { - return (data & (1 << bitnum)) >> bitnum; + return (data & (1 << bitnum)) >> bitnum; } static const struct i2c_device_id clovercon_idtable[] = { - { "classic", CLASSIC_ID }, - {} + { "classic", CLASSIC_ID }, + {} }; MODULE_DEVICE_TABLE(i2c, clovercon_idtable); struct clovercon_info { - struct input_polled_dev *dev; - struct i2c_client *client; - struct i2c_adapter *adapter; + struct input_polled_dev *dev; + struct i2c_client *client; + struct i2c_adapter *adapter; #if CLOVERCON_DETECT_USE_IRQ - int irq; + int irq; #endif - int detection_active; - int gpio; - int id; - enum ControllerState state; - u16 retry_counter; - int home_counter; - int reset_counter; - int autofire_timer; - int autofire_counter_a; - int autofire_counter_b; - int autofire_counter_x; - int autofire_counter_y; - bool autofire_a; - bool autofire_b; - bool autofire_x; - bool autofire_y; - int start_counter; - u8 data_format; - u16 buttons_state; + int detection_active; + int gpio; + int id; + enum ControllerState state; + u16 retry_counter; + int home_counter; + int reset_counter; + int autofire_timer; + int autofire_counter_a; + int autofire_counter_b; + int autofire_counter_x; + int autofire_counter_y; + bool autofire_a; + bool autofire_b; + bool autofire_x; + bool autofire_y; + int start_counter; + u8 data_format; + u16 buttons_state; #if STATE_DEVICES - struct miscdevice state_device; - char state_device_name[32]; + struct miscdevice state_device; + char state_device_name[32]; #endif }; @@ -284,980 +244,1035 @@ static int arr_argc = 0; const char *controller_names[] = {CON_NAME_PREFIX"1", CON_NAME_PREFIX"2", CON_NAME_PREFIX"3", CON_NAME_PREFIX"4"}; -module_param_array(module_params, int, &arr_argc, 0000); +module_param_array(module_params, int, &arr_argc, S_IRUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(module_params, "Input info in the form con0_i2c_bus, con0_detect_gpio, " - "form con1_i2c_bus, con1_detect_gpio, ... gpio < 0 means no detection"); -module_param(home_combination, short, 0000); + "form con1_i2c_bus, con1_detect_gpio, ... gpio < 0 means no detection"); +module_param(home_combination, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(home_combination, "Button combination to open menu " - "(0x001=A,0x002=B,0x004=Select,0x008=Start,0x010=Up,0x020=Down,0x040=Left,0x080=Right,0x100=X,0x200=Y,0x400=L,0x800=R"); -module_param(autofire, byte, 0000); + "(0x001=A,0x002=B,0x004=Select,0x008=Start,0x010=Up,0x020=Down,0x040=Left,0x080=Right,0x100=X,0x200=Y,0x400=L,0x800=R"); +module_param(autofire, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(autofire, "Enable autofire (hold select+a / select+b for a second)"); -module_param(autofire_xy, byte, 0000); +module_param(autofire_xy, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(autofire_xy, "Use X/Y on classic controller as autofire A/B"); -module_param(autofire_interval, byte, 0000); +module_param(autofire_interval, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(autofire_interval, "Autofire interval (default is 8)"); -module_param(fc_start, byte, 0000); +module_param(fc_start, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(fc_start, "Enable start button emulation for second controller"); #if CLOVERCON_DETECT_USE_IRQ struct clovercon_info * clovercon_info_from_irq(int irq) { - int i; - for (i = 0; i < MAX_CON_COUNT; i++) { - if (con_info_list[i].irq == irq) { - return &con_info_list[i]; - } - } - return NULL; + int i; + for (i = 0; i < MAX_CON_COUNT; i++) { + if (con_info_list[i].irq == irq) { + return &con_info_list[i]; + } + } + return NULL; } #endif #if STATE_DEVICES static ssize_t clovercon_state_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos) + size_t count, loff_t *pos) { - // which contoller? we need device file name - char path_buffer[64]; - char state_buffer[16]; - int id; - size_t l; - u16 state; - - char* path = dentry_path_raw(fp->f_path.dentry,path_buffer,sizeof(path_buffer)); - while (*path && *(path+1)) path++; // moving to last character which is number - if (!*path) return 0; - id = *path - '0'; - - state = con_info_list[id-1].buttons_state; - snprintf(state_buffer, sizeof(state_buffer), "%02X%02X", state & 0xFF, (state >> 8) & 0xFF); - l = MAX(MIN(count, strlen(state_buffer) - *pos), 0); - memcpy(buf, state_buffer + *pos, l); - if (l > 0) *pos += l; - return l; + // which contoller? we need device file name + char path_buffer[64]; + char state_buffer[16]; + int id; + size_t l; + u16 state; + + char* path = dentry_path_raw(fp->f_path.dentry,path_buffer,sizeof(path_buffer)); + while (*path && *(path+1)) path++; // moving to last character which is number + if (!*path) return 0; + id = *path - '0'; + + state = con_info_list[id-1].buttons_state; + snprintf(state_buffer, sizeof(state_buffer), "%02X%02X", state & 0xFF, (state >> 8) & 0xFF); + l = MAX(MIN(count, strlen(state_buffer) - *pos), 0); + memcpy(buf, state_buffer + *pos, l); + if (l > 0) *pos += l; + return l; } #endif struct clovercon_info * clovercon_info_from_adapter(struct i2c_adapter *adapter) { - int i; - for (i = 0; i < MAX_CON_COUNT; i++) { - if (con_info_list[i].adapter == adapter) { - return &con_info_list[i]; - } - } - return NULL; + int i; + for (i = 0; i < MAX_CON_COUNT; i++) { + if (con_info_list[i].adapter == adapter) { + return &con_info_list[i]; + } + } + return NULL; } static int clovercon_write(struct i2c_client *client, u8 *data, size_t count) { - struct i2c_msg msg = { - .addr = client->addr, - .len = count, - .buf = data - }; - int ret; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - //read and write errors are DBG only to account for normal - //errors coming from disconnects - DBG("write failed with error %i", ret); - return ret; - } else if (ret != 1) { - DBG("incomplete write, return value: %i", ret); - return -EIO; - } - - return 0; + struct i2c_msg msg = { + .addr = client->addr, + .len = count, + .buf = data + }; + int ret; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + //read and write errors are DBG only to account for normal + //errors coming from disconnects + DBG("write failed with error %i", ret); + return ret; + } else if (ret != 1) { + DBG("incomplete write, return value: %i", ret); + return -EIO; + } + + return 0; } static int clovercon_prepare_read(struct i2c_client *client, u8 address) { - int ret; + int ret; - ret = clovercon_write(client, &address, 1); - if (ret) { - DBG("prepare_read failed"); - return ret; - } + ret = clovercon_write(client, &address, 1); + if (ret) { + DBG("prepare_read failed"); + return ret; + } - return 0; + return 0; } static int clovercon_read(struct i2c_client *client, u8 address, u8 *values, size_t count) { - struct i2c_msg data_msg = { - .addr = client->addr, - .flags = I2C_M_RD, - .len = count, - .buf = values - }; - int ret; - - ret = clovercon_prepare_read(client, address); - if (ret) - return ret; - - // Wait > 150µs before prepare_read and actual read - usleep_range(150, 200); - - ret = i2c_transfer(client->adapter, &data_msg, 1); - if (ret < 0) { - DBG("read failed with error %i", ret); - return ret; - } else if (ret != 1) { - DBG("incomplete read, return value: %i", ret); - return -EIO; - } - - return 0; + struct i2c_msg data_msg = { + .addr = client->addr, + .flags = I2C_M_RD, + .len = count, + .buf = values + }; + int ret; + + ret = clovercon_prepare_read(client, address); + if (ret) + return ret; + + // Wait > 150µs before prepare_read and actual read + usleep_range(150, 200); + + ret = i2c_transfer(client->adapter, &data_msg, 1); + if (ret < 0) { + DBG("read failed with error %i", ret); + return ret; + } else if (ret != 1) { + DBG("incomplete read, return value: %i", ret); + return -EIO; + } + + return 0; } static int clovercon_read_controller_info(struct i2c_client *client, u8 *data, size_t len) { - int ret; + int ret; - len = MIN(len, 0xff -0xfa + 1); - ret = clovercon_read(client, 0xfa, data, len); - if (ret) - return ret; + len = MIN(len, 0xff -0xfa + 1); + ret = clovercon_read(client, 0xfa, data, len); + if (ret) + return ret; - return len; - // print_hex_dump(KERN_DEBUG, "Controller info data: " , DUMP_PREFIX_NONE, 16, 256, data, len, false); + return len; } static int clovercon_setup(struct clovercon_info *info) { - struct i2c_client *client = info->client; - u8 init_data[] = { 0xf0, 0x55, 0xfb, 0x00, 0xfe, 3 }; - static const int CON_INFO_LEN = 6; - u8 con_info_data[CON_INFO_LEN]; - int ret; + struct i2c_client *client = info->client; + u8 init_data[] = { 0xf0, 0x55, 0xfb, 0x00, 0xfe, 3 }; + static const int CON_INFO_LEN = 6; + u8 con_info_data[CON_INFO_LEN]; + int ret; #if VERBOSITY > 2 - static const int READ_LEN = 21; - u8 data[READ_LEN]; + u8 debug_data[32]; + s16 debug_pos; #endif - DBG("Clovercon setup"); + DBG("Clovercon setup"); #if VERBOSITY > 3 - ret = clovercon_read_controller_info(client, con_info_data, CON_INFO_LEN); - if (ret < 0) { - ERR("error reading controller info"); - goto err; - } - hex_dump("con_info_data before setup:", con_info_data, ret); + ret = clovercon_read_controller_info(client, con_info_data, CON_INFO_LEN); + if (ret < 0) { + ERR("error reading controller info"); + goto err; + } + HEXDUMP("con_info_data before setup: ", con_info_data, ret); #endif - ret = clovercon_write(client, &init_data[0], 2); - if (ret) - goto err; - ret = clovercon_write(client, &init_data[2], 2); - if (ret) - goto err; - // trying to set data format to 3 - ret = clovercon_write(client, &init_data[4], 2); - - ret = clovercon_read_controller_info(client, con_info_data, CON_INFO_LEN); - if (ret < 0) { - ERR("error reading controller info"); - goto err; - } else if (ret != CON_INFO_LEN) { - ERR("wrong read length %i reading controller info", ret); - ret = -EIO; - goto err; - } - hex_dump("con_info_data after setup:", con_info_data, sizeof(con_info_data)); + ret = clovercon_write(client, &init_data[0], 2); + if (ret) + goto err; + ret = clovercon_write(client, &init_data[2], 2); + if (ret) + goto err; + // trying to set data format to 3 + ret = clovercon_write(client, &init_data[4], 2); + + ret = clovercon_read_controller_info(client, con_info_data, CON_INFO_LEN); + if (ret < 0) { + ERR("error reading controller info"); + goto err; + } else if (ret != CON_INFO_LEN) { + ERR("wrong read length %i reading controller info", ret); + ret = -EIO; + goto err; + } + HEXDUMP("con_info_data after setup: ", con_info_data, sizeof(con_info_data)); #if VERBOSITY > 2 - ret = clovercon_read(client, 0, data, READ_LEN); - if (ret) - { - ERR("read failed for active controller - possible controller disconnect"); - ret = -EIO; - goto err; - } - hex_dump("poll:", data, READ_LEN); + for (debug_pos = 0; debug_pos < 256; debug_pos += sizeof(debug_data)) + { + ret = clovercon_read(client, debug_pos, debug_data, sizeof(debug_data)); + if (ret) + { + ERR("read failed for active controller - possible controller disconnect"); + ret = -EIO; + goto err; + } + HEXDUMP("clvcon dump: ", debug_data, sizeof(debug_data)); + } #endif - // autodetecting data format - // it should be 0x03 for original classic controllers and clovercons - // but seems like not every 3rd party controller supports data format selection - info->data_format = con_info_data[4]; - - if (con_info_data[5] != 1) { - ERR("unsupported controller id %i", (int)con_info_data[5]); - ret = -EIO; - goto err; - } - return 0; + // autodetecting data format + // it should be 0x03 for original classic controllers and clovercons + // but seems like not every 3rd party controller supports data format selection + info->data_format = con_info_data[4]; + + // no, i want to support everything! /Cluster + /* + if (con_info_data[5] != 1) { + ERR("unsupported controller id %i", (int)con_info_data[5]); + ret = -EIO; + goto err; + } + */ + return 0; err: - ERR("controller setup failed with error %i", -ret); - return ret; + ERR("controller setup failed with error %i", -ret); + return ret; } static void clamp_stick(int *px, int *py) { - int x_sign = 1; - int y_sign = 1; - int x = *px; - int y = *py; - //int norm; - - if (x < 0) { - x_sign = -1; - x = -x; - } - if (y < 0) { - y_sign = -1; - y = -y; - } - - x = MAX(0, x - DEAD_ZONE); - y = MAX(0, y - DEAD_ZONE); + int x_sign = 1; + int y_sign = 1; + int x = *px; + int y = *py; + //int norm; + + if (x < 0) { + x_sign = -1; + x = -x; + } + if (y < 0) { + y_sign = -1; + y = -y; + } + + x = MAX(0, x - DEAD_ZONE); + y = MAX(0, y - DEAD_ZONE); /* - if (x == 0 && y == 0) { - goto clamp_end; - } + if (x == 0 && y == 0) { + goto clamp_end; + } - norm = (y <= x) ? (DIAG_MAX * x + (STICK_MAX - DIAG_MAX) * y) : - (DIAG_MAX * y + (STICK_MAX - DIAG_MAX) * x); + norm = (y <= x) ? (DIAG_MAX * x + (STICK_MAX - DIAG_MAX) * y) : + (DIAG_MAX * y + (STICK_MAX - DIAG_MAX) * x); - if (DIAG_MAX * STICK_MAX < norm) { - x = DIAG_MAX * STICK_MAX * x / norm; - y = DIAG_MAX * STICK_MAX * y / norm; - } + if (DIAG_MAX * STICK_MAX < norm) { + x = DIAG_MAX * STICK_MAX * x / norm; + y = DIAG_MAX * STICK_MAX * y / norm; + } clamp_end: */ - *px = x * x_sign; - *py = y * y_sign; + *px = x * x_sign; + *py = y * y_sign; } static void clovercon_poll(struct input_polled_dev *polled_dev) { - struct clovercon_info *info = polled_dev->private; - static const int READ_LEN = 21; - u8 data[READ_LEN]; - int jx, jy, rx, ry, tl, tr; - bool left, right, up, down, a, b, x, y, select, start, home, l, r, zl, zr, reset; - u16 retry_delay = RETRY_BASE_DELAY; - u16 buttons_state; - int ret; - bool turbo; - - switch (info->state) { - case CS_OK: - ret = clovercon_read(info->client, 0, data, READ_LEN); - if (ret) { - DBG("read failed for active controller - possible controller disconnect"); - INF("moving controller %i to error state", info->id); - info->state = CS_RETRY_1; - break; - } - - //print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 256, data, READ_LEN, false); - - /* The Wii could trust the payload 100% because the Wii Remote - * controller is powered with AA batteries. In the case of - * CLOVER high voltage on DC power supply has shown very noisy - * payloads coming out of the I²C bus. But we don't have any - * checksum or parity bits to discard such corrupted payloads. - * - * However the payload zero padding should be impacted by the - * DC noise and some high bits should pop up there too. - * - * Use that as last resort discarding criteria */ - jy = 0; - data[18] = 0; // for ultra shitty pro controller clones - // which will work only after hardware - // modification (2.2K pull-up resistor on SCL and SDA) - for (jx=8; jx<21; jx++) { - if (data[jx] == 0xFF) data[jx] = 0; // for 3rd party controllers - jy += data[jx]; - } - if (jy) { - /* noise, discard payload */ - break; - } - - if (info->data_format == 3) - { - jx = data[0] - 0x80; - rx = data[1] - 0x80; - jy = 0x7fl - data[2]; - ry = 0x7fl - data[3]; - tl = data[4]; - tr = data[5]; - - r = !get_bit(data[6], D_BTN_R); - start = !get_bit(data[6], D_BTN_START); - home = !get_bit(data[6], D_BTN_HOME); - select = !get_bit(data[6], D_BTN_SELECT); - l = !get_bit(data[6], D_BTN_L); - down = !get_bit(data[6], D_BTN_DOWN); - right = !get_bit(data[6], D_BTN_RIGHT); - - up = !get_bit(data[7], D_BTN_UP); - left = !get_bit(data[7], D_BTN_LEFT); - zr = !get_bit(data[7], D_BTN_ZR); - x = !get_bit(data[7], D_BTN_X); - y = !get_bit(data[7], D_BTN_Y); - a = !get_bit(data[7], D_BTN_A); - b = !get_bit(data[7], D_BTN_B); - zl = !get_bit(data[7], D_BTN_ZL); - } else { - jx = ((data[0] & 0x3f) - 0x20) * 4; - rx = (((data[2] >> 7) | ((data[1] & 0xC0) >> 5) | ((data[0] & 0xC0) >> 3)) - 0x10) * 8; - jy = ((data[1] & 0x3f) - 0x20) * -4; - ry = ((data[2] & 0x1f) - 0x10) * -8; - tl = ((data[3] >> 5) | ((data[2] & 0x60) >> 2)) * 8; - tr = (data[3] & 0x1f) * 8; - - r = !get_bit(data[4], D_BTN_R); - start = !get_bit(data[4], D_BTN_START); - home = !get_bit(data[4], D_BTN_HOME); - select = !get_bit(data[4], D_BTN_SELECT); - l = !get_bit(data[4], D_BTN_L); - down = !get_bit(data[4], D_BTN_DOWN); - right = !get_bit(data[4], D_BTN_RIGHT); - - up = !get_bit(data[5], D_BTN_UP); - left = !get_bit(data[5], D_BTN_LEFT); - zr = !get_bit(data[5], D_BTN_ZR); - x = !get_bit(data[5], D_BTN_X); - y = !get_bit(data[5], D_BTN_Y); - a = !get_bit(data[5], D_BTN_A); - b = !get_bit(data[5], D_BTN_B); - zl = !get_bit(data[5], D_BTN_ZL); - } - - // Bitmask for current controller state - buttons_state = 0; - if (a) buttons_state |= (1 << 0); - if (b) buttons_state |= (1 << 1); - if (select) buttons_state |= (1 << 2); - if (start) buttons_state |= (1 << 3); - if (up || (jy < -DEAD_ZONE)) buttons_state |= (1 << 4); - if (down || (jy > DEAD_ZONE)) buttons_state |= (1 << 5); - if (left || (jx < -DEAD_ZONE)) buttons_state |= (1 << 6); - if (right || (jx > DEAD_ZONE)) buttons_state |= (1 << 7); - if (x) buttons_state |= (1 << 8); - if (y) buttons_state |= (1 << 9); - if (l) buttons_state |= (1 << 10); - if (r) buttons_state |= (1 << 11); - if (zl) buttons_state |= (1 << 12); - if (zr) buttons_state |= (1 << 13); - info->buttons_state = buttons_state; - - // Reset combination - reset = home_combination == buttons_state; - - // Start button workaroud for second controller on Famicom - if (fc_start && info->id == 2) - { - if (a && !select && b && !start && up && !down && !left && !right) - info->start_counter++; - else - info->start_counter = 0; - if (info->start_counter >= START_COMBINATION_THRESHOLD) - start = 1; - } - - // Autofire - info->autofire_timer++; - if (info->autofire_timer >= autofire_interval*2) - info->autofire_timer = 0; - turbo = info->autofire_timer / autofire_interval; - if (autofire) - { - if (select && a && !b && !x && !y && !start && !up && !down && !left && !right) - info->autofire_counter_a++; - else - info->autofire_counter_a = 0; - if (select && !a && b && !x && !y && !start && !up && !down && !left && !right) - info->autofire_counter_b++; - else - info->autofire_counter_b = 0; - if (select && !a && !b && x && !y && !start && !up && !down && !left && !right) - info->autofire_counter_x++; - else - info->autofire_counter_x = 0; - if (select && !a && !b && !x && y && !start && !up && !down && !left && !right) - info->autofire_counter_y++; - else - info->autofire_counter_y = 0; - - if (info->autofire_counter_a == AUTOFIRE_COMBINATION_THRESHOLD) - info->autofire_a = !info->autofire_a; - if (info->autofire_counter_b == AUTOFIRE_COMBINATION_THRESHOLD) - info->autofire_b = !info->autofire_b; - if (info->autofire_counter_x == AUTOFIRE_COMBINATION_THRESHOLD) - info->autofire_x = !info->autofire_x; - if (info->autofire_counter_y == AUTOFIRE_COMBINATION_THRESHOLD) - info->autofire_y = !info->autofire_y; - - if (info->autofire_a && !turbo) a = 0; - if (info->autofire_b && !turbo) b = 0; - if (info->autofire_x && !turbo) x = 0; - if (info->autofire_y && !turbo) y = 0; - } - if (autofire_xy) - { - // X and Y on classic controller now are autofire A and B - if (x && turbo) a = 1; - if (y && turbo) b = 1; - x = y = 0; - } - - /* When DC noise is kicking in, we want to avoid the emulator - * switching to the menu supriously because of noisy home button - * events. - * - * Note that even the Mini NES controller can report the event - * even though no physical button is present. The MCU is the - * same as the classic/pro controller. */ - if (home) { - info->home_counter++; - if (info->home_counter>HOME_BUTTON_THRESHOLD) { - info->home_counter = HOME_BUTTON_THRESHOLD; - } - } else { - info->home_counter = 0; - } - if (reset) { - info->reset_counter++; - if (info->reset_counter>RESET_COMBINATION_THRESHOLD) { - info->reset_counter = RESET_COMBINATION_THRESHOLD; - } - } else { - info->reset_counter = 0; - } - - clamp_stick(&jx, &jy); - clamp_stick(&rx, &ry); - - input_report_abs(polled_dev->input, ABS_X, jx); - input_report_abs(polled_dev->input, ABS_Y, jy); - input_report_abs(polled_dev->input, ABS_RX, rx); - input_report_abs(polled_dev->input, ABS_RY, ry); - input_report_abs(polled_dev->input, ABS_Z, tl); - input_report_abs(polled_dev->input, ABS_RZ, tr); - input_report_key(polled_dev->input, BTN_TR, r); - input_report_key(polled_dev->input, BTN_START, start); - input_report_key(polled_dev->input, BTN_MODE, (info->home_counter>=HOME_BUTTON_THRESHOLD) || (info->reset_counter>=RESET_COMBINATION_THRESHOLD)); - input_report_key(polled_dev->input, BTN_SELECT, select); - input_report_key(polled_dev->input, BTN_TL, l); - input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY4, down); - input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY2, right); - input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY3, up); - input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY1, left); - input_report_key(polled_dev->input, BTN_TR2, zr); - input_report_key(polled_dev->input, BTN_X, x); - input_report_key(polled_dev->input, BTN_A, a); - input_report_key(polled_dev->input, BTN_Y, y); - input_report_key(polled_dev->input, BTN_B, b); - input_report_key(polled_dev->input, BTN_TL2, zl); - - input_sync(polled_dev->input); - - break; - case CS_RETRY_1: - retry_delay /= 2; - //fall-through - case CS_RETRY_2: - retry_delay /= 2; - //fall-through - case CS_ERR: - info->retry_counter++; - if (info->retry_counter == retry_delay) { - DBG("retrying controller setup"); - ret = clovercon_setup(info); - if (ret) { - info->state = MIN(CS_ERR, info->state + 1); - } else { - info->state = CS_OK; - INF("setup succeeded for controller %i, moving to OK state", info->id); - } - info->retry_counter = 0; - } - break; - default: - info->state = CS_ERR; - } + struct clovercon_info *info = polled_dev->private; + static const int READ_LEN = 21; + u8 data[READ_LEN]; + int jx, jy, rx, ry, tl, tr, ax, ay, az; + bool left, right, up, down, a, b, x, y, select, start, home, l, r, zl, zr, reset; + u16 retry_delay = RETRY_BASE_DELAY; + u16 buttons_state; + int ret; + bool turbo; + + switch (info->state) { + case CS_OK: + ret = clovercon_read(info->client, 0, data, READ_LEN); + if (ret) { + DBG("read failed for active controller - possible controller disconnect"); + INF("moving controller %i to error state", info->id); + info->state = CS_RETRY_1; + break; + } + + #if VERBOSITY > 4 + HEXDUMP("clvcon polling: ", data, READ_LEN); + #endif + + /* The Wii could trust the payload 100% because the Wii Remote + * controller is powered with AA batteries. In the case of + * CLOVER high voltage on DC power supply has shown very noisy + * payloads coming out of the I²C bus. But we don't have any + * checksum or parity bits to discard such corrupted payloads. + * + * However the payload zero padding should be impacted by the + * DC noise and some high bits should pop up there too. + * + * Use that as last resort discarding criteria */ + jy = 0; + data[18] = 0; // for ultra shitty pro controller clones + // which will work only after hardware + // modification (2.2K pull-up resistor on SCL and SDA) + for (jx=10; jx<21; jx++) { + if (data[jx] == 0xFF) data[jx] = 0; // for 3rd party controllers + jy += data[jx]; + } + if (jy) { + /* noise, discard payload */ + break; + } + + jx=0, jy=0, rx=0, ry=0, tl=0, tr=0, + left=0, right=0, up=0, down=0, a=0, b=0, x=0, y=0, select=0, + start=0, home=0, l=0, r=0, zl=0, zr=0; + switch (info->data_format) + { + case 0: // Nunchuck! So one-handed people can play too. Seriosly. + jx = data[0] - 0x80; + jy = 0x7fl - data[1]; + ax = data[2] - 0x80; + ay = data[3] - 0x80; + az = data[4] - 0x80; + a = !get_bit(data[5], 0); + b = !get_bit(data[5], 1); + up = jy < -DEAD_ZONE; + down = jy > DEAD_ZONE; + left = jx < -DEAD_ZONE; + right = jx > DEAD_ZONE; + // Tilt nunchuck left = select + if (!get_bit(info->buttons_state, 2)) // Select not pressed yet + { + select = (ax <= -ACC_TRIGGER_ZONE) && (ay < ACC_DEAD_ZONE) && (ay > -ACC_DEAD_ZONE) && (az < ACC_DEAD_ZONE) && (az > -ACC_DEAD_ZONE); + } else { // Already pressed + select = ax <= -ACC_DEAD_ZONE; + } + // Tilt nunchuck right = start + start = (ax >= ACC_TRIGGER_ZONE) && (ay < ACC_DEAD_ZONE) && (ay > -ACC_DEAD_ZONE) && (az < ACC_DEAD_ZONE) && (az > -ACC_DEAD_ZONE); + if (!get_bit(info->buttons_state, 3)) // Start not pressed yet + { + start = (ax >= ACC_TRIGGER_ZONE) && (ay < ACC_DEAD_ZONE) && (ay > -ACC_DEAD_ZONE) && (az < ACC_DEAD_ZONE) && (az > -ACC_DEAD_ZONE); + } else { // Already pressed + start = ax >= ACC_DEAD_ZONE; + } + // Tilt nunchuck upside down = home + home = (az <= -ACC_TRIGGER_ZONE); + break; + case 1: + jx = ((data[0] & 0x3f) - 0x20) * 4; + rx = (((data[2] >> 7) | ((data[1] & 0xC0) >> 5) | ((data[0] & 0xC0) >> 3)) - 0x10) * 8; + jy = ((data[1] & 0x3f) - 0x20) * -4; + ry = ((data[2] & 0x1f) - 0x10) * -8; + tl = ((data[3] >> 5) | ((data[2] & 0x60) >> 2)) * 8 - 0x80; + tr = (data[3] & 0x1f) * 8 - 0x80; + r = !get_bit(data[4], D_BTN_R); + start = !get_bit(data[4], D_BTN_START); + home = !get_bit(data[4], D_BTN_HOME); + select = !get_bit(data[4], D_BTN_SELECT); + l = !get_bit(data[4], D_BTN_L); + down = !get_bit(data[4], D_BTN_DOWN); + right = !get_bit(data[4], D_BTN_RIGHT); + up = !get_bit(data[5], D_BTN_UP); + left = !get_bit(data[5], D_BTN_LEFT); + zr = !get_bit(data[5], D_BTN_ZR); + x = !get_bit(data[5], D_BTN_X); + y = !get_bit(data[5], D_BTN_Y); + a = !get_bit(data[5], D_BTN_A); + b = !get_bit(data[5], D_BTN_B); + zl = !get_bit(data[5], D_BTN_ZL); + break; + case 2: + jx = data[0] - 0x80; + rx = data[1] - 0x80; + jy = 0x7fl - data[2]; + ry = 0x7fl - data[3]; + //wtf = data[4]; // What is it? + tl = data[5] - 0x80; + tr = data[6] - 0x80; + r = !get_bit(data[7], D_BTN_R); + start = !get_bit(data[7], D_BTN_START); + home = !get_bit(data[7], D_BTN_HOME); + select = !get_bit(data[7], D_BTN_SELECT); + l = !get_bit(data[7], D_BTN_L); + down = !get_bit(data[7], D_BTN_DOWN); + right = !get_bit(data[7], D_BTN_RIGHT); + up = !get_bit(data[8], D_BTN_UP); + left = !get_bit(data[8], D_BTN_LEFT); + zr = !get_bit(data[8], D_BTN_ZR); + x = !get_bit(data[8], D_BTN_X); + y = !get_bit(data[8], D_BTN_Y); + a = !get_bit(data[8], D_BTN_A); + b = !get_bit(data[8], D_BTN_B); + zl = !get_bit(data[8], D_BTN_ZL); + break; + case 3: + jx = data[0] - 0x80; + rx = data[1] - 0x80; + jy = 0x7fl - data[2]; + ry = 0x7fl - data[3]; + tl = data[4]; + tr = data[5]; + r = !get_bit(data[6], D_BTN_R); + start = !get_bit(data[6], D_BTN_START); + home = !get_bit(data[6], D_BTN_HOME); + select = !get_bit(data[6], D_BTN_SELECT); + l = !get_bit(data[6], D_BTN_L); + down = !get_bit(data[6], D_BTN_DOWN); + right = !get_bit(data[6], D_BTN_RIGHT); + up = !get_bit(data[7], D_BTN_UP); + left = !get_bit(data[7], D_BTN_LEFT); + zr = !get_bit(data[7], D_BTN_ZR); + x = !get_bit(data[7], D_BTN_X); + y = !get_bit(data[7], D_BTN_Y); + a = !get_bit(data[7], D_BTN_A); + b = !get_bit(data[7], D_BTN_B); + zl = !get_bit(data[7], D_BTN_ZL); + } + + // Bitmask for current controller state + buttons_state = 0; + if (a) buttons_state |= (1 << 0); + if (b) buttons_state |= (1 << 1); + if (select) buttons_state |= (1 << 2); + if (start) buttons_state |= (1 << 3); + if (up || (jy < -DEAD_ZONE)) buttons_state |= (1 << 4); + if (down || (jy > DEAD_ZONE)) buttons_state |= (1 << 5); + if (left || (jx < -DEAD_ZONE)) buttons_state |= (1 << 6); + if (right || (jx > DEAD_ZONE)) buttons_state |= (1 << 7); + if (x) buttons_state |= (1 << 8); + if (y) buttons_state |= (1 << 9); + if (l) buttons_state |= (1 << 10); + if (r) buttons_state |= (1 << 11); + if (zl) buttons_state |= (1 << 12); + if (zr) buttons_state |= (1 << 13); + info->buttons_state = buttons_state; + + // Reset combination + reset = home_combination == buttons_state; + + // Start button workaroud for second controller on Famicom + if (fc_start && info->id == 2) + { + if (a && !select && b && !start && up && !down && !left && !right) + info->start_counter++; + else + info->start_counter = 0; + if (info->start_counter >= START_COMBINATION_THRESHOLD) + start = 1; + } + + // Autofire + info->autofire_timer++; + if (info->autofire_timer >= autofire_interval*2) + info->autofire_timer = 0; + turbo = info->autofire_timer / autofire_interval; + if (autofire) + { + if (select && a && !b && !x && !y && !start && !up && !down && !left && !right) + info->autofire_counter_a++; + else + info->autofire_counter_a = 0; + if (select && !a && b && !x && !y && !start && !up && !down && !left && !right) + info->autofire_counter_b++; + else + info->autofire_counter_b = 0; + if (select && !a && !b && x && !y && !start && !up && !down && !left && !right) + info->autofire_counter_x++; + else + info->autofire_counter_x = 0; + if (select && !a && !b && !x && y && !start && !up && !down && !left && !right) + info->autofire_counter_y++; + else + info->autofire_counter_y = 0; + + if (info->autofire_counter_a == AUTOFIRE_COMBINATION_THRESHOLD) + info->autofire_a = !info->autofire_a; + if (info->autofire_counter_b == AUTOFIRE_COMBINATION_THRESHOLD) + info->autofire_b = !info->autofire_b; + if (info->autofire_counter_x == AUTOFIRE_COMBINATION_THRESHOLD) + info->autofire_x = !info->autofire_x; + if (info->autofire_counter_y == AUTOFIRE_COMBINATION_THRESHOLD) + info->autofire_y = !info->autofire_y; + + if (info->autofire_a && !turbo) a = 0; + if (info->autofire_b && !turbo) b = 0; + if (info->autofire_x && !turbo) x = 0; + if (info->autofire_y && !turbo) y = 0; + } + if (autofire_xy) + { + // X and Y on classic controller now are autofire A and B + if (x && turbo) a = 1; + if (y && turbo) b = 1; + x = y = 0; + } + + /* When DC noise is kicking in, we want to avoid the emulator + * switching to the menu supriously because of noisy home button + * events. + * + * Note that even the Mini NES controller can report the event + * even though no physical button is present. The MCU is the + * same as the classic/pro controller. */ + if (home) { + info->home_counter++; + if (info->home_counter>HOME_BUTTON_THRESHOLD) { + info->home_counter = HOME_BUTTON_THRESHOLD; + } + } else { + info->home_counter = 0; + } + if (reset) { + info->reset_counter++; + if (info->reset_counter>RESET_COMBINATION_THRESHOLD) { + info->reset_counter = RESET_COMBINATION_THRESHOLD; + } + } else { + info->reset_counter = 0; + } + + clamp_stick(&jx, &jy); + clamp_stick(&rx, &ry); + + input_report_abs(polled_dev->input, ABS_X, jx); + input_report_abs(polled_dev->input, ABS_Y, jy); + input_report_abs(polled_dev->input, ABS_RX, rx); + input_report_abs(polled_dev->input, ABS_RY, ry); + input_report_abs(polled_dev->input, ABS_Z, tl); + input_report_abs(polled_dev->input, ABS_RZ, tr); + input_report_key(polled_dev->input, BTN_TR, r); + input_report_key(polled_dev->input, BTN_START, start); + input_report_key(polled_dev->input, BTN_MODE, (info->home_counter>=HOME_BUTTON_THRESHOLD) || (info->reset_counter>=RESET_COMBINATION_THRESHOLD)); + input_report_key(polled_dev->input, BTN_SELECT, select); + input_report_key(polled_dev->input, BTN_TL, l); + input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY4, down); + input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY2, right); + input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY3, up); + input_report_key(polled_dev->input, BTN_TRIGGER_HAPPY1, left); + input_report_key(polled_dev->input, BTN_TR2, zr); + input_report_key(polled_dev->input, BTN_X, x); + input_report_key(polled_dev->input, BTN_A, a); + input_report_key(polled_dev->input, BTN_Y, y); + input_report_key(polled_dev->input, BTN_B, b); + input_report_key(polled_dev->input, BTN_TL2, zl); + + input_sync(polled_dev->input); + + break; + case CS_RETRY_1: + retry_delay /= 2; + //fall-through + case CS_RETRY_2: + retry_delay /= 2; + //fall-through + case CS_ERR: + info->retry_counter++; + if (info->retry_counter == retry_delay) { + DBG("retrying controller setup"); + ret = clovercon_setup(info); + if (ret) { + info->state = MIN(CS_ERR, info->state + 1); + } else { + info->state = CS_OK; + INF("setup succeeded for controller %i, moving to OK state", info->id); + } + info->retry_counter = 0; + } + break; + default: + info->state = CS_ERR; + } } static void clovercon_open(struct input_polled_dev *polled_dev) { - struct clovercon_info *info = polled_dev->private; - if (clovercon_setup(info)) { - info->retry_counter = 0; - info->state = CS_RETRY_1; - INF("opened controller %i, controller in error state after failed setup", info->id); - } else { - info->state = CS_OK; - info->home_counter = 0; - info->reset_counter = 0; - INF("opened controller %i, controller in OK state", info->id); - } + struct clovercon_info *info = polled_dev->private; + if (clovercon_setup(info)) { + info->retry_counter = 0; + info->state = CS_RETRY_1; + INF("opened controller %i, controller in error state after failed setup", info->id); + } else { + info->state = CS_OK; + info->home_counter = 0; + info->reset_counter = 0; + INF("opened controller %i, controller in OK state", info->id); + } } static int clovercon_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct clovercon_info *info; - struct input_polled_dev *polled_dev; - struct input_dev *input_dev; - int ret = 0; - - switch (id->driver_data) { - case CLASSIC_ID: - DBG("probing classic controller"); - break; - default: - ERR("unknown id: %lu\n", id->driver_data); - return -EINVAL; - } - - mutex_lock(&con_state_lock); - info = clovercon_info_from_adapter(client->adapter); - if (!info) { - ERR("unkonwn client passed to probe"); - mutex_unlock(&con_state_lock); - return -EINVAL; - } - info->client = client; - i2c_set_clientdata(client, info); - mutex_unlock(&con_state_lock); - - polled_dev = input_allocate_polled_device(); - if (!polled_dev) { - ERR("error allocating polled device"); - return -ENOMEM; - } - - info->dev = polled_dev; - - polled_dev->poll_interval = POLL_INTERVAL; - polled_dev->poll = clovercon_poll; - polled_dev->open = clovercon_open; - polled_dev->private = info; - - input_dev = polled_dev->input; - - //change controller_names initializer when changing MAX_CON_COUNT - BUILD_BUG_ON(MAX_CON_COUNT != ARRAY_SIZE(controller_names)); - input_dev->name = controller_names[info->id - 1]; - input_dev->phys = DRV_NAME"/clovercon"; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - - set_bit(EV_ABS, input_dev->evbit); - set_bit(ABS_X, input_dev->absbit); - set_bit(ABS_Y, input_dev->absbit); - set_bit(ABS_RX, input_dev->absbit); - set_bit(ABS_RY, input_dev->absbit); - /* - L/R are analog on the classic controller, digital - on the pro with values 0 - 0xf8 - */ - set_bit(ABS_Z, input_dev->absbit); - set_bit(ABS_RZ, input_dev->absbit); - - set_bit(EV_KEY, input_dev->evbit); - set_bit(BTN_X, input_dev->keybit); - set_bit(BTN_B, input_dev->keybit); - set_bit(BTN_A, input_dev->keybit); - set_bit(BTN_Y, input_dev->keybit); - set_bit(BTN_TRIGGER_HAPPY3, input_dev->keybit); // up - set_bit(BTN_TRIGGER_HAPPY4, input_dev->keybit); // down - set_bit(BTN_TRIGGER_HAPPY2, input_dev->keybit); // right - set_bit(BTN_TRIGGER_HAPPY1, input_dev->keybit); // left - set_bit(BTN_TR, input_dev->keybit); - set_bit(BTN_TL, input_dev->keybit); - set_bit(BTN_TR2, input_dev->keybit); - set_bit(BTN_TL2, input_dev->keybit); - set_bit(BTN_SELECT, input_dev->keybit); - set_bit(BTN_START, input_dev->keybit); - set_bit(BTN_MODE, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_X, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); - input_set_abs_params(input_dev, ABS_Y, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); - input_set_abs_params(input_dev, ABS_RX, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); - input_set_abs_params(input_dev, ABS_RY, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); - input_set_abs_params(input_dev, ABS_Z, TRIGGER_MIN, TRIGGER_MAX, TRIGGER_FUZZ, 0); - input_set_abs_params(input_dev, ABS_RZ, TRIGGER_MIN, TRIGGER_MAX, TRIGGER_FUZZ, 0); - - ret = input_register_polled_device(polled_dev); - if (ret) { - ERR("registering polled device failed"); - goto err_register_polled_dev; - } - - INF("probed controller %i", info->id); - - return 0; + struct clovercon_info *info; + struct input_polled_dev *polled_dev; + struct input_dev *input_dev; + int ret = 0; + + switch (id->driver_data) { + case CLASSIC_ID: + DBG("probing classic controller"); + break; + default: + ERR("unknown id: %lu\n", id->driver_data); + return -EINVAL; + } + + mutex_lock(&con_state_lock); + info = clovercon_info_from_adapter(client->adapter); + if (!info) { + ERR("unkonwn client passed to probe"); + mutex_unlock(&con_state_lock); + return -EINVAL; + } + info->client = client; + i2c_set_clientdata(client, info); + mutex_unlock(&con_state_lock); + + polled_dev = input_allocate_polled_device(); + if (!polled_dev) { + ERR("error allocating polled device"); + return -ENOMEM; + } + + info->dev = polled_dev; + + polled_dev->poll_interval = POLL_INTERVAL; + polled_dev->poll = clovercon_poll; + polled_dev->open = clovercon_open; + polled_dev->private = info; + + input_dev = polled_dev->input; + + //change controller_names initializer when changing MAX_CON_COUNT + BUILD_BUG_ON(MAX_CON_COUNT != ARRAY_SIZE(controller_names)); + input_dev->name = controller_names[info->id - 1]; + input_dev->phys = DRV_NAME"/clovercon"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + set_bit(EV_ABS, input_dev->evbit); + set_bit(ABS_X, input_dev->absbit); + set_bit(ABS_Y, input_dev->absbit); + set_bit(ABS_RX, input_dev->absbit); + set_bit(ABS_RY, input_dev->absbit); + /* + L/R are analog on the classic controller, digital + on the pro with values 0 - 0xf8 + */ + set_bit(ABS_Z, input_dev->absbit); + set_bit(ABS_RZ, input_dev->absbit); + + set_bit(EV_KEY, input_dev->evbit); + set_bit(BTN_X, input_dev->keybit); + set_bit(BTN_B, input_dev->keybit); + set_bit(BTN_A, input_dev->keybit); + set_bit(BTN_Y, input_dev->keybit); + set_bit(BTN_TRIGGER_HAPPY3, input_dev->keybit); // up + set_bit(BTN_TRIGGER_HAPPY4, input_dev->keybit); // down + set_bit(BTN_TRIGGER_HAPPY2, input_dev->keybit); // right + set_bit(BTN_TRIGGER_HAPPY1, input_dev->keybit); // left + set_bit(BTN_TR, input_dev->keybit); + set_bit(BTN_TL, input_dev->keybit); + set_bit(BTN_TR2, input_dev->keybit); + set_bit(BTN_TL2, input_dev->keybit); + set_bit(BTN_SELECT, input_dev->keybit); + set_bit(BTN_START, input_dev->keybit); + set_bit(BTN_MODE, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Y, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); + input_set_abs_params(input_dev, ABS_RX, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); + input_set_abs_params(input_dev, ABS_RY, -STICK_MAX, STICK_MAX, STICK_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Z, TRIGGER_MIN, TRIGGER_MAX, TRIGGER_FUZZ, 0); + input_set_abs_params(input_dev, ABS_RZ, TRIGGER_MIN, TRIGGER_MAX, TRIGGER_FUZZ, 0); + + ret = input_register_polled_device(polled_dev); + if (ret) { + ERR("registering polled device failed"); + goto err_register_polled_dev; + } + + INF("probed controller %i", info->id); + + return 0; err_register_polled_dev: - input_free_polled_device(polled_dev); + input_free_polled_device(polled_dev); - return ret; + return ret; } static int clovercon_remove(struct i2c_client *client) { - struct clovercon_info *info; - struct input_polled_dev *polled_dev; - - mutex_lock(&con_state_lock); - info = i2c_get_clientdata(client); - polled_dev = info->dev; - info->dev = NULL; - mutex_unlock(&con_state_lock); + struct clovercon_info *info; + struct input_polled_dev *polled_dev; + + mutex_lock(&con_state_lock); + info = i2c_get_clientdata(client); + polled_dev = info->dev; + info->dev = NULL; + mutex_unlock(&con_state_lock); - input_unregister_polled_device(polled_dev); - input_free_polled_device(polled_dev); + input_unregister_polled_device(polled_dev); + input_free_polled_device(polled_dev); - INF("removed controller %i", info->id); + INF("removed controller %i", info->id); - return 0; + return 0; } static struct i2c_driver clovercon_driver = { - .driver = { - .name = "clovercon", - .owner = THIS_MODULE, - }, - - .id_table = clovercon_idtable, - .probe = clovercon_probe, - .remove = clovercon_remove, + .driver = { + .name = "clovercon", + .owner = THIS_MODULE, + }, + + .id_table = clovercon_idtable, + .probe = clovercon_probe, + .remove = clovercon_remove, }; static struct i2c_board_info clovercon_i2c_board_info = { - I2C_BOARD_INFO("classic", CONTROLLER_I2C_ADDRESS), + I2C_BOARD_INFO("classic", CONTROLLER_I2C_ADDRESS), }; /* Must be holding con_state_lock */ int clovercon_add_controller(struct clovercon_info *info) { - struct i2c_client *client; - - mutex_unlock(&con_state_lock); - client = i2c_new_device(info->adapter, &clovercon_i2c_board_info); - mutex_lock(&con_state_lock); - if (!client) { - ERR("could not create i2c device"); - return -ENOMEM; - } + struct i2c_client *client; + + mutex_unlock(&con_state_lock); + client = i2c_new_device(info->adapter, &clovercon_i2c_board_info); + mutex_lock(&con_state_lock); + if (!client) { + ERR("could not create i2c device"); + return -ENOMEM; + } #if STATE_DEVICES - info->state_device.minor = MISC_DYNAMIC_MINOR, - sprintf(info->state_device_name, "clovercon%d", info->id); - info->state_device.name = info->state_device_name; - info->state_device.fops = &clovercon_state_fops, - misc_register(&info->state_device); + info->state_device.minor = MISC_DYNAMIC_MINOR, + sprintf(info->state_device_name, "clovercon%d", info->id); + info->state_device.name = info->state_device_name; + info->state_device.fops = &clovercon_state_fops, + misc_register(&info->state_device); #endif - INF("added device for controller %i", info->id); - return 0; + INF("added device for controller %i", info->id); + return 0; } /* Must be holding con_state_lock */ void clovercon_remove_controller(struct clovercon_info *info) { - struct i2c_client *client = info->client; + struct i2c_client *client = info->client; - mutex_unlock(&con_state_lock); - i2c_unregister_device(client); - mutex_lock(&con_state_lock); - info->client = NULL; + mutex_unlock(&con_state_lock); + i2c_unregister_device(client); + mutex_lock(&con_state_lock); + info->client = NULL; #if STATE_DEVICES - misc_deregister(&info->state_device); + misc_deregister(&info->state_device); #endif - INF("removed device for controller %i", info->id); + INF("removed device for controller %i", info->id); } static void clovercon_remove_controllers(void) { - int i; - - mutex_lock(&con_state_lock); - for (i = 0; i < arr_argc / 2; i++) { - if (!con_info_list[i].client) { - continue; - } - clovercon_remove_controller(&con_info_list[i]); - } - mutex_unlock(&con_state_lock); + int i; + + mutex_lock(&con_state_lock); + for (i = 0; i < arr_argc / 2; i++) { + if (!con_info_list[i].client) { + continue; + } + clovercon_remove_controller(&con_info_list[i]); + } + mutex_unlock(&con_state_lock); } static void clovercon_detect_task(struct work_struct *dummy) { - struct clovercon_info *info; - int i; - int val; - - mutex_lock(&detect_task_lock); - //DBG("detect task running"); - mutex_lock(&con_state_lock); - for (i = 0; i < MAX_CON_COUNT; i++) { - info = &con_info_list[i]; - if (!info->detection_active) { - continue; - } - val = gpio_get_value(info->gpio); - //DBG("detect pin value: %i", val); - if (val && !info->client) { - //DBG("detect task adding controller %i", i); - clovercon_add_controller(info); - } else if (!val && info->client) { - //DBG("detect task removing controller %i", i); - clovercon_remove_controller(info); - } - } - mutex_unlock(&con_state_lock); - mutex_unlock(&detect_task_lock); - //DBG("detect task done"); + struct clovercon_info *info; + int i; + int val; + + mutex_lock(&detect_task_lock); + //DBG("detect task running"); + mutex_lock(&con_state_lock); + for (i = 0; i < MAX_CON_COUNT; i++) { + info = &con_info_list[i]; + if (!info->detection_active) { + continue; + } + val = gpio_get_value(info->gpio); + //DBG("detect pin value: %i", val); + if (val && !info->client) { + //DBG("detect task adding controller %i", i); + clovercon_add_controller(info); + } else if (!val && info->client) { + //DBG("detect task removing controller %i", i); + clovercon_remove_controller(info); + } + } + mutex_unlock(&con_state_lock); + mutex_unlock(&detect_task_lock); + //DBG("detect task done"); } #if CLOVERCON_DETECT_USE_IRQ static irqreturn_t clovercon_detect_interrupt(int irq, void* dummy) { - struct clovercon_info *info = clovercon_info_from_irq(irq); - static int initialized = 0; + struct clovercon_info *info = clovercon_info_from_irq(irq); + static int initialized = 0; - if (info == NULL) { - FAST_ERR("could not find controller info associated with irq %i", irq); - return IRQ_HANDLED; - } + if (info == NULL) { + FAST_ERR("could not find controller info associated with irq %i", irq); + return IRQ_HANDLED; + } - if (initialized == 0) { - INIT_DELAYED_WORK(&detect_work, clovercon_detect_task); - initialized = 1; - } else { - PREPARE_DELAYED_WORK(&detect_work, clovercon_detect_task); - } + if (initialized == 0) { + INIT_DELAYED_WORK(&detect_work, clovercon_detect_task); + initialized = 1; + } else { + PREPARE_DELAYED_WORK(&detect_work, clovercon_detect_task); + } - schedule_delayed_work(&detect_work, DETECT_DELAY); + schedule_delayed_work(&detect_work, DETECT_DELAY); - FAST_DBG("interrupt handler on int %i", irq); - return IRQ_HANDLED; + FAST_DBG("interrupt handler on int %i", irq); + return IRQ_HANDLED; } static int clovercon_setup_irq_detect(struct clovercon_info *info) { - int irq; - int ret; - - ret = gpio_to_irq(info->gpio); - if (ret < 0) { - ERR("gpio to irq failed"); - return ret; - } else { - irq = ret; - DBG("irq for gpio %i: %i", info->gpio, irq); - } - - mutex_lock(&con_state_lock); - info->irq = irq; - info->detection_active = 1; - mutex_unlock(&con_state_lock); - - ret = request_irq(ret, clovercon_detect_interrupt, IRQ_TYPE_EDGE_BOTH, "clovercon", NULL); - if (ret) { - ERR("failed to request irq"); - return ret; - } - - return 0; + int irq; + int ret; + + ret = gpio_to_irq(info->gpio); + if (ret < 0) { + ERR("gpio to irq failed"); + return ret; + } else { + irq = ret; + DBG("irq for gpio %i: %i", info->gpio, irq); + } + + mutex_lock(&con_state_lock); + info->irq = irq; + info->detection_active = 1; + mutex_unlock(&con_state_lock); + + ret = request_irq(ret, clovercon_detect_interrupt, IRQ_TYPE_EDGE_BOTH, "clovercon", NULL); + if (ret) { + ERR("failed to request irq"); + return ret; + } + + return 0; } static void clovercon_teardown_irq_detect(struct clovercon_info *info) { - free_irq(info->irq, NULL); - mutex_lock(&con_state_lock); - info->detection_active = 0; - info->irq = INVAL_IRQ; - mutex_unlock(&con_state_lock); + free_irq(info->irq, NULL); + mutex_lock(&con_state_lock); + info->detection_active = 0; + info->irq = INVAL_IRQ; + mutex_unlock(&con_state_lock); } #else //CLOVERCON_DETECT_USE_IRQ static void clovercon_detect_timer_task(struct work_struct *dummy) { - static int initialized = 0; + static int initialized = 0; - if (initialized == 0) { - INIT_DELAYED_WORK(&detect_work, clovercon_detect_timer_task); - initialized = 1; - } else { - PREPARE_DELAYED_WORK(&detect_work, clovercon_detect_timer_task); - } + if (initialized == 0) { + INIT_DELAYED_WORK(&detect_work, clovercon_detect_timer_task); + initialized = 1; + } else { + PREPARE_DELAYED_WORK(&detect_work, clovercon_detect_timer_task); + } - clovercon_detect_task(NULL); - schedule_delayed_work(&detect_work, DETECT_DELAY); + clovercon_detect_task(NULL); + schedule_delayed_work(&detect_work, DETECT_DELAY); } static int clovercon_setup_timer_detect(struct clovercon_info *info) { - static int task_running = 0; + static int task_running = 0; - mutex_lock(&con_state_lock); - info->detection_active = 1; - mutex_unlock(&con_state_lock); - - if (task_running) - return 0; + mutex_lock(&con_state_lock); + info->detection_active = 1; + mutex_unlock(&con_state_lock); + + if (task_running) + return 0; - task_running = 1; - clovercon_detect_timer_task(NULL); + task_running = 1; + clovercon_detect_timer_task(NULL); - return 0; + return 0; } static void clovercon_teardown_timer_detect(struct clovercon_info *info) { - mutex_lock(&con_state_lock); - info->detection_active = 0; - mutex_unlock(&con_state_lock); + mutex_lock(&con_state_lock); + info->detection_active = 0; + mutex_unlock(&con_state_lock); } #endif //CLOVERCON_DETECT_USE_IRQ static int clovercon_setup_i2c(struct clovercon_info *info, int i2c_bus) { - struct i2c_adapter *adapter; + struct i2c_adapter *adapter; - adapter = i2c_get_adapter(i2c_bus); - if (!adapter) { - ERR("could not access i2c bus %i", i2c_bus); - return -EINVAL; - } + adapter = i2c_get_adapter(i2c_bus); + if (!adapter) { + ERR("could not access i2c bus %i", i2c_bus); + return -EINVAL; + } - info->adapter = adapter; + info->adapter = adapter; - return 0; + return 0; } static int clovercon_setup_detection(struct clovercon_info *info, int gpio_pin) { - int ret; + int ret; - ret = gpio_request(gpio_pin, "clovercon_detect"); - if (ret) { - ERR("gpio request failed for pin %i", gpio_pin); - return ret; - } + ret = gpio_request(gpio_pin, "clovercon_detect"); + if (ret) { + ERR("gpio request failed for pin %i", gpio_pin); + return ret; + } - ret = gpio_direction_input(gpio_pin); - if (ret) { - ERR("gpio input direction failed"); - goto err_gpio_cleanup; - } + ret = gpio_direction_input(gpio_pin); + if (ret) { + ERR("gpio input direction failed"); + goto err_gpio_cleanup; + } - info->gpio = gpio_pin; + info->gpio = gpio_pin; - ret = gpio_set_debounce(gpio_pin, DEBOUNCE_VALUE); - if (ret) { - ERR("failed to debounce gpio %i", gpio_pin); - goto err_gpio_cleanup; - } + ret = gpio_set_debounce(gpio_pin, DEBOUNCE_VALUE); + if (ret) { + ERR("failed to debounce gpio %i", gpio_pin); + goto err_gpio_cleanup; + } #if CLOVERCON_DETECT_USE_IRQ - info->irq = INVAL_IRQ; - ret = clovercon_setup_irq_detect(info); + info->irq = INVAL_IRQ; + ret = clovercon_setup_irq_detect(info); #else - ret = clovercon_setup_timer_detect(info); + ret = clovercon_setup_timer_detect(info); #endif - if (ret) { - ERR("controller detection setup failed"); - goto err_detect_cleanup; - } + if (ret) { + ERR("controller detection setup failed"); + goto err_detect_cleanup; + } - return 0; + return 0; err_detect_cleanup: #if CLOVERCON_DETECT_USE_IRQ - clovercon_teardown_irq_detect(info); + clovercon_teardown_irq_detect(info); #else - clovercon_teardown_timer_detect(info); + clovercon_teardown_timer_detect(info); #endif err_gpio_cleanup: - gpio_free(gpio_pin); - return ret; + gpio_free(gpio_pin); + return ret; } static void clovercon_teardown_detection(void) { - int i; - int gpio; + int i; + int gpio; - cancel_delayed_work_sync(&detect_work); + cancel_delayed_work_sync(&detect_work); - for (i = 0; i < MAX_CON_COUNT; i++) { - if (!con_info_list[i].detection_active) { - continue; - } + for (i = 0; i < MAX_CON_COUNT; i++) { + if (!con_info_list[i].detection_active) { + continue; + } #if CLOVERCON_DETECT_USE_IRQ - clovercon_teardown_irq_detect(&con_info_list[i]); + clovercon_teardown_irq_detect(&con_info_list[i]); #else - clovercon_teardown_timer_detect(&con_info_list[i]); + clovercon_teardown_timer_detect(&con_info_list[i]); #endif - mutex_lock(&con_state_lock); - con_info_list[i].adapter = NULL; - gpio = con_info_list[i].gpio; - mutex_unlock(&con_state_lock); - - DBG("Freeing gpio %i", gpio); - gpio_free(gpio); - } + mutex_lock(&con_state_lock); + con_info_list[i].adapter = NULL; + gpio = con_info_list[i].gpio; + mutex_unlock(&con_state_lock); + + DBG("Freeing gpio %i", gpio); + gpio_free(gpio); + } } static int __init clovercon_init(void) { - int i2c_bus; - int gpio_pin; - int ret; - int i; - - for (i = 0; i < MAX_CON_COUNT; i++) { - con_info_list[i].detection_active = 0; - con_info_list[i].id = i + 1; - } - - for (i = 0; i < arr_argc / 2; i++) { - i2c_bus = module_params[2 * i]; - gpio_pin = module_params[2 * i + 1]; - - DBG("initializing controller %i on bus %i, gpio %i", i, i2c_bus, gpio_pin); - - ret = clovercon_setup_i2c(&con_info_list[i], i2c_bus); - if (ret) { - ERR("failed to init controller %i", i); - goto err_controller_cleanup; - } - - if (gpio_pin < 0) { - mutex_lock(&con_state_lock); - ret = clovercon_add_controller(&con_info_list[i]); - mutex_unlock(&con_state_lock); - } else { - ret = clovercon_setup_detection(&con_info_list[i], gpio_pin); - if (ret) { - ERR("failed to init controller %i", i); - goto err_controller_cleanup; - } - } - } - - ret = i2c_add_driver(&clovercon_driver); - if (ret) { - ERR("failed to add driver"); - goto err_controller_cleanup; - } + int i2c_bus; + int gpio_pin; + int ret; + int i; + + for (i = 0; i < MAX_CON_COUNT; i++) { + con_info_list[i].detection_active = 0; + con_info_list[i].id = i + 1; + } -#if VERBOSITY > 0 - misc_register(&clovercon_debug_device); -#endif + for (i = 0; i < arr_argc / 2; i++) { + i2c_bus = module_params[2 * i]; + gpio_pin = module_params[2 * i + 1]; + + DBG("initializing controller %i on bus %i, gpio %i", i, i2c_bus, gpio_pin); + + ret = clovercon_setup_i2c(&con_info_list[i], i2c_bus); + if (ret) { + ERR("failed to init controller %i", i); + goto err_controller_cleanup; + } + + if (gpio_pin < 0) { + mutex_lock(&con_state_lock); + ret = clovercon_add_controller(&con_info_list[i]); + mutex_unlock(&con_state_lock); + } else { + ret = clovercon_setup_detection(&con_info_list[i], gpio_pin); + if (ret) { + ERR("failed to init controller %i", i); + goto err_controller_cleanup; + } + } + } - return 0; + ret = i2c_add_driver(&clovercon_driver); + if (ret) { + ERR("failed to add driver"); + goto err_controller_cleanup; + } + + return 0; err_controller_cleanup: - clovercon_teardown_detection(); - clovercon_remove_controllers(); - return ret; + clovercon_teardown_detection(); + clovercon_remove_controllers(); + return ret; } module_init(clovercon_init); static void __exit clovercon_exit(void) { - DBG("exit"); + DBG("exit"); - clovercon_teardown_detection(); - clovercon_remove_controllers(); - i2c_del_driver(&clovercon_driver); -#if VERBOSITY > 0 - misc_deregister(&clovercon_debug_device); -#endif + clovercon_teardown_detection(); + clovercon_remove_controllers(); + i2c_del_driver(&clovercon_driver); } module_exit(clovercon_exit); diff --git a/clovercon/mod/etc/preinit.d/pc000_clovercon b/clovercon/mod/etc/preinit.d/pc000_clovercon new file mode 100755 index 00000000..07b870b3 --- /dev/null +++ b/clovercon/mod/etc/preinit.d/pc000_clovercon @@ -0,0 +1,35 @@ +if [ "$cfg_clovercon_enabled" == "y" ]; then + +CLOVER_BOARD_NAME=$(cat $mountpoint/etc/clover/boardtype) +MODULE="clvcon" +[ -f "$mountpoint/lib/modules/$(uname -r)/extra/$MODULE.ko" ] || MODULE="clovercon" + +echo "clovercon_hack: starting driver" +echo "home button combination: $cfg_clovercon_home_combination" +echo "autofire: $cfg_clovercon_autofire" +echo "autofire XY: $cfg_clovercon_autofire_xy" +echo "autofire interval: $cfg_clovercon_autofire_interval" +echo "famicom 2nd start workaround: $cfg_clovercon_fc_start" + +case "${CLOVER_BOARD_NAME}" in + fp) + module_params=2,195,1,194 + ;; + ep) + module_params=1,195,2,194 + ;; + dp-hvc) + module_params=1,-1,2,-1 + ;; + dp-shvc) + module_params=1,-1,2,-1 + ;; + dp-nes) + module_params=1,195,2,194 + ;; +esac + +overmount /lib/modules/$(uname -r)/extra/$MODULE.ko +insmod $mountpoint/lib/modules/$(uname -r)/extra/$MODULE.ko module_params=$module_params home_combination=$cfg_clovercon_home_combination autofire=$cfg_clovercon_autofire autofire_xy=$cfg_clovercon_autofire_xy autofire_interval=$cfg_clovercon_autofire_interval fc_start=$cfg_clovercon_fc_start + +fi diff --git a/clovercon/mod/install b/clovercon/mod/install new file mode 100755 index 00000000..5d9bf850 --- /dev/null +++ b/clovercon/mod/install @@ -0,0 +1,6 @@ +cfg_clovercon_enabled='y' +cfg_clovercon_home_combination='0x24' +cfg_clovercon_autofire='1' +cfg_clovercon_autofire_xy='0' +cfg_clovercon_autofire_interval='8' +cfg_clovercon_fc_start='0' diff --git a/clovercon/mod/readme.txt b/clovercon/mod/readme.txt new file mode 100755 index 00000000..ce810620 --- /dev/null +++ b/clovercon/mod/readme.txt @@ -0,0 +1,7 @@ +=== Clovercon Hack === + +This module installs custom clovercon gamepad driver. +Features: +* It allowes to use button combination to open menu +* Autofire +* Start button simulation on second controller (for Famicom Mini) diff --git a/include/linux/compiler-gcc7.h b/include/linux/compiler-gcc7.h new file mode 100644 index 00000000..20600a85 --- /dev/null +++ b/include/linux/compiler-gcc7.h @@ -0,0 +1,63 @@ +#ifndef __LINUX_COMPILER_H +#error "Please don't include directly, include instead." +#endif + +#define __used __attribute__((__used__)) +#define __must_check __attribute__((warn_unused_result)) +#define __compiler_offsetof(a, b) __builtin_offsetof(a, b) + +/* Mark functions as cold. gcc will assume any path leading to a call + to them will be unlikely. This means a lot of manual unlikely()s + are unnecessary now for any paths leading to the usual suspects + like BUG(), printk(), panic() etc. [but let's keep them for now for + older compilers] + Early snapshots of gcc 4.3 don't support this and we can't detect this + in the preprocessor, but we can live with this because they're unreleased. + Maketime probing would be overkill here. + gcc also has a __attribute__((__hot__)) to move hot functions into + a special section, but I don't see any sense in this right now in + the kernel context */ +#define __cold __attribute__((__cold__)) + +#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) + +#ifndef __CHECKER__ +# define __compiletime_warning(message) __attribute__((warning(message))) +# define __compiletime_error(message) __attribute__((error(message))) +#endif /* __CHECKER__ */ + +/* + * Mark a position in code as unreachable. This can be used to + * suppress control flow warnings after asm blocks that transfer + * control elsewhere. + * + * Early snapshots of gcc 4.5 don't support this and we can't detect + * this in the preprocessor, but we can live with this because they're + * unreleased. Really, we need to have autoconf for the kernel. + */ +#define unreachable() __builtin_unreachable() + +/* Mark a function definition as prohibited from being cloned. */ +#define __noclone __attribute__((__noclone__)) + +/* + * Tell the optimizer that something else uses this function or variable. + */ +#define __visible __attribute__((externally_visible)) + +/* + * GCC 'asm goto' miscompiles certain code sequences: + * + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670 + * + * Work it around via a compiler barrier quirk suggested by Jakub Jelinek. + * + * (asm goto is automatically volatile - the naming reflects this.) + */ +#define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0) + +#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP +#define __HAVE_BUILTIN_BSWAP32__ +#define __HAVE_BUILTIN_BSWAP64__ +#define __HAVE_BUILTIN_BSWAP16__ +#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ -- cgit v1.2.3