diff options
44 files changed, 4167 insertions, 0 deletions
diff --git a/.bzr/README b/.bzr/README new file mode 100644 index 0000000..4f8e767 --- /dev/null +++ b/.bzr/README | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | This is a Bazaar control directory. | ||
| 2 | Do not change any files in this directory. | ||
| 3 | See http://bazaar-vcs.org/ for more information about Bazaar. | ||
diff --git a/.bzr/branch-format b/.bzr/branch-format new file mode 100644 index 0000000..9eb09b7 --- /dev/null +++ b/.bzr/branch-format | |||
| @@ -0,0 +1 @@ | |||
| Bazaar-NG meta directory, format 1 | |||
diff --git a/.bzr/branch/branch.conf b/.bzr/branch/branch.conf new file mode 100644 index 0000000..945d68d --- /dev/null +++ b/.bzr/branch/branch.conf | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | parent_location = bzr+ssh://rydberg@bazaar.launchpad.net/%7Erydberg/%2Bjunk/hid-dkms/ | ||
| 2 | push_location = bzr+ssh://rydberg@bazaar.launchpad.net/%7Erydberg/%2Bjunk/hid-dkms/ | ||
diff --git a/.bzr/branch/format b/.bzr/branch/format new file mode 100644 index 0000000..1fc6b88 --- /dev/null +++ b/.bzr/branch/format | |||
| @@ -0,0 +1 @@ | |||
| Bazaar Branch Format 6 (bzr 0.15) | |||
diff --git a/.bzr/branch/last-revision b/.bzr/branch/last-revision new file mode 100644 index 0000000..1d1de7c --- /dev/null +++ b/.bzr/branch/last-revision | |||
| @@ -0,0 +1 @@ | |||
| 9 rydberg@alnilam-20081101141318-o6fj8hf92xznomx5 | |||
diff --git a/.bzr/branch/tags b/.bzr/branch/tags new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.bzr/branch/tags | |||
diff --git a/.bzr/checkout/conflicts b/.bzr/checkout/conflicts new file mode 100644 index 0000000..0dc2d3a --- /dev/null +++ b/.bzr/checkout/conflicts | |||
| @@ -0,0 +1 @@ | |||
| BZR conflict list format 1 | |||
diff --git a/.bzr/checkout/dirstate b/.bzr/checkout/dirstate new file mode 100644 index 0000000..d35fffb --- /dev/null +++ b/.bzr/checkout/dirstate | |||
| Binary files differ | |||
diff --git a/.bzr/checkout/format b/.bzr/checkout/format new file mode 100644 index 0000000..9ffb5e4 --- /dev/null +++ b/.bzr/checkout/format | |||
| @@ -0,0 +1 @@ | |||
| Bazaar Working Tree Format 4 (bzr 0.15) | |||
diff --git a/.bzr/repository/format b/.bzr/repository/format new file mode 100644 index 0000000..520349d --- /dev/null +++ b/.bzr/repository/format | |||
| @@ -0,0 +1 @@ | |||
| Bazaar pack repository format 1 (needs bzr 0.92) | |||
diff --git a/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.iix b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.iix new file mode 100644 index 0000000..6c13d03 --- /dev/null +++ b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.iix | |||
| Binary files differ | |||
diff --git a/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.rix b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.rix new file mode 100644 index 0000000..468b6fa --- /dev/null +++ b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.rix | |||
| Binary files differ | |||
diff --git a/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.six b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.six new file mode 100644 index 0000000..1206f93 --- /dev/null +++ b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.six | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | Bazaar Graph Index 1 | ||
| 2 | node_ref_lists=0 | ||
| 3 | key_elements=1 | ||
| 4 | len=0 | ||
| 5 | |||
diff --git a/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.tix b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.tix new file mode 100644 index 0000000..5da8d3b --- /dev/null +++ b/.bzr/repository/indices/24bcc111992ea05ca0281a0bf87125ca.tix | |||
| Binary files differ | |||
diff --git a/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.iix b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.iix new file mode 100644 index 0000000..80c3d0a --- /dev/null +++ b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.iix | |||
| Binary files differ | |||
diff --git a/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.rix b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.rix new file mode 100644 index 0000000..cbc6da2 --- /dev/null +++ b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.rix | |||
| Binary files differ | |||
diff --git a/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.six b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.six new file mode 100644 index 0000000..1206f93 --- /dev/null +++ b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.six | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | Bazaar Graph Index 1 | ||
| 2 | node_ref_lists=0 | ||
| 3 | key_elements=1 | ||
| 4 | len=0 | ||
| 5 | |||
diff --git a/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.tix b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.tix new file mode 100644 index 0000000..601e3c5 --- /dev/null +++ b/.bzr/repository/indices/31b649a838d14af33c8712436de120f7.tix | |||
| Binary files differ | |||
diff --git a/.bzr/repository/pack-names b/.bzr/repository/pack-names new file mode 100644 index 0000000..447372f --- /dev/null +++ b/.bzr/repository/pack-names | |||
| Binary files differ | |||
diff --git a/.bzr/repository/packs/24bcc111992ea05ca0281a0bf87125ca.pack b/.bzr/repository/packs/24bcc111992ea05ca0281a0bf87125ca.pack new file mode 100644 index 0000000..be71df0 --- /dev/null +++ b/.bzr/repository/packs/24bcc111992ea05ca0281a0bf87125ca.pack | |||
| Binary files differ | |||
diff --git a/.bzr/repository/packs/31b649a838d14af33c8712436de120f7.pack b/.bzr/repository/packs/31b649a838d14af33c8712436de120f7.pack new file mode 100644 index 0000000..49fec63 --- /dev/null +++ b/.bzr/repository/packs/31b649a838d14af33c8712436de120f7.pack | |||
| Binary files differ | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | Modifying and building the DKMS package | ||
| 2 | |||
| 3 | 1. use bzr to sync source with launchpad | ||
| 4 | |||
| 5 | 2. make your changes | ||
| 6 | |||
| 7 | 3. modify the VERSION variable in Makefile, then do | ||
| 8 | |||
| 9 | make bump | ||
| 10 | |||
| 11 | 4. add a new entry to debian/changelog, matching VERSION | ||
| 12 | |||
| 13 | 5. build binary package for testing with | ||
| 14 | |||
| 15 | dpkg-buildpackage | ||
| 16 | |||
| 17 | 6. clean the checkout with | ||
| 18 | |||
| 19 | make distclean | ||
| 20 | |||
| 21 | 7. build source package for upload with | ||
| 22 | |||
| 23 | debuild -S -sa | ||
| 24 | |||
| 25 | 8. upload packacke to ppa with (see ~/.dput.cf) | ||
| 26 | |||
| 27 | dput your-mactel-ppa-dput-entry name-and-version_source.changes | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ba277e9 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | NAME = hid | ||
| 2 | VERSION = 0.10.2 | ||
| 3 | SRC = usr/src | ||
| 4 | DKMSSRC = $(SRC)/dkms_source_tree | ||
| 5 | |||
| 6 | TARBALL = $(NAME)-$(VERSION).dkms.tar.gz | ||
| 7 | PATCH = ./scripts/patch-name-version.sh $(NAME) $(VERSION) | ||
| 8 | |||
| 9 | KERNSRC = $(HOME)/code/kernel/intrepid | ||
| 10 | |||
| 11 | all: $(SRC)/$(TARBALL) | ||
| 12 | |||
| 13 | $(SRC)/$(TARBALL): $(DKMSSRC)/$(NAME)-input.c | ||
| 14 | (cd $(SRC); tar cvfz $(TARBALL) dkms*) | ||
| 15 | |||
| 16 | clean: | ||
| 17 | rm -f $(SRC)/$(TARBALL) | ||
| 18 | |||
| 19 | distclean: clean | ||
| 20 | rm -rf *-stamp debian/$(NAME)-dkms debian/*.log debian/files | ||
| 21 | |||
| 22 | install: $(SRC)/$(TARBALL) | ||
| 23 | install -d "$(DESTDIR)/$(SRC)" | ||
| 24 | (cd $(SRC); install -m 644 $(NAME)-$(VERSION).dkms.tar.gz "$(DESTDIR)/$(SRC)") | ||
| 25 | |||
| 26 | bump: | ||
| 27 | $(PATCH) debian/postinst | ||
| 28 | $(PATCH) debian/postrm | ||
| 29 | $(PATCH) debian/prerm | ||
| 30 | $(PATCH) debian/rules | ||
| 31 | $(PATCH) debian/postinst | ||
| 32 | $(PATCH) $(DKMSSRC)/dkms.conf | ||
| 33 | chmod 0755 debian/rules | ||
| 34 | |||
| 35 | fetch: | ||
| 36 | cp $(KERNSRC)/drivers/hid/*.c $(DKMSSRC) | ||
| 37 | (cd $(DKMSSRC); cat patches/*.patch | patch -p3) | ||
| 38 | uname -r > $(DKMSSRC)/KERNEL_SOURCE_VERSION | ||
diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..d1b0b68 --- /dev/null +++ b/debian/README.Debian | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | hid DKMS module for Debian | ||
| 2 | |||
| 3 | This package adds supplementary media key mappings. | ||
| 4 | |||
| 5 | * Install the debian package (uses dkms) | ||
| 6 | |||
| 7 | * Reboot | ||
| 8 | |||
| 9 | Enjoy! | ||
| 10 | |||
| 11 | Henrik Rydberg <rydberg@euromail.se> | ||
diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..2c2bd40 --- /dev/null +++ b/debian/changelog | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | hid-dkms (0.10.2) unstable; urgency=low | ||
| 2 | |||
| 3 | * Map FN+ENTER to INSERT | ||
| 4 | * Automatically update the ramdisk | ||
| 5 | |||
| 6 | -- Henrik Rydberg <rydberg@euromail.se> Wed, 29 Oct 2008 20:33:08 +0100 | ||
| 7 | |||
| 8 | hid-dkms (0.10.1) unstable; urgency=low | ||
| 9 | |||
| 10 | * Map Expose/Dashboard to CYCLEWINDOWS/CALC function keys | ||
| 11 | |||
| 12 | -- Henrik Rydberg <rydberg@euromail.se> Wed, 29 Oct 2008 20:33:08 +0100 | ||
diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat | |||
| @@ -0,0 +1 @@ | |||
| 5 | |||
diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..9901739 --- /dev/null +++ b/debian/control | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | Source: hid-dkms | ||
| 2 | Section: misc | ||
| 3 | Priority: optional | ||
| 4 | Maintainer: Henrik Rydberg <rydberg@euromail.se> | ||
| 5 | Build-Depends: debhelper (>= 5) | ||
| 6 | Standards-Version: 3.7.2 | ||
| 7 | Homepage: https://launchpad.net/~rydberg | ||
| 8 | |||
| 9 | Package: hid-dkms | ||
| 10 | Architecture: all | ||
| 11 | Depends: dkms (>= 1.95), bash (>> 1.99) | ||
| 12 | Description: Supplementary hid support | ||
| 13 | This driver supplements the apple function key mapping with useful | ||
| 14 | defaults for the Expose (FN+F3), Dashboard (FN+F4) and Insert | ||
| 15 | (FN+Enter) keys. | ||
diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..89821cf --- /dev/null +++ b/debian/copyright | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | The hid-dkms package was created using DKMS <dkms-devel@dell.com> | ||
| 2 | |||
| 3 | Source URL: https://launchpad.net/~rydberg | ||
| 4 | |||
| 5 | Upstream Author: Intrepid kernel 2.6.27 | ||
| 6 | |||
| 7 | Copyright: | ||
| 8 | Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) | ||
| 9 | |||
| 10 | See the hid-input.c kernel source for a full list of credits. | ||
| 11 | |||
| 12 | License: | ||
| 13 | |||
| 14 | This program is free software; you can redistribute it and/or modify | ||
| 15 | it under the terms of the GNU General Public License as published by | ||
| 16 | the Free Software Foundation; either version 2 of the License, or | ||
| 17 | (at your option) any later version. | ||
| 18 | |||
| 19 | This program is distributed in the hope that it will be useful, | ||
| 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 22 | GNU General Public License for more details. | ||
| 23 | |||
| 24 | You should have received a copy of the GNU General Public License | ||
| 25 | along with this program; if not, write to the Free Software | ||
| 26 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 27 | |||
| 28 | Packaging: | ||
| 29 | Copyright (C) 2008 by Henrik Rydberg <rydberg@euromail.se> | ||
| 30 | released under GPL 2 | ||
diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..b601f22 --- /dev/null +++ b/debian/dirs | |||
| @@ -0,0 +1 @@ | |||
| usr/src | |||
diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..30e9c0c --- /dev/null +++ b/debian/postinst | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | set -e | ||
| 4 | |||
| 5 | NAME=hid | ||
| 6 | VERSION=0.10.2 | ||
| 7 | |||
| 8 | case "$1" in | ||
| 9 | configure) | ||
| 10 | # Determine current arch / kernel | ||
| 11 | c_arch=`uname -m` | ||
| 12 | c_kern=`uname -r` | ||
| 13 | |||
| 14 | if [ -e "/var/lib/dkms/$NAME/$VERSION" ]; then | ||
| 15 | echo "Removing old $NAME-$VERSION DKMS files..." | ||
| 16 | dkms remove -m $NAME -v $VERSION --all | ||
| 17 | fi | ||
| 18 | |||
| 19 | echo "Loading new $NAME-$VERSION DKMS files..." | ||
| 20 | #dkms add -m $NAME -v $VERSION | ||
| 21 | dkms ldtarball --archive "/usr/src/$NAME-$VERSION.dkms.tar.gz" | ||
| 22 | rm -f "/usr/src/$NAME-$VERSION.dkms.tar.gz" | ||
| 23 | |||
| 24 | echo "Installing prebuilt kernel module binaries (if any)" | ||
| 25 | set +e | ||
| 26 | IFS=' | ||
| 27 | ' | ||
| 28 | for kern in `dkms status -m $NAME -v $VERSION -a $c_arch | grep ": built" | awk {'print $3'} | sed 's/,$//'`; do | ||
| 29 | echo "Trying kernel: $kern" | ||
| 30 | dkms install --force -m $NAME -v $VERSION -k $kern -a $c_arch | ||
| 31 | done | ||
| 32 | unset IFS | ||
| 33 | set -e | ||
| 34 | |||
| 35 | # If none installed, install. | ||
| 36 | ##if [ `dkms status -m $NAME -v $VERSION -k $c_kern -a $c_arch | grep -c ": installed"` -eq 0 ]; then | ||
| 37 | dkms_status=`dkms status -m $NAME -v $VERSION -k $c_kern -a $c_arch` | ||
| 38 | if [ `echo $dkms_status | grep -c ": installed"` -eq 0 ]; then | ||
| 39 | if [ `echo $c_kern | grep -c "BOOT"` -eq 0 ] && [ -e "/lib/modules/$c_kern/build/include" ]; then | ||
| 40 | # Only build if we need to. | ||
| 41 | if [ `echo $dkms_status | grep -c ": built"` -eq 0 ]; then | ||
| 42 | echo "Building module..." | ||
| 43 | dkms build -m $NAME -v $VERSION | ||
| 44 | fi | ||
| 45 | echo "Installing module..." | ||
| 46 | dkms install -m $NAME -v $VERSION | ||
| 47 | elif [ `echo $c_kern | grep -c "BOOT"` -gt 0 ]; then | ||
| 48 | echo -e "" | ||
| 49 | echo -e "Module build for the currently running kernel was skipped since you" | ||
| 50 | echo -e "are running a BOOT variant of the kernel." | ||
| 51 | else | ||
| 52 | echo -e "" | ||
| 53 | echo -e "Module build for the currently running kernel was skipped since the" | ||
| 54 | echo -e "kernel source for this kernel does not seem to be installed." | ||
| 55 | fi | ||
| 56 | fi | ||
| 57 | ;; | ||
| 58 | |||
| 59 | abort-upgrade|abort-remove|abort-deconfigure) | ||
| 60 | ;; | ||
| 61 | |||
| 62 | *) | ||
| 63 | echo "postinst called with unknown argument \`$1'" >&2 | ||
| 64 | exit 1 | ||
| 65 | ;; | ||
| 66 | esac | ||
| 67 | |||
| 68 | #DEBHELPER# | ||
| 69 | |||
| 70 | exit 0 | ||
| 71 | |||
diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..02d3578 --- /dev/null +++ b/debian/postrm | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | NAME=hid | ||
| 4 | VERSION=0.10.2 | ||
| 5 | |||
| 6 | set -e | ||
| 7 | |||
| 8 | case "$1" in | ||
| 9 | purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) | ||
| 10 | moddir="/lib/modules/`uname -r`/kernel/" | ||
| 11 | depmod | ||
| 12 | if [ -d "/usr/src/$NAME-$VERSION" ]; then | ||
| 13 | echo "Removing old module source..." | ||
| 14 | rm -rf "/usr/src/$NAME-$VERSION" | ||
| 15 | fi | ||
| 16 | ;; | ||
| 17 | |||
| 18 | *) | ||
| 19 | echo "postrm called with unknown argument \`$1'" >&2 | ||
| 20 | exit 1 | ||
| 21 | ;; | ||
| 22 | esac | ||
| 23 | |||
| 24 | #DEBHELPER# | ||
| 25 | |||
| 26 | exit 0 | ||
| 27 | |||
diff --git a/debian/prerm b/debian/prerm new file mode 100644 index 0000000..9871780 --- /dev/null +++ b/debian/prerm | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | NAME=hid | ||
| 4 | VERSION=0.10.2 | ||
| 5 | |||
| 6 | set -e | ||
| 7 | |||
| 8 | case "$1" in | ||
| 9 | remove|upgrade|deconfigure) | ||
| 10 | if [ "`dkms status -m $NAME`" ]; then | ||
| 11 | dkms remove -m $NAME -v $VERSION --all | ||
| 12 | fi | ||
| 13 | ;; | ||
| 14 | |||
| 15 | failed-upgrade) | ||
| 16 | ;; | ||
| 17 | |||
| 18 | *) | ||
| 19 | echo "prerm called with unknown argument \`$1'" >&2 | ||
| 20 | exit 1 | ||
| 21 | ;; | ||
| 22 | esac | ||
| 23 | |||
| 24 | #DEBHELPER# | ||
| 25 | |||
| 26 | exit 0 | ||
| 27 | |||
| 28 | |||
diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..04f3312 --- /dev/null +++ b/debian/rules | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #!/usr/bin/make -f | ||
| 2 | # -*- makefile -*- | ||
| 3 | |||
| 4 | # Uncomment this to turn on verbose mode. | ||
| 5 | #export DH_VERBOSE=1 | ||
| 6 | |||
| 7 | NAME=hid | ||
| 8 | VERSION=0.10.2 | ||
| 9 | |||
| 10 | configure: configure-stamp | ||
| 11 | configure-stamp: | ||
| 12 | dh_testdir | ||
| 13 | touch configure-stamp | ||
| 14 | |||
| 15 | |||
| 16 | build: build-stamp | ||
| 17 | |||
| 18 | build-stamp: configure-stamp | ||
| 19 | dh_testdir | ||
| 20 | $(MAKE) | ||
| 21 | touch $@ | ||
| 22 | |||
| 23 | clean: | ||
| 24 | dh_testdir | ||
| 25 | dh_testroot | ||
| 26 | rm -f build-stamp configure-stamp | ||
| 27 | -$(MAKE) clean | ||
| 28 | dh_clean | ||
| 29 | |||
| 30 | install: build | ||
| 31 | dh_testdir | ||
| 32 | dh_testroot | ||
| 33 | dh_clean -k | ||
| 34 | dh_installdirs | ||
| 35 | $(MAKE) DESTDIR=$(CURDIR)/debian/$(NAME)-dkms NAME=$(NAME) VERSION=$(VERSION) install | ||
| 36 | |||
| 37 | binary-arch: build install | ||
| 38 | |||
| 39 | binary-indep: build install | ||
| 40 | dh_testdir | ||
| 41 | dh_testroot | ||
| 42 | dh_link | ||
| 43 | dh_strip | ||
| 44 | dh_compress | ||
| 45 | dh_fixperms | ||
| 46 | dh_installdeb | ||
| 47 | dh_shlibdeps | ||
| 48 | dh_gencontrol | ||
| 49 | dh_md5sums | ||
| 50 | dh_builddeb | ||
| 51 | |||
| 52 | binary: binary-indep binary-arch | ||
| 53 | .PHONY: build clean binary-indep binary-arch binary install configure | ||
diff --git a/scripts/patch-name-version.sh b/scripts/patch-name-version.sh new file mode 100755 index 0000000..d1a7796 --- /dev/null +++ b/scripts/patch-name-version.sh | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | #1: name | ||
| 3 | #2: version | ||
| 4 | #3: file | ||
| 5 | cat $3 \ | ||
| 6 | | sed "s/^NAME=.*/NAME=$1/" \ | ||
| 7 | | sed "s/^VERSION=.*/VERSION=$2/" \ | ||
| 8 | > /tmp/$1.$$ | ||
| 9 | mv /tmp/$1.$$ $3 | ||
diff --git a/usr/src/dkms_main_tree/dkms_dbversion b/usr/src/dkms_main_tree/dkms_dbversion new file mode 100644 index 0000000..227cea2 --- /dev/null +++ b/usr/src/dkms_main_tree/dkms_dbversion | |||
| @@ -0,0 +1 @@ | |||
| 2.0.0 | |||
diff --git a/usr/src/dkms_source_tree/KERNEL_SOURCE_VERSION b/usr/src/dkms_source_tree/KERNEL_SOURCE_VERSION new file mode 100644 index 0000000..9cd2822 --- /dev/null +++ b/usr/src/dkms_source_tree/KERNEL_SOURCE_VERSION | |||
| @@ -0,0 +1 @@ | |||
| 2.6.27-7-generic | |||
diff --git a/usr/src/dkms_source_tree/Makefile b/usr/src/dkms_source_tree/Makefile new file mode 100644 index 0000000..2537695 --- /dev/null +++ b/usr/src/dkms_source_tree/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the HID driver | ||
| 3 | # | ||
| 4 | hid-objs := hid-core.o hid-input.o hid-input-quirks.o | ||
| 5 | |||
| 6 | obj-$(CONFIG_HID) += hid.o | ||
| 7 | |||
| 8 | hid-$(CONFIG_HID_DEBUG) += hid-debug.o | ||
| 9 | hid-$(CONFIG_HIDRAW) += hidraw.o | ||
diff --git a/usr/src/dkms_source_tree/dkms.conf b/usr/src/dkms_source_tree/dkms.conf new file mode 100644 index 0000000..941ae52 --- /dev/null +++ b/usr/src/dkms_source_tree/dkms.conf | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | NAME=hid | ||
| 2 | VERSION=0.10.2 | ||
| 3 | PACKAGE_NAME="$NAME" | ||
| 4 | PACKAGE_VERSION="$VERSION" | ||
| 5 | MAKE[0]="make -C ${kernel_source_dir} | ||
| 6 | SUBDIRS=${dkms_tree}/${NAME}/${VERSION}/build modules" | ||
| 7 | BUILT_MODULE_NAME[0]="hid" | ||
| 8 | DEST_MODULE_LOCATION[0]="/kernel/drivers/hid" | ||
| 9 | REMAKE_INITRD=y | ||
diff --git a/usr/src/dkms_source_tree/hid-core.c b/usr/src/dkms_source_tree/hid-core.c new file mode 100644 index 0000000..426ac5a --- /dev/null +++ b/usr/src/dkms_source_tree/hid-core.c | |||
| @@ -0,0 +1,1023 @@ | |||
| 1 | /* | ||
| 2 | * HID support for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 1999 Andreas Gal | ||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | ||
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | ||
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | ||
| 8 | */ | ||
| 9 | |||
| 10 | /* | ||
| 11 | * This program is free software; you can redistribute it and/or modify it | ||
| 12 | * under the terms of the GNU General Public License as published by the Free | ||
| 13 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 14 | * any later version. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/list.h> | ||
| 22 | #include <linux/mm.h> | ||
| 23 | #include <linux/spinlock.h> | ||
| 24 | #include <asm/unaligned.h> | ||
| 25 | #include <asm/byteorder.h> | ||
| 26 | #include <linux/input.h> | ||
| 27 | #include <linux/wait.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | #include <linux/sched.h> | ||
| 30 | |||
| 31 | #include <linux/hid.h> | ||
| 32 | #include <linux/hiddev.h> | ||
| 33 | #include <linux/hid-debug.h> | ||
| 34 | #include <linux/hidraw.h> | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Version Information | ||
| 38 | */ | ||
| 39 | |||
| 40 | #define DRIVER_VERSION "v2.6" | ||
| 41 | #define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" | ||
| 42 | #define DRIVER_DESC "HID core driver" | ||
| 43 | #define DRIVER_LICENSE "GPL" | ||
| 44 | |||
| 45 | #ifdef CONFIG_HID_DEBUG | ||
| 46 | int hid_debug = 0; | ||
| 47 | module_param_named(debug, hid_debug, int, 0600); | ||
| 48 | MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)"); | ||
| 49 | EXPORT_SYMBOL_GPL(hid_debug); | ||
| 50 | #endif | ||
| 51 | |||
| 52 | /* | ||
| 53 | * Register a new report for a device. | ||
| 54 | */ | ||
| 55 | |||
| 56 | static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id) | ||
| 57 | { | ||
| 58 | struct hid_report_enum *report_enum = device->report_enum + type; | ||
| 59 | struct hid_report *report; | ||
| 60 | |||
| 61 | if (report_enum->report_id_hash[id]) | ||
| 62 | return report_enum->report_id_hash[id]; | ||
| 63 | |||
| 64 | if (!(report = kzalloc(sizeof(struct hid_report), GFP_KERNEL))) | ||
| 65 | return NULL; | ||
| 66 | |||
| 67 | if (id != 0) | ||
| 68 | report_enum->numbered = 1; | ||
| 69 | |||
| 70 | report->id = id; | ||
| 71 | report->type = type; | ||
| 72 | report->size = 0; | ||
| 73 | report->device = device; | ||
| 74 | report_enum->report_id_hash[id] = report; | ||
| 75 | |||
| 76 | list_add_tail(&report->list, &report_enum->report_list); | ||
| 77 | |||
| 78 | return report; | ||
| 79 | } | ||
| 80 | |||
| 81 | /* | ||
| 82 | * Register a new field for this report. | ||
| 83 | */ | ||
| 84 | |||
| 85 | static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) | ||
| 86 | { | ||
| 87 | struct hid_field *field; | ||
| 88 | |||
| 89 | if (report->maxfield == HID_MAX_FIELDS) { | ||
| 90 | dbg_hid("too many fields in report\n"); | ||
| 91 | return NULL; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (!(field = kzalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage) | ||
| 95 | + values * sizeof(unsigned), GFP_KERNEL))) return NULL; | ||
| 96 | |||
| 97 | field->index = report->maxfield++; | ||
| 98 | report->field[field->index] = field; | ||
| 99 | field->usage = (struct hid_usage *)(field + 1); | ||
| 100 | field->value = (s32 *)(field->usage + usages); | ||
| 101 | field->report = report; | ||
| 102 | |||
| 103 | return field; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* | ||
| 107 | * Open a collection. The type/usage is pushed on the stack. | ||
| 108 | */ | ||
| 109 | |||
| 110 | static int open_collection(struct hid_parser *parser, unsigned type) | ||
| 111 | { | ||
| 112 | struct hid_collection *collection; | ||
| 113 | unsigned usage; | ||
| 114 | |||
| 115 | usage = parser->local.usage[0]; | ||
| 116 | |||
| 117 | if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { | ||
| 118 | dbg_hid("collection stack overflow\n"); | ||
| 119 | return -1; | ||
| 120 | } | ||
| 121 | |||
| 122 | if (parser->device->maxcollection == parser->device->collection_size) { | ||
| 123 | collection = kmalloc(sizeof(struct hid_collection) * | ||
| 124 | parser->device->collection_size * 2, GFP_KERNEL); | ||
| 125 | if (collection == NULL) { | ||
| 126 | dbg_hid("failed to reallocate collection array\n"); | ||
| 127 | return -1; | ||
| 128 | } | ||
| 129 | memcpy(collection, parser->device->collection, | ||
| 130 | sizeof(struct hid_collection) * | ||
| 131 | parser->device->collection_size); | ||
| 132 | memset(collection + parser->device->collection_size, 0, | ||
| 133 | sizeof(struct hid_collection) * | ||
| 134 | parser->device->collection_size); | ||
| 135 | kfree(parser->device->collection); | ||
| 136 | parser->device->collection = collection; | ||
| 137 | parser->device->collection_size *= 2; | ||
| 138 | } | ||
| 139 | |||
| 140 | parser->collection_stack[parser->collection_stack_ptr++] = | ||
| 141 | parser->device->maxcollection; | ||
| 142 | |||
| 143 | collection = parser->device->collection + | ||
| 144 | parser->device->maxcollection++; | ||
| 145 | collection->type = type; | ||
| 146 | collection->usage = usage; | ||
| 147 | collection->level = parser->collection_stack_ptr - 1; | ||
| 148 | |||
| 149 | if (type == HID_COLLECTION_APPLICATION) | ||
| 150 | parser->device->maxapplication++; | ||
| 151 | |||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* | ||
| 156 | * Close a collection. | ||
| 157 | */ | ||
| 158 | |||
| 159 | static int close_collection(struct hid_parser *parser) | ||
| 160 | { | ||
| 161 | if (!parser->collection_stack_ptr) { | ||
| 162 | dbg_hid("collection stack underflow\n"); | ||
| 163 | return -1; | ||
| 164 | } | ||
| 165 | parser->collection_stack_ptr--; | ||
| 166 | return 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | /* | ||
| 170 | * Climb up the stack, search for the specified collection type | ||
| 171 | * and return the usage. | ||
| 172 | */ | ||
| 173 | |||
| 174 | static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) | ||
| 175 | { | ||
| 176 | int n; | ||
| 177 | for (n = parser->collection_stack_ptr - 1; n >= 0; n--) | ||
| 178 | if (parser->device->collection[parser->collection_stack[n]].type == type) | ||
| 179 | return parser->device->collection[parser->collection_stack[n]].usage; | ||
| 180 | return 0; /* we know nothing about this usage type */ | ||
| 181 | } | ||
| 182 | |||
| 183 | /* | ||
| 184 | * Add a usage to the temporary parser table. | ||
| 185 | */ | ||
| 186 | |||
| 187 | static int hid_add_usage(struct hid_parser *parser, unsigned usage) | ||
| 188 | { | ||
| 189 | if (parser->local.usage_index >= HID_MAX_USAGES) { | ||
| 190 | dbg_hid("usage index exceeded\n"); | ||
| 191 | return -1; | ||
| 192 | } | ||
| 193 | parser->local.usage[parser->local.usage_index] = usage; | ||
| 194 | parser->local.collection_index[parser->local.usage_index] = | ||
| 195 | parser->collection_stack_ptr ? | ||
| 196 | parser->collection_stack[parser->collection_stack_ptr - 1] : 0; | ||
| 197 | parser->local.usage_index++; | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* | ||
| 202 | * Register a new field for this report. | ||
| 203 | */ | ||
| 204 | |||
| 205 | static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags) | ||
| 206 | { | ||
| 207 | struct hid_report *report; | ||
| 208 | struct hid_field *field; | ||
| 209 | int usages; | ||
| 210 | unsigned offset; | ||
| 211 | int i; | ||
| 212 | |||
| 213 | if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) { | ||
| 214 | dbg_hid("hid_register_report failed\n"); | ||
| 215 | return -1; | ||
| 216 | } | ||
| 217 | |||
| 218 | if (parser->global.logical_maximum < parser->global.logical_minimum) { | ||
| 219 | dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum); | ||
| 220 | return -1; | ||
| 221 | } | ||
| 222 | |||
| 223 | offset = report->size; | ||
| 224 | report->size += parser->global.report_size * parser->global.report_count; | ||
| 225 | |||
| 226 | if (!parser->local.usage_index) /* Ignore padding fields */ | ||
| 227 | return 0; | ||
| 228 | |||
| 229 | usages = max_t(int, parser->local.usage_index, parser->global.report_count); | ||
| 230 | |||
| 231 | if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL) | ||
| 232 | return 0; | ||
| 233 | |||
| 234 | field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); | ||
| 235 | field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); | ||
| 236 | field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); | ||
| 237 | |||
| 238 | for (i = 0; i < usages; i++) { | ||
| 239 | int j = i; | ||
| 240 | /* Duplicate the last usage we parsed if we have excess values */ | ||
| 241 | if (i >= parser->local.usage_index) | ||
| 242 | j = parser->local.usage_index - 1; | ||
| 243 | field->usage[i].hid = parser->local.usage[j]; | ||
| 244 | field->usage[i].collection_index = | ||
| 245 | parser->local.collection_index[j]; | ||
| 246 | } | ||
| 247 | |||
| 248 | field->maxusage = usages; | ||
| 249 | field->flags = flags; | ||
| 250 | field->report_offset = offset; | ||
| 251 | field->report_type = report_type; | ||
| 252 | field->report_size = parser->global.report_size; | ||
| 253 | field->report_count = parser->global.report_count; | ||
| 254 | field->logical_minimum = parser->global.logical_minimum; | ||
| 255 | field->logical_maximum = parser->global.logical_maximum; | ||
| 256 | field->physical_minimum = parser->global.physical_minimum; | ||
| 257 | field->physical_maximum = parser->global.physical_maximum; | ||
| 258 | field->unit_exponent = parser->global.unit_exponent; | ||
| 259 | field->unit = parser->global.unit; | ||
| 260 | |||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | |||
| 264 | /* | ||
| 265 | * Read data value from item. | ||
| 266 | */ | ||
| 267 | |||
| 268 | static u32 item_udata(struct hid_item *item) | ||
| 269 | { | ||
| 270 | switch (item->size) { | ||
| 271 | case 1: return item->data.u8; | ||
| 272 | case 2: return item->data.u16; | ||
| 273 | case 4: return item->data.u32; | ||
| 274 | } | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | static s32 item_sdata(struct hid_item *item) | ||
| 279 | { | ||
| 280 | switch (item->size) { | ||
| 281 | case 1: return item->data.s8; | ||
| 282 | case 2: return item->data.s16; | ||
| 283 | case 4: return item->data.s32; | ||
| 284 | } | ||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | /* | ||
| 289 | * Process a global item. | ||
| 290 | */ | ||
| 291 | |||
| 292 | static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) | ||
| 293 | { | ||
| 294 | switch (item->tag) { | ||
| 295 | |||
| 296 | case HID_GLOBAL_ITEM_TAG_PUSH: | ||
| 297 | |||
| 298 | if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { | ||
| 299 | dbg_hid("global enviroment stack overflow\n"); | ||
| 300 | return -1; | ||
| 301 | } | ||
| 302 | |||
| 303 | memcpy(parser->global_stack + parser->global_stack_ptr++, | ||
| 304 | &parser->global, sizeof(struct hid_global)); | ||
| 305 | return 0; | ||
| 306 | |||
| 307 | case HID_GLOBAL_ITEM_TAG_POP: | ||
| 308 | |||
| 309 | if (!parser->global_stack_ptr) { | ||
| 310 | dbg_hid("global enviroment stack underflow\n"); | ||
| 311 | return -1; | ||
| 312 | } | ||
| 313 | |||
| 314 | memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr, | ||
| 315 | sizeof(struct hid_global)); | ||
| 316 | return 0; | ||
| 317 | |||
| 318 | case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: | ||
| 319 | parser->global.usage_page = item_udata(item); | ||
| 320 | return 0; | ||
| 321 | |||
| 322 | case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: | ||
| 323 | parser->global.logical_minimum = item_sdata(item); | ||
| 324 | return 0; | ||
| 325 | |||
| 326 | case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: | ||
| 327 | if (parser->global.logical_minimum < 0) | ||
| 328 | parser->global.logical_maximum = item_sdata(item); | ||
| 329 | else | ||
| 330 | parser->global.logical_maximum = item_udata(item); | ||
| 331 | return 0; | ||
| 332 | |||
| 333 | case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: | ||
| 334 | parser->global.physical_minimum = item_sdata(item); | ||
| 335 | return 0; | ||
| 336 | |||
| 337 | case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: | ||
| 338 | if (parser->global.physical_minimum < 0) | ||
| 339 | parser->global.physical_maximum = item_sdata(item); | ||
| 340 | else | ||
| 341 | parser->global.physical_maximum = item_udata(item); | ||
| 342 | return 0; | ||
| 343 | |||
| 344 | case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: | ||
| 345 | parser->global.unit_exponent = item_sdata(item); | ||
| 346 | return 0; | ||
| 347 | |||
| 348 | case HID_GLOBAL_ITEM_TAG_UNIT: | ||
| 349 | parser->global.unit = item_udata(item); | ||
| 350 | return 0; | ||
| 351 | |||
| 352 | case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: | ||
| 353 | if ((parser->global.report_size = item_udata(item)) > 32) { | ||
| 354 | dbg_hid("invalid report_size %d\n", parser->global.report_size); | ||
| 355 | return -1; | ||
| 356 | } | ||
| 357 | return 0; | ||
| 358 | |||
| 359 | case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: | ||
| 360 | if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) { | ||
| 361 | dbg_hid("invalid report_count %d\n", parser->global.report_count); | ||
| 362 | return -1; | ||
| 363 | } | ||
| 364 | return 0; | ||
| 365 | |||
| 366 | case HID_GLOBAL_ITEM_TAG_REPORT_ID: | ||
| 367 | if ((parser->global.report_id = item_udata(item)) == 0) { | ||
| 368 | dbg_hid("report_id 0 is invalid\n"); | ||
| 369 | return -1; | ||
| 370 | } | ||
| 371 | return 0; | ||
| 372 | |||
| 373 | default: | ||
| 374 | dbg_hid("unknown global tag 0x%x\n", item->tag); | ||
| 375 | return -1; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | /* | ||
| 380 | * Process a local item. | ||
| 381 | */ | ||
| 382 | |||
| 383 | static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) | ||
| 384 | { | ||
| 385 | __u32 data; | ||
| 386 | unsigned n; | ||
| 387 | |||
| 388 | if (item->size == 0) { | ||
| 389 | dbg_hid("item data expected for local item\n"); | ||
| 390 | return -1; | ||
| 391 | } | ||
| 392 | |||
| 393 | data = item_udata(item); | ||
| 394 | |||
| 395 | switch (item->tag) { | ||
| 396 | |||
| 397 | case HID_LOCAL_ITEM_TAG_DELIMITER: | ||
| 398 | |||
| 399 | if (data) { | ||
| 400 | /* | ||
| 401 | * We treat items before the first delimiter | ||
| 402 | * as global to all usage sets (branch 0). | ||
| 403 | * In the moment we process only these global | ||
| 404 | * items and the first delimiter set. | ||
| 405 | */ | ||
| 406 | if (parser->local.delimiter_depth != 0) { | ||
| 407 | dbg_hid("nested delimiters\n"); | ||
| 408 | return -1; | ||
| 409 | } | ||
| 410 | parser->local.delimiter_depth++; | ||
| 411 | parser->local.delimiter_branch++; | ||
| 412 | } else { | ||
| 413 | if (parser->local.delimiter_depth < 1) { | ||
| 414 | dbg_hid("bogus close delimiter\n"); | ||
| 415 | return -1; | ||
| 416 | } | ||
| 417 | parser->local.delimiter_depth--; | ||
| 418 | } | ||
| 419 | return 1; | ||
| 420 | |||
| 421 | case HID_LOCAL_ITEM_TAG_USAGE: | ||
| 422 | |||
| 423 | if (parser->local.delimiter_branch > 1) { | ||
| 424 | dbg_hid("alternative usage ignored\n"); | ||
| 425 | return 0; | ||
| 426 | } | ||
| 427 | |||
| 428 | if (item->size <= 2) | ||
| 429 | data = (parser->global.usage_page << 16) + data; | ||
| 430 | |||
| 431 | return hid_add_usage(parser, data); | ||
| 432 | |||
| 433 | case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: | ||
| 434 | |||
| 435 | if (parser->local.delimiter_branch > 1) { | ||
| 436 | dbg_hid("alternative usage ignored\n"); | ||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (item->size <= 2) | ||
| 441 | data = (parser->global.usage_page << 16) + data; | ||
| 442 | |||
| 443 | parser->local.usage_minimum = data; | ||
| 444 | return 0; | ||
| 445 | |||
| 446 | case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: | ||
| 447 | |||
| 448 | if (parser->local.delimiter_branch > 1) { | ||
| 449 | dbg_hid("alternative usage ignored\n"); | ||
| 450 | return 0; | ||
| 451 | } | ||
| 452 | |||
| 453 | if (item->size <= 2) | ||
| 454 | data = (parser->global.usage_page << 16) + data; | ||
| 455 | |||
| 456 | for (n = parser->local.usage_minimum; n <= data; n++) | ||
| 457 | if (hid_add_usage(parser, n)) { | ||
| 458 | dbg_hid("hid_add_usage failed\n"); | ||
| 459 | return -1; | ||
| 460 | } | ||
| 461 | return 0; | ||
| 462 | |||
| 463 | default: | ||
| 464 | |||
| 465 | dbg_hid("unknown local item tag 0x%x\n", item->tag); | ||
| 466 | return 0; | ||
| 467 | } | ||
| 468 | return 0; | ||
| 469 | } | ||
| 470 | |||
| 471 | /* | ||
| 472 | * Process a main item. | ||
| 473 | */ | ||
| 474 | |||
| 475 | static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) | ||
| 476 | { | ||
| 477 | __u32 data; | ||
| 478 | int ret; | ||
| 479 | |||
| 480 | data = item_udata(item); | ||
| 481 | |||
| 482 | switch (item->tag) { | ||
| 483 | case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: | ||
| 484 | ret = open_collection(parser, data & 0xff); | ||
| 485 | break; | ||
| 486 | case HID_MAIN_ITEM_TAG_END_COLLECTION: | ||
| 487 | ret = close_collection(parser); | ||
| 488 | break; | ||
| 489 | case HID_MAIN_ITEM_TAG_INPUT: | ||
| 490 | ret = hid_add_field(parser, HID_INPUT_REPORT, data); | ||
| 491 | break; | ||
| 492 | case HID_MAIN_ITEM_TAG_OUTPUT: | ||
| 493 | ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); | ||
| 494 | break; | ||
| 495 | case HID_MAIN_ITEM_TAG_FEATURE: | ||
| 496 | ret = hid_add_field(parser, HID_FEATURE_REPORT, data); | ||
| 497 | break; | ||
| 498 | default: | ||
| 499 | dbg_hid("unknown main item tag 0x%x\n", item->tag); | ||
| 500 | ret = 0; | ||
| 501 | } | ||
| 502 | |||
| 503 | memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ | ||
| 504 | |||
| 505 | return ret; | ||
| 506 | } | ||
| 507 | |||
| 508 | /* | ||
| 509 | * Process a reserved item. | ||
| 510 | */ | ||
| 511 | |||
| 512 | static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) | ||
| 513 | { | ||
| 514 | dbg_hid("reserved item type, tag 0x%x\n", item->tag); | ||
| 515 | return 0; | ||
| 516 | } | ||
| 517 | |||
| 518 | /* | ||
| 519 | * Free a report and all registered fields. The field->usage and | ||
| 520 | * field->value table's are allocated behind the field, so we need | ||
| 521 | * only to free(field) itself. | ||
| 522 | */ | ||
| 523 | |||
| 524 | static void hid_free_report(struct hid_report *report) | ||
| 525 | { | ||
| 526 | unsigned n; | ||
| 527 | |||
| 528 | for (n = 0; n < report->maxfield; n++) | ||
| 529 | kfree(report->field[n]); | ||
| 530 | kfree(report); | ||
| 531 | } | ||
| 532 | |||
| 533 | /* | ||
| 534 | * Free a device structure, all reports, and all fields. | ||
| 535 | */ | ||
| 536 | |||
| 537 | void hid_free_device(struct hid_device *device) | ||
| 538 | { | ||
| 539 | unsigned i,j; | ||
| 540 | |||
| 541 | for (i = 0; i < HID_REPORT_TYPES; i++) { | ||
| 542 | struct hid_report_enum *report_enum = device->report_enum + i; | ||
| 543 | |||
| 544 | for (j = 0; j < 256; j++) { | ||
| 545 | struct hid_report *report = report_enum->report_id_hash[j]; | ||
| 546 | if (report) | ||
| 547 | hid_free_report(report); | ||
| 548 | } | ||
| 549 | } | ||
| 550 | |||
| 551 | kfree(device->rdesc); | ||
| 552 | kfree(device->collection); | ||
| 553 | kfree(device); | ||
| 554 | } | ||
| 555 | EXPORT_SYMBOL_GPL(hid_free_device); | ||
| 556 | |||
| 557 | /* | ||
| 558 | * Fetch a report description item from the data stream. We support long | ||
| 559 | * items, though they are not used yet. | ||
| 560 | */ | ||
| 561 | |||
| 562 | static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) | ||
| 563 | { | ||
| 564 | u8 b; | ||
| 565 | |||
| 566 | if ((end - start) <= 0) | ||
| 567 | return NULL; | ||
| 568 | |||
| 569 | b = *start++; | ||
| 570 | |||
| 571 | item->type = (b >> 2) & 3; | ||
| 572 | item->tag = (b >> 4) & 15; | ||
| 573 | |||
| 574 | if (item->tag == HID_ITEM_TAG_LONG) { | ||
| 575 | |||
| 576 | item->format = HID_ITEM_FORMAT_LONG; | ||
| 577 | |||
| 578 | if ((end - start) < 2) | ||
| 579 | return NULL; | ||
| 580 | |||
| 581 | item->size = *start++; | ||
| 582 | item->tag = *start++; | ||
| 583 | |||
| 584 | if ((end - start) < item->size) | ||
| 585 | return NULL; | ||
| 586 | |||
| 587 | item->data.longdata = start; | ||
| 588 | start += item->size; | ||
| 589 | return start; | ||
| 590 | } | ||
| 591 | |||
| 592 | item->format = HID_ITEM_FORMAT_SHORT; | ||
| 593 | item->size = b & 3; | ||
| 594 | |||
| 595 | switch (item->size) { | ||
| 596 | |||
| 597 | case 0: | ||
| 598 | return start; | ||
| 599 | |||
| 600 | case 1: | ||
| 601 | if ((end - start) < 1) | ||
| 602 | return NULL; | ||
| 603 | item->data.u8 = *start++; | ||
| 604 | return start; | ||
| 605 | |||
| 606 | case 2: | ||
| 607 | if ((end - start) < 2) | ||
| 608 | return NULL; | ||
| 609 | item->data.u16 = get_unaligned_le16(start); | ||
| 610 | start = (__u8 *)((__le16 *)start + 1); | ||
| 611 | return start; | ||
| 612 | |||
| 613 | case 3: | ||
| 614 | item->size++; | ||
| 615 | if ((end - start) < 4) | ||
| 616 | return NULL; | ||
| 617 | item->data.u32 = get_unaligned_le32(start); | ||
| 618 | start = (__u8 *)((__le32 *)start + 1); | ||
| 619 | return start; | ||
| 620 | } | ||
| 621 | |||
| 622 | return NULL; | ||
| 623 | } | ||
| 624 | |||
| 625 | /* | ||
| 626 | * Parse a report description into a hid_device structure. Reports are | ||
| 627 | * enumerated, fields are attached to these reports. | ||
| 628 | */ | ||
| 629 | |||
| 630 | struct hid_device *hid_parse_report(__u8 *start, unsigned size) | ||
| 631 | { | ||
| 632 | struct hid_device *device; | ||
| 633 | struct hid_parser *parser; | ||
| 634 | struct hid_item item; | ||
| 635 | __u8 *end; | ||
| 636 | unsigned i; | ||
| 637 | static int (*dispatch_type[])(struct hid_parser *parser, | ||
| 638 | struct hid_item *item) = { | ||
| 639 | hid_parser_main, | ||
| 640 | hid_parser_global, | ||
| 641 | hid_parser_local, | ||
| 642 | hid_parser_reserved | ||
| 643 | }; | ||
| 644 | |||
| 645 | if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL))) | ||
| 646 | return NULL; | ||
| 647 | |||
| 648 | if (!(device->collection = kzalloc(sizeof(struct hid_collection) * | ||
| 649 | HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) { | ||
| 650 | kfree(device); | ||
| 651 | return NULL; | ||
| 652 | } | ||
| 653 | device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; | ||
| 654 | |||
| 655 | for (i = 0; i < HID_REPORT_TYPES; i++) | ||
| 656 | INIT_LIST_HEAD(&device->report_enum[i].report_list); | ||
| 657 | |||
| 658 | if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) { | ||
| 659 | kfree(device->collection); | ||
| 660 | kfree(device); | ||
| 661 | return NULL; | ||
| 662 | } | ||
| 663 | memcpy(device->rdesc, start, size); | ||
| 664 | device->rsize = size; | ||
| 665 | |||
| 666 | if (!(parser = vmalloc(sizeof(struct hid_parser)))) { | ||
| 667 | kfree(device->rdesc); | ||
| 668 | kfree(device->collection); | ||
| 669 | kfree(device); | ||
| 670 | return NULL; | ||
| 671 | } | ||
| 672 | memset(parser, 0, sizeof(struct hid_parser)); | ||
| 673 | parser->device = device; | ||
| 674 | |||
| 675 | end = start + size; | ||
| 676 | while ((start = fetch_item(start, end, &item)) != NULL) { | ||
| 677 | |||
| 678 | if (item.format != HID_ITEM_FORMAT_SHORT) { | ||
| 679 | dbg_hid("unexpected long global item\n"); | ||
| 680 | hid_free_device(device); | ||
| 681 | vfree(parser); | ||
| 682 | return NULL; | ||
| 683 | } | ||
| 684 | |||
| 685 | if (dispatch_type[item.type](parser, &item)) { | ||
| 686 | dbg_hid("item %u %u %u %u parsing failed\n", | ||
| 687 | item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); | ||
| 688 | hid_free_device(device); | ||
| 689 | vfree(parser); | ||
| 690 | return NULL; | ||
| 691 | } | ||
| 692 | |||
| 693 | if (start == end) { | ||
| 694 | if (parser->collection_stack_ptr) { | ||
| 695 | dbg_hid("unbalanced collection at end of report description\n"); | ||
| 696 | hid_free_device(device); | ||
| 697 | vfree(parser); | ||
| 698 | return NULL; | ||
| 699 | } | ||
| 700 | if (parser->local.delimiter_depth) { | ||
| 701 | dbg_hid("unbalanced delimiter at end of report description\n"); | ||
| 702 | hid_free_device(device); | ||
| 703 | vfree(parser); | ||
| 704 | return NULL; | ||
| 705 | } | ||
| 706 | vfree(parser); | ||
| 707 | return device; | ||
| 708 | } | ||
| 709 | } | ||
| 710 | |||
| 711 | dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); | ||
| 712 | hid_free_device(device); | ||
| 713 | vfree(parser); | ||
| 714 | return NULL; | ||
| 715 | } | ||
| 716 | EXPORT_SYMBOL_GPL(hid_parse_report); | ||
| 717 | |||
| 718 | /* | ||
| 719 | * Convert a signed n-bit integer to signed 32-bit integer. Common | ||
| 720 | * cases are done through the compiler, the screwed things has to be | ||
| 721 | * done by hand. | ||
| 722 | */ | ||
| 723 | |||
| 724 | static s32 snto32(__u32 value, unsigned n) | ||
| 725 | { | ||
| 726 | switch (n) { | ||
| 727 | case 8: return ((__s8)value); | ||
| 728 | case 16: return ((__s16)value); | ||
| 729 | case 32: return ((__s32)value); | ||
| 730 | } | ||
| 731 | return value & (1 << (n - 1)) ? value | (-1 << n) : value; | ||
| 732 | } | ||
| 733 | |||
| 734 | /* | ||
| 735 | * Convert a signed 32-bit integer to a signed n-bit integer. | ||
| 736 | */ | ||
| 737 | |||
| 738 | static u32 s32ton(__s32 value, unsigned n) | ||
| 739 | { | ||
| 740 | s32 a = value >> (n - 1); | ||
| 741 | if (a && a != -1) | ||
| 742 | return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; | ||
| 743 | return value & ((1 << n) - 1); | ||
| 744 | } | ||
| 745 | |||
| 746 | /* | ||
| 747 | * Extract/implement a data field from/to a little endian report (bit array). | ||
| 748 | * | ||
| 749 | * Code sort-of follows HID spec: | ||
| 750 | * http://www.usb.org/developers/devclass_docs/HID1_11.pdf | ||
| 751 | * | ||
| 752 | * While the USB HID spec allows unlimited length bit fields in "report | ||
| 753 | * descriptors", most devices never use more than 16 bits. | ||
| 754 | * One model of UPS is claimed to report "LINEV" as a 32-bit field. | ||
| 755 | * Search linux-kernel and linux-usb-devel archives for "hid-core extract". | ||
| 756 | */ | ||
| 757 | |||
| 758 | static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) | ||
| 759 | { | ||
| 760 | u64 x; | ||
| 761 | |||
| 762 | if (n > 32) | ||
| 763 | printk(KERN_WARNING "HID: extract() called with n (%d) > 32! (%s)\n", | ||
| 764 | n, current->comm); | ||
| 765 | |||
| 766 | report += offset >> 3; /* adjust byte index */ | ||
| 767 | offset &= 7; /* now only need bit offset into one byte */ | ||
| 768 | x = get_unaligned_le64(report); | ||
| 769 | x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */ | ||
| 770 | return (u32) x; | ||
| 771 | } | ||
| 772 | |||
| 773 | /* | ||
| 774 | * "implement" : set bits in a little endian bit stream. | ||
| 775 | * Same concepts as "extract" (see comments above). | ||
| 776 | * The data mangled in the bit stream remains in little endian | ||
| 777 | * order the whole time. It make more sense to talk about | ||
| 778 | * endianness of register values by considering a register | ||
| 779 | * a "cached" copy of the little endiad bit stream. | ||
| 780 | */ | ||
| 781 | static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) | ||
| 782 | { | ||
| 783 | u64 x; | ||
| 784 | u64 m = (1ULL << n) - 1; | ||
| 785 | |||
| 786 | if (n > 32) | ||
| 787 | printk(KERN_WARNING "HID: implement() called with n (%d) > 32! (%s)\n", | ||
| 788 | n, current->comm); | ||
| 789 | |||
| 790 | if (value > m) | ||
| 791 | printk(KERN_WARNING "HID: implement() called with too large value %d! (%s)\n", | ||
| 792 | value, current->comm); | ||
| 793 | WARN_ON(value > m); | ||
| 794 | value &= m; | ||
| 795 | |||
| 796 | report += offset >> 3; | ||
| 797 | offset &= 7; | ||
| 798 | |||
| 799 | x = get_unaligned_le64(report); | ||
| 800 | x &= ~(m << offset); | ||
| 801 | x |= ((u64)value) << offset; | ||
| 802 | put_unaligned_le64(x, report); | ||
| 803 | } | ||
| 804 | |||
| 805 | /* | ||
| 806 | * Search an array for a value. | ||
| 807 | */ | ||
| 808 | |||
| 809 | static __inline__ int search(__s32 *array, __s32 value, unsigned n) | ||
| 810 | { | ||
| 811 | while (n--) { | ||
| 812 | if (*array++ == value) | ||
| 813 | return 0; | ||
| 814 | } | ||
| 815 | return -1; | ||
| 816 | } | ||
| 817 | |||
| 818 | static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt) | ||
| 819 | { | ||
| 820 | hid_dump_input(usage, value); | ||
| 821 | if (hid->claimed & HID_CLAIMED_INPUT) | ||
| 822 | hidinput_hid_event(hid, field, usage, value); | ||
| 823 | if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) | ||
| 824 | hid->hiddev_hid_event(hid, field, usage, value); | ||
| 825 | } | ||
| 826 | |||
| 827 | /* | ||
| 828 | * Analyse a received field, and fetch the data from it. The field | ||
| 829 | * content is stored for next report processing (we do differential | ||
| 830 | * reporting to the layer). | ||
| 831 | */ | ||
| 832 | |||
| 833 | static void hid_input_field(struct hid_device *hid, struct hid_field *field, | ||
| 834 | __u8 *data, int interrupt) | ||
| 835 | { | ||
| 836 | unsigned n; | ||
| 837 | unsigned count = field->report_count; | ||
| 838 | unsigned offset = field->report_offset; | ||
| 839 | unsigned size = field->report_size; | ||
| 840 | __s32 min = field->logical_minimum; | ||
| 841 | __s32 max = field->logical_maximum; | ||
| 842 | __s32 *value; | ||
| 843 | |||
| 844 | if (!(value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC))) | ||
| 845 | return; | ||
| 846 | |||
| 847 | for (n = 0; n < count; n++) { | ||
| 848 | |||
| 849 | value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) : | ||
| 850 | extract(data, offset + n * size, size); | ||
| 851 | |||
| 852 | if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */ | ||
| 853 | && value[n] >= min && value[n] <= max | ||
| 854 | && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) | ||
| 855 | goto exit; | ||
| 856 | } | ||
| 857 | |||
| 858 | for (n = 0; n < count; n++) { | ||
| 859 | |||
| 860 | if (HID_MAIN_ITEM_VARIABLE & field->flags) { | ||
| 861 | hid_process_event(hid, field, &field->usage[n], value[n], interrupt); | ||
| 862 | continue; | ||
| 863 | } | ||
| 864 | |||
| 865 | if (field->value[n] >= min && field->value[n] <= max | ||
| 866 | && field->usage[field->value[n] - min].hid | ||
| 867 | && search(value, field->value[n], count)) | ||
| 868 | hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt); | ||
| 869 | |||
| 870 | if (value[n] >= min && value[n] <= max | ||
| 871 | && field->usage[value[n] - min].hid | ||
| 872 | && search(field->value, value[n], count)) | ||
| 873 | hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt); | ||
| 874 | } | ||
| 875 | |||
| 876 | memcpy(field->value, value, count * sizeof(__s32)); | ||
| 877 | exit: | ||
| 878 | kfree(value); | ||
| 879 | } | ||
| 880 | |||
| 881 | /* | ||
| 882 | * Output the field into the report. | ||
| 883 | */ | ||
| 884 | |||
| 885 | static void hid_output_field(struct hid_field *field, __u8 *data) | ||
| 886 | { | ||
| 887 | unsigned count = field->report_count; | ||
| 888 | unsigned offset = field->report_offset; | ||
| 889 | unsigned size = field->report_size; | ||
| 890 | unsigned bitsused = offset + count * size; | ||
| 891 | unsigned n; | ||
| 892 | |||
| 893 | /* make sure the unused bits in the last byte are zeros */ | ||
| 894 | if (count > 0 && size > 0 && (bitsused % 8) != 0) | ||
| 895 | data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1; | ||
| 896 | |||
| 897 | for (n = 0; n < count; n++) { | ||
| 898 | if (field->logical_minimum < 0) /* signed values */ | ||
| 899 | implement(data, offset + n * size, size, s32ton(field->value[n], size)); | ||
| 900 | else /* unsigned values */ | ||
| 901 | implement(data, offset + n * size, size, field->value[n]); | ||
| 902 | } | ||
| 903 | } | ||
| 904 | |||
| 905 | /* | ||
| 906 | * Create a report. | ||
| 907 | */ | ||
| 908 | |||
| 909 | void hid_output_report(struct hid_report *report, __u8 *data) | ||
| 910 | { | ||
| 911 | unsigned n; | ||
| 912 | |||
| 913 | if (report->id > 0) | ||
| 914 | *data++ = report->id; | ||
| 915 | |||
| 916 | for (n = 0; n < report->maxfield; n++) | ||
| 917 | hid_output_field(report->field[n], data); | ||
| 918 | } | ||
| 919 | EXPORT_SYMBOL_GPL(hid_output_report); | ||
| 920 | |||
| 921 | /* | ||
| 922 | * Set a field value. The report this field belongs to has to be | ||
| 923 | * created and transferred to the device, to set this value in the | ||
| 924 | * device. | ||
| 925 | */ | ||
| 926 | |||
| 927 | int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) | ||
| 928 | { | ||
| 929 | unsigned size = field->report_size; | ||
| 930 | |||
| 931 | hid_dump_input(field->usage + offset, value); | ||
| 932 | |||
| 933 | if (offset >= field->report_count) { | ||
| 934 | dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); | ||
| 935 | hid_dump_field(field, 8); | ||
| 936 | return -1; | ||
| 937 | } | ||
| 938 | if (field->logical_minimum < 0) { | ||
| 939 | if (value != snto32(s32ton(value, size), size)) { | ||
| 940 | dbg_hid("value %d is out of range\n", value); | ||
| 941 | return -1; | ||
| 942 | } | ||
| 943 | } | ||
| 944 | field->value[offset] = value; | ||
| 945 | return 0; | ||
| 946 | } | ||
| 947 | EXPORT_SYMBOL_GPL(hid_set_field); | ||
| 948 | |||
| 949 | int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) | ||
| 950 | { | ||
| 951 | struct hid_report_enum *report_enum = hid->report_enum + type; | ||
| 952 | struct hid_report *report; | ||
| 953 | int n, rsize, i; | ||
| 954 | |||
| 955 | if (!hid) | ||
| 956 | return -ENODEV; | ||
| 957 | |||
| 958 | if (!size) { | ||
| 959 | dbg_hid("empty report\n"); | ||
| 960 | return -1; | ||
| 961 | } | ||
| 962 | |||
| 963 | dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); | ||
| 964 | |||
| 965 | n = 0; /* Normally report number is 0 */ | ||
| 966 | if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ | ||
| 967 | n = *data++; | ||
| 968 | size--; | ||
| 969 | } | ||
| 970 | |||
| 971 | /* dump the report */ | ||
| 972 | dbg_hid("report %d (size %u) = ", n, size); | ||
| 973 | for (i = 0; i < size; i++) | ||
| 974 | dbg_hid_line(" %02x", data[i]); | ||
| 975 | dbg_hid_line("\n"); | ||
| 976 | |||
| 977 | if (!(report = report_enum->report_id_hash[n])) { | ||
| 978 | dbg_hid("undefined report_id %d received\n", n); | ||
| 979 | return -1; | ||
| 980 | } | ||
| 981 | |||
| 982 | rsize = ((report->size - 1) >> 3) + 1; | ||
| 983 | |||
| 984 | if (size < rsize) { | ||
| 985 | dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize); | ||
| 986 | memset(data + size, 0, rsize - size); | ||
| 987 | } | ||
| 988 | |||
| 989 | if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) | ||
| 990 | hid->hiddev_report_event(hid, report); | ||
| 991 | if (hid->claimed & HID_CLAIMED_HIDRAW) { | ||
| 992 | /* numbered reports need to be passed with the report num */ | ||
| 993 | if (report_enum->numbered) | ||
| 994 | hidraw_report_event(hid, data - 1, size + 1); | ||
| 995 | else | ||
| 996 | hidraw_report_event(hid, data, size); | ||
| 997 | } | ||
| 998 | |||
| 999 | for (n = 0; n < report->maxfield; n++) | ||
| 1000 | hid_input_field(hid, report->field[n], data, interrupt); | ||
| 1001 | |||
| 1002 | if (hid->claimed & HID_CLAIMED_INPUT) | ||
| 1003 | hidinput_report_event(hid, report); | ||
| 1004 | |||
| 1005 | return 0; | ||
| 1006 | } | ||
| 1007 | EXPORT_SYMBOL_GPL(hid_input_report); | ||
| 1008 | |||
| 1009 | static int __init hid_init(void) | ||
| 1010 | { | ||
| 1011 | return hidraw_init(); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | static void __exit hid_exit(void) | ||
| 1015 | { | ||
| 1016 | hidraw_exit(); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | module_init(hid_init); | ||
| 1020 | module_exit(hid_exit); | ||
| 1021 | |||
| 1022 | MODULE_LICENSE(DRIVER_LICENSE); | ||
| 1023 | |||
diff --git a/usr/src/dkms_source_tree/hid-debug.c b/usr/src/dkms_source_tree/hid-debug.c new file mode 100644 index 0000000..47ac1a7 --- /dev/null +++ b/usr/src/dkms_source_tree/hid-debug.c | |||
| @@ -0,0 +1,778 @@ | |||
| 1 | /* | ||
| 2 | * (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de> | ||
| 3 | * (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz> | ||
| 4 | * (c) 2007 Jiri Kosina | ||
| 5 | * | ||
| 6 | * Some debug stuff for the HID parser. | ||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program; if not, write to the Free Software | ||
| 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 23 | * | ||
| 24 | * Should you need to contact me, the author, you can do so either by | ||
| 25 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
| 26 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include <linux/hid.h> | ||
| 30 | #include <linux/hid-debug.h> | ||
| 31 | |||
| 32 | struct hid_usage_entry { | ||
| 33 | unsigned page; | ||
| 34 | unsigned usage; | ||
| 35 | const char *description; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static const struct hid_usage_entry hid_usage_table[] = { | ||
| 39 | { 0, 0, "Undefined" }, | ||
| 40 | { 1, 0, "GenericDesktop" }, | ||
| 41 | {0, 0x01, "Pointer"}, | ||
| 42 | {0, 0x02, "Mouse"}, | ||
| 43 | {0, 0x04, "Joystick"}, | ||
| 44 | {0, 0x05, "GamePad"}, | ||
| 45 | {0, 0x06, "Keyboard"}, | ||
| 46 | {0, 0x07, "Keypad"}, | ||
| 47 | {0, 0x08, "MultiAxis"}, | ||
| 48 | {0, 0x30, "X"}, | ||
| 49 | {0, 0x31, "Y"}, | ||
| 50 | {0, 0x32, "Z"}, | ||
| 51 | {0, 0x33, "Rx"}, | ||
| 52 | {0, 0x34, "Ry"}, | ||
| 53 | {0, 0x35, "Rz"}, | ||
| 54 | {0, 0x36, "Slider"}, | ||
| 55 | {0, 0x37, "Dial"}, | ||
| 56 | {0, 0x38, "Wheel"}, | ||
| 57 | {0, 0x39, "HatSwitch"}, | ||
| 58 | {0, 0x3a, "CountedBuffer"}, | ||
| 59 | {0, 0x3b, "ByteCount"}, | ||
| 60 | {0, 0x3c, "MotionWakeup"}, | ||
| 61 | {0, 0x3d, "Start"}, | ||
| 62 | {0, 0x3e, "Select"}, | ||
| 63 | {0, 0x40, "Vx"}, | ||
| 64 | {0, 0x41, "Vy"}, | ||
| 65 | {0, 0x42, "Vz"}, | ||
| 66 | {0, 0x43, "Vbrx"}, | ||
| 67 | {0, 0x44, "Vbry"}, | ||
| 68 | {0, 0x45, "Vbrz"}, | ||
| 69 | {0, 0x46, "Vno"}, | ||
| 70 | {0, 0x80, "SystemControl"}, | ||
| 71 | {0, 0x81, "SystemPowerDown"}, | ||
| 72 | {0, 0x82, "SystemSleep"}, | ||
| 73 | {0, 0x83, "SystemWakeUp"}, | ||
| 74 | {0, 0x84, "SystemContextMenu"}, | ||
| 75 | {0, 0x85, "SystemMainMenu"}, | ||
| 76 | {0, 0x86, "SystemAppMenu"}, | ||
| 77 | {0, 0x87, "SystemMenuHelp"}, | ||
| 78 | {0, 0x88, "SystemMenuExit"}, | ||
| 79 | {0, 0x89, "SystemMenuSelect"}, | ||
| 80 | {0, 0x8a, "SystemMenuRight"}, | ||
| 81 | {0, 0x8b, "SystemMenuLeft"}, | ||
| 82 | {0, 0x8c, "SystemMenuUp"}, | ||
| 83 | {0, 0x8d, "SystemMenuDown"}, | ||
| 84 | {0, 0x90, "D-PadUp"}, | ||
| 85 | {0, 0x91, "D-PadDown"}, | ||
| 86 | {0, 0x92, "D-PadRight"}, | ||
| 87 | {0, 0x93, "D-PadLeft"}, | ||
| 88 | { 2, 0, "Simulation" }, | ||
| 89 | {0, 0xb0, "Aileron"}, | ||
| 90 | {0, 0xb1, "AileronTrim"}, | ||
| 91 | {0, 0xb2, "Anti-Torque"}, | ||
| 92 | {0, 0xb3, "Autopilot"}, | ||
| 93 | {0, 0xb4, "Chaff"}, | ||
| 94 | {0, 0xb5, "Collective"}, | ||
| 95 | {0, 0xb6, "DiveBrake"}, | ||
| 96 | {0, 0xb7, "ElectronicCountermeasures"}, | ||
| 97 | {0, 0xb8, "Elevator"}, | ||
| 98 | {0, 0xb9, "ElevatorTrim"}, | ||
| 99 | {0, 0xba, "Rudder"}, | ||
| 100 | {0, 0xbb, "Throttle"}, | ||
| 101 | {0, 0xbc, "FlightCommunications"}, | ||
| 102 | {0, 0xbd, "FlareRelease"}, | ||
| 103 | {0, 0xbe, "LandingGear"}, | ||
| 104 | {0, 0xbf, "ToeBrake"}, | ||
| 105 | { 7, 0, "Keyboard" }, | ||
| 106 | { 8, 0, "LED" }, | ||
| 107 | {0, 0x01, "NumLock"}, | ||
| 108 | {0, 0x02, "CapsLock"}, | ||
| 109 | {0, 0x03, "ScrollLock"}, | ||
| 110 | {0, 0x04, "Compose"}, | ||
| 111 | {0, 0x05, "Kana"}, | ||
| 112 | {0, 0x4b, "GenericIndicator"}, | ||
| 113 | { 9, 0, "Button" }, | ||
| 114 | { 10, 0, "Ordinal" }, | ||
| 115 | { 12, 0, "Consumer" }, | ||
| 116 | {0, 0x238, "HorizontalWheel"}, | ||
| 117 | { 13, 0, "Digitizers" }, | ||
| 118 | {0, 0x01, "Digitizer"}, | ||
| 119 | {0, 0x02, "Pen"}, | ||
| 120 | {0, 0x03, "LightPen"}, | ||
| 121 | {0, 0x04, "TouchScreen"}, | ||
| 122 | {0, 0x05, "TouchPad"}, | ||
| 123 | {0, 0x20, "Stylus"}, | ||
| 124 | {0, 0x21, "Puck"}, | ||
| 125 | {0, 0x22, "Finger"}, | ||
| 126 | {0, 0x30, "TipPressure"}, | ||
| 127 | {0, 0x31, "BarrelPressure"}, | ||
| 128 | {0, 0x32, "InRange"}, | ||
| 129 | {0, 0x33, "Touch"}, | ||
| 130 | {0, 0x34, "UnTouch"}, | ||
| 131 | {0, 0x35, "Tap"}, | ||
| 132 | {0, 0x39, "TabletFunctionKey"}, | ||
| 133 | {0, 0x3a, "ProgramChangeKey"}, | ||
| 134 | {0, 0x3c, "Invert"}, | ||
| 135 | {0, 0x42, "TipSwitch"}, | ||
| 136 | {0, 0x43, "SecondaryTipSwitch"}, | ||
| 137 | {0, 0x44, "BarrelSwitch"}, | ||
| 138 | {0, 0x45, "Eraser"}, | ||
| 139 | {0, 0x46, "TabletPick"}, | ||
| 140 | { 15, 0, "PhysicalInterfaceDevice" }, | ||
| 141 | {0, 0x00, "Undefined"}, | ||
| 142 | {0, 0x01, "Physical_Interface_Device"}, | ||
| 143 | {0, 0x20, "Normal"}, | ||
| 144 | {0, 0x21, "Set_Effect_Report"}, | ||
| 145 | {0, 0x22, "Effect_Block_Index"}, | ||
| 146 | {0, 0x23, "Parameter_Block_Offset"}, | ||
| 147 | {0, 0x24, "ROM_Flag"}, | ||
| 148 | {0, 0x25, "Effect_Type"}, | ||
| 149 | {0, 0x26, "ET_Constant_Force"}, | ||
| 150 | {0, 0x27, "ET_Ramp"}, | ||
| 151 | {0, 0x28, "ET_Custom_Force_Data"}, | ||
| 152 | {0, 0x30, "ET_Square"}, | ||
| 153 | {0, 0x31, "ET_Sine"}, | ||
| 154 | {0, 0x32, "ET_Triangle"}, | ||
| 155 | {0, 0x33, "ET_Sawtooth_Up"}, | ||
| 156 | {0, 0x34, "ET_Sawtooth_Down"}, | ||
| 157 | {0, 0x40, "ET_Spring"}, | ||
| 158 | {0, 0x41, "ET_Damper"}, | ||
| 159 | {0, 0x42, "ET_Inertia"}, | ||
| 160 | {0, 0x43, "ET_Friction"}, | ||
| 161 | {0, 0x50, "Duration"}, | ||
| 162 | {0, 0x51, "Sample_Period"}, | ||
| 163 | {0, 0x52, "Gain"}, | ||
| 164 | {0, 0x53, "Trigger_Button"}, | ||
| 165 | {0, 0x54, "Trigger_Repeat_Interval"}, | ||
| 166 | {0, 0x55, "Axes_Enable"}, | ||
| 167 | {0, 0x56, "Direction_Enable"}, | ||
| 168 | {0, 0x57, "Direction"}, | ||
| 169 | {0, 0x58, "Type_Specific_Block_Offset"}, | ||
| 170 | {0, 0x59, "Block_Type"}, | ||
| 171 | {0, 0x5A, "Set_Envelope_Report"}, | ||
| 172 | {0, 0x5B, "Attack_Level"}, | ||
| 173 | {0, 0x5C, "Attack_Time"}, | ||
| 174 | {0, 0x5D, "Fade_Level"}, | ||
| 175 | {0, 0x5E, "Fade_Time"}, | ||
| 176 | {0, 0x5F, "Set_Condition_Report"}, | ||
| 177 | {0, 0x60, "CP_Offset"}, | ||
| 178 | {0, 0x61, "Positive_Coefficient"}, | ||
| 179 | {0, 0x62, "Negative_Coefficient"}, | ||
| 180 | {0, 0x63, "Positive_Saturation"}, | ||
| 181 | {0, 0x64, "Negative_Saturation"}, | ||
| 182 | {0, 0x65, "Dead_Band"}, | ||
| 183 | {0, 0x66, "Download_Force_Sample"}, | ||
| 184 | {0, 0x67, "Isoch_Custom_Force_Enable"}, | ||
| 185 | {0, 0x68, "Custom_Force_Data_Report"}, | ||
| 186 | {0, 0x69, "Custom_Force_Data"}, | ||
| 187 | {0, 0x6A, "Custom_Force_Vendor_Defined_Data"}, | ||
| 188 | {0, 0x6B, "Set_Custom_Force_Report"}, | ||
| 189 | {0, 0x6C, "Custom_Force_Data_Offset"}, | ||
| 190 | {0, 0x6D, "Sample_Count"}, | ||
| 191 | {0, 0x6E, "Set_Periodic_Report"}, | ||
| 192 | {0, 0x6F, "Offset"}, | ||
| 193 | {0, 0x70, "Magnitude"}, | ||
| 194 | {0, 0x71, "Phase"}, | ||
| 195 | {0, 0x72, "Period"}, | ||
| 196 | {0, 0x73, "Set_Constant_Force_Report"}, | ||
| 197 | {0, 0x74, "Set_Ramp_Force_Report"}, | ||
| 198 | {0, 0x75, "Ramp_Start"}, | ||
| 199 | {0, 0x76, "Ramp_End"}, | ||
| 200 | {0, 0x77, "Effect_Operation_Report"}, | ||
| 201 | {0, 0x78, "Effect_Operation"}, | ||
| 202 | {0, 0x79, "Op_Effect_Start"}, | ||
| 203 | {0, 0x7A, "Op_Effect_Start_Solo"}, | ||
| 204 | {0, 0x7B, "Op_Effect_Stop"}, | ||
| 205 | {0, 0x7C, "Loop_Count"}, | ||
| 206 | {0, 0x7D, "Device_Gain_Report"}, | ||
| 207 | {0, 0x7E, "Device_Gain"}, | ||
| 208 | {0, 0x7F, "PID_Pool_Report"}, | ||
| 209 | {0, 0x80, "RAM_Pool_Size"}, | ||
| 210 | {0, 0x81, "ROM_Pool_Size"}, | ||
| 211 | {0, 0x82, "ROM_Effect_Block_Count"}, | ||
| 212 | {0, 0x83, "Simultaneous_Effects_Max"}, | ||
| 213 | {0, 0x84, "Pool_Alignment"}, | ||
| 214 | {0, 0x85, "PID_Pool_Move_Report"}, | ||
| 215 | {0, 0x86, "Move_Source"}, | ||
| 216 | {0, 0x87, "Move_Destination"}, | ||
| 217 | {0, 0x88, "Move_Length"}, | ||
| 218 | {0, 0x89, "PID_Block_Load_Report"}, | ||
| 219 | {0, 0x8B, "Block_Load_Status"}, | ||
| 220 | {0, 0x8C, "Block_Load_Success"}, | ||
| 221 | {0, 0x8D, "Block_Load_Full"}, | ||
| 222 | {0, 0x8E, "Block_Load_Error"}, | ||
| 223 | {0, 0x8F, "Block_Handle"}, | ||
| 224 | {0, 0x90, "PID_Block_Free_Report"}, | ||
| 225 | {0, 0x91, "Type_Specific_Block_Handle"}, | ||
| 226 | {0, 0x92, "PID_State_Report"}, | ||
| 227 | {0, 0x94, "Effect_Playing"}, | ||
| 228 | {0, 0x95, "PID_Device_Control_Report"}, | ||
| 229 | {0, 0x96, "PID_Device_Control"}, | ||
| 230 | {0, 0x97, "DC_Enable_Actuators"}, | ||
| 231 | {0, 0x98, "DC_Disable_Actuators"}, | ||
| 232 | {0, 0x99, "DC_Stop_All_Effects"}, | ||
| 233 | {0, 0x9A, "DC_Device_Reset"}, | ||
| 234 | {0, 0x9B, "DC_Device_Pause"}, | ||
| 235 | {0, 0x9C, "DC_Device_Continue"}, | ||
| 236 | {0, 0x9F, "Device_Paused"}, | ||
| 237 | {0, 0xA0, "Actuators_Enabled"}, | ||
| 238 | {0, 0xA4, "Safety_Switch"}, | ||
| 239 | {0, 0xA5, "Actuator_Override_Switch"}, | ||
| 240 | {0, 0xA6, "Actuator_Power"}, | ||
| 241 | {0, 0xA7, "Start_Delay"}, | ||
| 242 | {0, 0xA8, "Parameter_Block_Size"}, | ||
| 243 | {0, 0xA9, "Device_Managed_Pool"}, | ||
| 244 | {0, 0xAA, "Shared_Parameter_Blocks"}, | ||
| 245 | {0, 0xAB, "Create_New_Effect_Report"}, | ||
| 246 | {0, 0xAC, "RAM_Pool_Available"}, | ||
| 247 | { 0x84, 0, "Power Device" }, | ||
| 248 | { 0x84, 0x02, "PresentStatus" }, | ||
| 249 | { 0x84, 0x03, "ChangeStatus" }, | ||
| 250 | { 0x84, 0x04, "UPS" }, | ||
| 251 | { 0x84, 0x05, "PowerSupply" }, | ||
| 252 | { 0x84, 0x10, "BatterySystem" }, | ||
| 253 | { 0x84, 0x11, "BatterySystemID" }, | ||
| 254 | { 0x84, 0x12, "Battery" }, | ||
| 255 | { 0x84, 0x13, "BatteryID" }, | ||
| 256 | { 0x84, 0x14, "Charger" }, | ||
| 257 | { 0x84, 0x15, "ChargerID" }, | ||
| 258 | { 0x84, 0x16, "PowerConverter" }, | ||
| 259 | { 0x84, 0x17, "PowerConverterID" }, | ||
| 260 | { 0x84, 0x18, "OutletSystem" }, | ||
| 261 | { 0x84, 0x19, "OutletSystemID" }, | ||
| 262 | { 0x84, 0x1a, "Input" }, | ||
| 263 | { 0x84, 0x1b, "InputID" }, | ||
| 264 | { 0x84, 0x1c, "Output" }, | ||
| 265 | { 0x84, 0x1d, "OutputID" }, | ||
| 266 | { 0x84, 0x1e, "Flow" }, | ||
| 267 | { 0x84, 0x1f, "FlowID" }, | ||
| 268 | { 0x84, 0x20, "Outlet" }, | ||
| 269 | { 0x84, 0x21, "OutletID" }, | ||
| 270 | { 0x84, 0x22, "Gang" }, | ||
| 271 | { 0x84, 0x24, "PowerSummary" }, | ||
| 272 | { 0x84, 0x25, "PowerSummaryID" }, | ||
| 273 | { 0x84, 0x30, "Voltage" }, | ||
| 274 | { 0x84, 0x31, "Current" }, | ||
| 275 | { 0x84, 0x32, "Frequency" }, | ||
| 276 | { 0x84, 0x33, "ApparentPower" }, | ||
| 277 | { 0x84, 0x35, "PercentLoad" }, | ||
| 278 | { 0x84, 0x40, "ConfigVoltage" }, | ||
| 279 | { 0x84, 0x41, "ConfigCurrent" }, | ||
| 280 | { 0x84, 0x43, "ConfigApparentPower" }, | ||
| 281 | { 0x84, 0x53, "LowVoltageTransfer" }, | ||
| 282 | { 0x84, 0x54, "HighVoltageTransfer" }, | ||
| 283 | { 0x84, 0x56, "DelayBeforeStartup" }, | ||
| 284 | { 0x84, 0x57, "DelayBeforeShutdown" }, | ||
| 285 | { 0x84, 0x58, "Test" }, | ||
| 286 | { 0x84, 0x5a, "AudibleAlarmControl" }, | ||
| 287 | { 0x84, 0x60, "Present" }, | ||
| 288 | { 0x84, 0x61, "Good" }, | ||
| 289 | { 0x84, 0x62, "InternalFailure" }, | ||
| 290 | { 0x84, 0x65, "Overload" }, | ||
| 291 | { 0x84, 0x66, "OverCharged" }, | ||
| 292 | { 0x84, 0x67, "OverTemperature" }, | ||
| 293 | { 0x84, 0x68, "ShutdownRequested" }, | ||
| 294 | { 0x84, 0x69, "ShutdownImminent" }, | ||
| 295 | { 0x84, 0x6b, "SwitchOn/Off" }, | ||
| 296 | { 0x84, 0x6c, "Switchable" }, | ||
| 297 | { 0x84, 0x6d, "Used" }, | ||
| 298 | { 0x84, 0x6e, "Boost" }, | ||
| 299 | { 0x84, 0x73, "CommunicationLost" }, | ||
| 300 | { 0x84, 0xfd, "iManufacturer" }, | ||
| 301 | { 0x84, 0xfe, "iProduct" }, | ||
| 302 | { 0x84, 0xff, "iSerialNumber" }, | ||
| 303 | { 0x85, 0, "Battery System" }, | ||
| 304 | { 0x85, 0x01, "SMBBatteryMode" }, | ||
| 305 | { 0x85, 0x02, "SMBBatteryStatus" }, | ||
| 306 | { 0x85, 0x03, "SMBAlarmWarning" }, | ||
| 307 | { 0x85, 0x04, "SMBChargerMode" }, | ||
| 308 | { 0x85, 0x05, "SMBChargerStatus" }, | ||
| 309 | { 0x85, 0x06, "SMBChargerSpecInfo" }, | ||
| 310 | { 0x85, 0x07, "SMBSelectorState" }, | ||
| 311 | { 0x85, 0x08, "SMBSelectorPresets" }, | ||
| 312 | { 0x85, 0x09, "SMBSelectorInfo" }, | ||
| 313 | { 0x85, 0x29, "RemainingCapacityLimit" }, | ||
| 314 | { 0x85, 0x2c, "CapacityMode" }, | ||
| 315 | { 0x85, 0x42, "BelowRemainingCapacityLimit" }, | ||
| 316 | { 0x85, 0x44, "Charging" }, | ||
| 317 | { 0x85, 0x45, "Discharging" }, | ||
| 318 | { 0x85, 0x4b, "NeedReplacement" }, | ||
| 319 | { 0x85, 0x66, "RemainingCapacity" }, | ||
| 320 | { 0x85, 0x68, "RunTimeToEmpty" }, | ||
| 321 | { 0x85, 0x6a, "AverageTimeToFull" }, | ||
| 322 | { 0x85, 0x83, "DesignCapacity" }, | ||
| 323 | { 0x85, 0x85, "ManufacturerDate" }, | ||
| 324 | { 0x85, 0x89, "iDeviceChemistry" }, | ||
| 325 | { 0x85, 0x8b, "Rechargable" }, | ||
| 326 | { 0x85, 0x8f, "iOEMInformation" }, | ||
| 327 | { 0x85, 0x8d, "CapacityGranularity1" }, | ||
| 328 | { 0x85, 0xd0, "ACPresent" }, | ||
| 329 | /* pages 0xff00 to 0xffff are vendor-specific */ | ||
| 330 | { 0xffff, 0, "Vendor-specific-FF" }, | ||
| 331 | { 0, 0, NULL } | ||
| 332 | }; | ||
| 333 | |||
| 334 | static void resolv_usage_page(unsigned page) { | ||
| 335 | const struct hid_usage_entry *p; | ||
| 336 | |||
| 337 | for (p = hid_usage_table; p->description; p++) | ||
| 338 | if (p->page == page) { | ||
| 339 | printk("%s", p->description); | ||
| 340 | return; | ||
| 341 | } | ||
| 342 | printk("%04x", page); | ||
| 343 | } | ||
| 344 | |||
| 345 | void hid_resolv_usage(unsigned usage) { | ||
| 346 | const struct hid_usage_entry *p; | ||
| 347 | |||
| 348 | if (!hid_debug) | ||
| 349 | return; | ||
| 350 | |||
| 351 | resolv_usage_page(usage >> 16); | ||
| 352 | printk("."); | ||
| 353 | for (p = hid_usage_table; p->description; p++) | ||
| 354 | if (p->page == (usage >> 16)) { | ||
| 355 | for(++p; p->description && p->usage != 0; p++) | ||
| 356 | if (p->usage == (usage & 0xffff)) { | ||
| 357 | printk("%s", p->description); | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | break; | ||
| 361 | } | ||
| 362 | printk("%04x", usage & 0xffff); | ||
| 363 | } | ||
| 364 | EXPORT_SYMBOL_GPL(hid_resolv_usage); | ||
| 365 | |||
| 366 | static void tab(int n) { | ||
| 367 | printk(KERN_DEBUG "%*s", n, ""); | ||
| 368 | } | ||
| 369 | |||
| 370 | void hid_dump_field(struct hid_field *field, int n) { | ||
| 371 | int j; | ||
| 372 | |||
| 373 | if (!hid_debug) | ||
| 374 | return; | ||
| 375 | |||
| 376 | if (field->physical) { | ||
| 377 | tab(n); | ||
| 378 | printk("Physical("); | ||
| 379 | hid_resolv_usage(field->physical); printk(")\n"); | ||
| 380 | } | ||
| 381 | if (field->logical) { | ||
| 382 | tab(n); | ||
| 383 | printk("Logical("); | ||
| 384 | hid_resolv_usage(field->logical); printk(")\n"); | ||
| 385 | } | ||
| 386 | tab(n); printk("Usage(%d)\n", field->maxusage); | ||
| 387 | for (j = 0; j < field->maxusage; j++) { | ||
| 388 | tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n"); | ||
| 389 | } | ||
| 390 | if (field->logical_minimum != field->logical_maximum) { | ||
| 391 | tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); | ||
| 392 | tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); | ||
| 393 | } | ||
| 394 | if (field->physical_minimum != field->physical_maximum) { | ||
| 395 | tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); | ||
| 396 | tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); | ||
| 397 | } | ||
| 398 | if (field->unit_exponent) { | ||
| 399 | tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); | ||
| 400 | } | ||
| 401 | if (field->unit) { | ||
| 402 | static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; | ||
| 403 | static const char *units[5][8] = { | ||
| 404 | { "None", "None", "None", "None", "None", "None", "None", "None" }, | ||
| 405 | { "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, | ||
| 406 | { "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, | ||
| 407 | { "None", "Inch", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" }, | ||
| 408 | { "None", "Degrees", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" } | ||
| 409 | }; | ||
| 410 | |||
| 411 | int i; | ||
| 412 | int sys; | ||
| 413 | __u32 data = field->unit; | ||
| 414 | |||
| 415 | /* First nibble tells us which system we're in. */ | ||
| 416 | sys = data & 0xf; | ||
| 417 | data >>= 4; | ||
| 418 | |||
| 419 | if(sys > 4) { | ||
| 420 | tab(n); printk("Unit(Invalid)\n"); | ||
| 421 | } | ||
| 422 | else { | ||
| 423 | int earlier_unit = 0; | ||
| 424 | |||
| 425 | tab(n); printk("Unit(%s : ", systems[sys]); | ||
| 426 | |||
| 427 | for (i=1 ; i<sizeof(__u32)*2 ; i++) { | ||
| 428 | char nibble = data & 0xf; | ||
| 429 | data >>= 4; | ||
| 430 | if (nibble != 0) { | ||
| 431 | if(earlier_unit++ > 0) | ||
| 432 | printk("*"); | ||
| 433 | printk("%s", units[sys][i]); | ||
| 434 | if(nibble != 1) { | ||
| 435 | /* This is a _signed_ nibble(!) */ | ||
| 436 | |||
| 437 | int val = nibble & 0x7; | ||
| 438 | if(nibble & 0x08) | ||
| 439 | val = -((0x7 & ~val) +1); | ||
| 440 | printk("^%d", val); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | } | ||
| 444 | printk(")\n"); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | tab(n); printk("Report Size(%u)\n", field->report_size); | ||
| 448 | tab(n); printk("Report Count(%u)\n", field->report_count); | ||
| 449 | tab(n); printk("Report Offset(%u)\n", field->report_offset); | ||
| 450 | |||
| 451 | tab(n); printk("Flags( "); | ||
| 452 | j = field->flags; | ||
| 453 | printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); | ||
| 454 | printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); | ||
| 455 | printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); | ||
| 456 | printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); | ||
| 457 | printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); | ||
| 458 | printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); | ||
| 459 | printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); | ||
| 460 | printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); | ||
| 461 | printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); | ||
| 462 | printk(")\n"); | ||
| 463 | } | ||
| 464 | EXPORT_SYMBOL_GPL(hid_dump_field); | ||
| 465 | |||
| 466 | void hid_dump_device(struct hid_device *device) { | ||
| 467 | struct hid_report_enum *report_enum; | ||
| 468 | struct hid_report *report; | ||
| 469 | struct list_head *list; | ||
| 470 | unsigned i,k; | ||
| 471 | static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; | ||
| 472 | |||
| 473 | if (!hid_debug) | ||
| 474 | return; | ||
| 475 | |||
| 476 | for (i = 0; i < HID_REPORT_TYPES; i++) { | ||
| 477 | report_enum = device->report_enum + i; | ||
| 478 | list = report_enum->report_list.next; | ||
| 479 | while (list != &report_enum->report_list) { | ||
| 480 | report = (struct hid_report *) list; | ||
| 481 | tab(2); | ||
| 482 | printk("%s", table[i]); | ||
| 483 | if (report->id) | ||
| 484 | printk("(%d)", report->id); | ||
| 485 | printk("[%s]", table[report->type]); | ||
| 486 | printk("\n"); | ||
| 487 | for (k = 0; k < report->maxfield; k++) { | ||
| 488 | tab(4); | ||
| 489 | printk("Field(%d)\n", k); | ||
| 490 | hid_dump_field(report->field[k], 6); | ||
| 491 | } | ||
| 492 | list = list->next; | ||
| 493 | } | ||
| 494 | } | ||
| 495 | } | ||
| 496 | EXPORT_SYMBOL_GPL(hid_dump_device); | ||
| 497 | |||
| 498 | void hid_dump_input(struct hid_usage *usage, __s32 value) { | ||
| 499 | if (hid_debug < 2) | ||
| 500 | return; | ||
| 501 | |||
| 502 | printk(KERN_DEBUG "hid-debug: input "); | ||
| 503 | hid_resolv_usage(usage->hid); | ||
| 504 | printk(" = %d\n", value); | ||
| 505 | } | ||
| 506 | EXPORT_SYMBOL_GPL(hid_dump_input); | ||
| 507 | |||
| 508 | static const char *events[EV_MAX + 1] = { | ||
| 509 | [EV_SYN] = "Sync", [EV_KEY] = "Key", | ||
| 510 | [EV_REL] = "Relative", [EV_ABS] = "Absolute", | ||
| 511 | [EV_MSC] = "Misc", [EV_LED] = "LED", | ||
| 512 | [EV_SND] = "Sound", [EV_REP] = "Repeat", | ||
| 513 | [EV_FF] = "ForceFeedback", [EV_PWR] = "Power", | ||
| 514 | [EV_FF_STATUS] = "ForceFeedbackStatus", | ||
| 515 | }; | ||
| 516 | |||
| 517 | static const char *syncs[2] = { | ||
| 518 | [SYN_REPORT] = "Report", [SYN_CONFIG] = "Config", | ||
| 519 | }; | ||
| 520 | static const char *keys[KEY_MAX + 1] = { | ||
| 521 | [KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc", | ||
| 522 | [KEY_1] = "1", [KEY_2] = "2", | ||
| 523 | [KEY_3] = "3", [KEY_4] = "4", | ||
| 524 | [KEY_5] = "5", [KEY_6] = "6", | ||
| 525 | [KEY_7] = "7", [KEY_8] = "8", | ||
| 526 | [KEY_9] = "9", [KEY_0] = "0", | ||
| 527 | [KEY_MINUS] = "Minus", [KEY_EQUAL] = "Equal", | ||
| 528 | [KEY_BACKSPACE] = "Backspace", [KEY_TAB] = "Tab", | ||
| 529 | [KEY_Q] = "Q", [KEY_W] = "W", | ||
| 530 | [KEY_E] = "E", [KEY_R] = "R", | ||
| 531 | [KEY_T] = "T", [KEY_Y] = "Y", | ||
| 532 | [KEY_U] = "U", [KEY_I] = "I", | ||
| 533 | [KEY_O] = "O", [KEY_P] = "P", | ||
| 534 | [KEY_LEFTBRACE] = "LeftBrace", [KEY_RIGHTBRACE] = "RightBrace", | ||
| 535 | [KEY_ENTER] = "Enter", [KEY_LEFTCTRL] = "LeftControl", | ||
| 536 | [KEY_A] = "A", [KEY_S] = "S", | ||
| 537 | [KEY_D] = "D", [KEY_F] = "F", | ||
| 538 | [KEY_G] = "G", [KEY_H] = "H", | ||
| 539 | [KEY_J] = "J", [KEY_K] = "K", | ||
| 540 | [KEY_L] = "L", [KEY_SEMICOLON] = "Semicolon", | ||
| 541 | [KEY_APOSTROPHE] = "Apostrophe", [KEY_GRAVE] = "Grave", | ||
| 542 | [KEY_LEFTSHIFT] = "LeftShift", [KEY_BACKSLASH] = "BackSlash", | ||
| 543 | [KEY_Z] = "Z", [KEY_X] = "X", | ||
| 544 | [KEY_C] = "C", [KEY_V] = "V", | ||
| 545 | [KEY_B] = "B", [KEY_N] = "N", | ||
| 546 | [KEY_M] = "M", [KEY_COMMA] = "Comma", | ||
| 547 | [KEY_DOT] = "Dot", [KEY_SLASH] = "Slash", | ||
| 548 | [KEY_RIGHTSHIFT] = "RightShift", [KEY_KPASTERISK] = "KPAsterisk", | ||
| 549 | [KEY_LEFTALT] = "LeftAlt", [KEY_SPACE] = "Space", | ||
| 550 | [KEY_CAPSLOCK] = "CapsLock", [KEY_F1] = "F1", | ||
| 551 | [KEY_F2] = "F2", [KEY_F3] = "F3", | ||
| 552 | [KEY_F4] = "F4", [KEY_F5] = "F5", | ||
| 553 | [KEY_F6] = "F6", [KEY_F7] = "F7", | ||
| 554 | [KEY_F8] = "F8", [KEY_F9] = "F9", | ||
| 555 | [KEY_F10] = "F10", [KEY_NUMLOCK] = "NumLock", | ||
| 556 | [KEY_SCROLLLOCK] = "ScrollLock", [KEY_KP7] = "KP7", | ||
| 557 | [KEY_KP8] = "KP8", [KEY_KP9] = "KP9", | ||
| 558 | [KEY_KPMINUS] = "KPMinus", [KEY_KP4] = "KP4", | ||
| 559 | [KEY_KP5] = "KP5", [KEY_KP6] = "KP6", | ||
| 560 | [KEY_KPPLUS] = "KPPlus", [KEY_KP1] = "KP1", | ||
| 561 | [KEY_KP2] = "KP2", [KEY_KP3] = "KP3", | ||
| 562 | [KEY_KP0] = "KP0", [KEY_KPDOT] = "KPDot", | ||
| 563 | [KEY_ZENKAKUHANKAKU] = "Zenkaku/Hankaku", [KEY_102ND] = "102nd", | ||
| 564 | [KEY_F11] = "F11", [KEY_F12] = "F12", | ||
| 565 | [KEY_RO] = "RO", [KEY_KATAKANA] = "Katakana", | ||
| 566 | [KEY_HIRAGANA] = "HIRAGANA", [KEY_HENKAN] = "Henkan", | ||
| 567 | [KEY_KATAKANAHIRAGANA] = "Katakana/Hiragana", [KEY_MUHENKAN] = "Muhenkan", | ||
| 568 | [KEY_KPJPCOMMA] = "KPJpComma", [KEY_KPENTER] = "KPEnter", | ||
| 569 | [KEY_RIGHTCTRL] = "RightCtrl", [KEY_KPSLASH] = "KPSlash", | ||
| 570 | [KEY_SYSRQ] = "SysRq", [KEY_RIGHTALT] = "RightAlt", | ||
| 571 | [KEY_LINEFEED] = "LineFeed", [KEY_HOME] = "Home", | ||
| 572 | [KEY_UP] = "Up", [KEY_PAGEUP] = "PageUp", | ||
| 573 | [KEY_LEFT] = "Left", [KEY_RIGHT] = "Right", | ||
| 574 | [KEY_END] = "End", [KEY_DOWN] = "Down", | ||
| 575 | [KEY_PAGEDOWN] = "PageDown", [KEY_INSERT] = "Insert", | ||
| 576 | [KEY_DELETE] = "Delete", [KEY_MACRO] = "Macro", | ||
| 577 | [KEY_MUTE] = "Mute", [KEY_VOLUMEDOWN] = "VolumeDown", | ||
| 578 | [KEY_VOLUMEUP] = "VolumeUp", [KEY_POWER] = "Power", | ||
| 579 | [KEY_KPEQUAL] = "KPEqual", [KEY_KPPLUSMINUS] = "KPPlusMinus", | ||
| 580 | [KEY_PAUSE] = "Pause", [KEY_KPCOMMA] = "KPComma", | ||
| 581 | [KEY_HANGUEL] = "Hangeul", [KEY_HANJA] = "Hanja", | ||
| 582 | [KEY_YEN] = "Yen", [KEY_LEFTMETA] = "LeftMeta", | ||
| 583 | [KEY_RIGHTMETA] = "RightMeta", [KEY_COMPOSE] = "Compose", | ||
| 584 | [KEY_STOP] = "Stop", [KEY_AGAIN] = "Again", | ||
| 585 | [KEY_PROPS] = "Props", [KEY_UNDO] = "Undo", | ||
| 586 | [KEY_FRONT] = "Front", [KEY_COPY] = "Copy", | ||
| 587 | [KEY_OPEN] = "Open", [KEY_PASTE] = "Paste", | ||
| 588 | [KEY_FIND] = "Find", [KEY_CUT] = "Cut", | ||
| 589 | [KEY_HELP] = "Help", [KEY_MENU] = "Menu", | ||
| 590 | [KEY_CALC] = "Calc", [KEY_SETUP] = "Setup", | ||
| 591 | [KEY_SLEEP] = "Sleep", [KEY_WAKEUP] = "WakeUp", | ||
| 592 | [KEY_FILE] = "File", [KEY_SENDFILE] = "SendFile", | ||
| 593 | [KEY_DELETEFILE] = "DeleteFile", [KEY_XFER] = "X-fer", | ||
| 594 | [KEY_PROG1] = "Prog1", [KEY_PROG2] = "Prog2", | ||
| 595 | [KEY_WWW] = "WWW", [KEY_MSDOS] = "MSDOS", | ||
| 596 | [KEY_COFFEE] = "Coffee", [KEY_DIRECTION] = "Direction", | ||
| 597 | [KEY_CYCLEWINDOWS] = "CycleWindows", [KEY_MAIL] = "Mail", | ||
| 598 | [KEY_BOOKMARKS] = "Bookmarks", [KEY_COMPUTER] = "Computer", | ||
| 599 | [KEY_BACK] = "Back", [KEY_FORWARD] = "Forward", | ||
| 600 | [KEY_CLOSECD] = "CloseCD", [KEY_EJECTCD] = "EjectCD", | ||
| 601 | [KEY_EJECTCLOSECD] = "EjectCloseCD", [KEY_NEXTSONG] = "NextSong", | ||
| 602 | [KEY_PLAYPAUSE] = "PlayPause", [KEY_PREVIOUSSONG] = "PreviousSong", | ||
| 603 | [KEY_STOPCD] = "StopCD", [KEY_RECORD] = "Record", | ||
| 604 | [KEY_REWIND] = "Rewind", [KEY_PHONE] = "Phone", | ||
| 605 | [KEY_ISO] = "ISOKey", [KEY_CONFIG] = "Config", | ||
| 606 | [KEY_HOMEPAGE] = "HomePage", [KEY_REFRESH] = "Refresh", | ||
| 607 | [KEY_EXIT] = "Exit", [KEY_MOVE] = "Move", | ||
| 608 | [KEY_EDIT] = "Edit", [KEY_SCROLLUP] = "ScrollUp", | ||
| 609 | [KEY_SCROLLDOWN] = "ScrollDown", [KEY_KPLEFTPAREN] = "KPLeftParenthesis", | ||
| 610 | [KEY_KPRIGHTPAREN] = "KPRightParenthesis", [KEY_NEW] = "New", | ||
| 611 | [KEY_REDO] = "Redo", [KEY_F13] = "F13", | ||
| 612 | [KEY_F14] = "F14", [KEY_F15] = "F15", | ||
| 613 | [KEY_F16] = "F16", [KEY_F17] = "F17", | ||
| 614 | [KEY_F18] = "F18", [KEY_F19] = "F19", | ||
| 615 | [KEY_F20] = "F20", [KEY_F21] = "F21", | ||
| 616 | [KEY_F22] = "F22", [KEY_F23] = "F23", | ||
| 617 | [KEY_F24] = "F24", [KEY_PLAYCD] = "PlayCD", | ||
| 618 | [KEY_PAUSECD] = "PauseCD", [KEY_PROG3] = "Prog3", | ||
| 619 | [KEY_PROG4] = "Prog4", [KEY_SUSPEND] = "Suspend", | ||
| 620 | [KEY_CLOSE] = "Close", [KEY_PLAY] = "Play", | ||
| 621 | [KEY_FASTFORWARD] = "FastForward", [KEY_BASSBOOST] = "BassBoost", | ||
| 622 | [KEY_PRINT] = "Print", [KEY_HP] = "HP", | ||
| 623 | [KEY_CAMERA] = "Camera", [KEY_SOUND] = "Sound", | ||
| 624 | [KEY_QUESTION] = "Question", [KEY_EMAIL] = "Email", | ||
| 625 | [KEY_CHAT] = "Chat", [KEY_SEARCH] = "Search", | ||
| 626 | [KEY_CONNECT] = "Connect", [KEY_FINANCE] = "Finance", | ||
| 627 | [KEY_SPORT] = "Sport", [KEY_SHOP] = "Shop", | ||
| 628 | [KEY_ALTERASE] = "AlternateErase", [KEY_CANCEL] = "Cancel", | ||
| 629 | [KEY_BRIGHTNESSDOWN] = "BrightnessDown", [KEY_BRIGHTNESSUP] = "BrightnessUp", | ||
| 630 | [KEY_MEDIA] = "Media", [KEY_UNKNOWN] = "Unknown", | ||
| 631 | [BTN_0] = "Btn0", [BTN_1] = "Btn1", | ||
| 632 | [BTN_2] = "Btn2", [BTN_3] = "Btn3", | ||
| 633 | [BTN_4] = "Btn4", [BTN_5] = "Btn5", | ||
| 634 | [BTN_6] = "Btn6", [BTN_7] = "Btn7", | ||
| 635 | [BTN_8] = "Btn8", [BTN_9] = "Btn9", | ||
| 636 | [BTN_LEFT] = "LeftBtn", [BTN_RIGHT] = "RightBtn", | ||
| 637 | [BTN_MIDDLE] = "MiddleBtn", [BTN_SIDE] = "SideBtn", | ||
| 638 | [BTN_EXTRA] = "ExtraBtn", [BTN_FORWARD] = "ForwardBtn", | ||
| 639 | [BTN_BACK] = "BackBtn", [BTN_TASK] = "TaskBtn", | ||
| 640 | [BTN_TRIGGER] = "Trigger", [BTN_THUMB] = "ThumbBtn", | ||
| 641 | [BTN_THUMB2] = "ThumbBtn2", [BTN_TOP] = "TopBtn", | ||
| 642 | [BTN_TOP2] = "TopBtn2", [BTN_PINKIE] = "PinkieBtn", | ||
| 643 | [BTN_BASE] = "BaseBtn", [BTN_BASE2] = "BaseBtn2", | ||
| 644 | [BTN_BASE3] = "BaseBtn3", [BTN_BASE4] = "BaseBtn4", | ||
| 645 | [BTN_BASE5] = "BaseBtn5", [BTN_BASE6] = "BaseBtn6", | ||
| 646 | [BTN_DEAD] = "BtnDead", [BTN_A] = "BtnA", | ||
| 647 | [BTN_B] = "BtnB", [BTN_C] = "BtnC", | ||
| 648 | [BTN_X] = "BtnX", [BTN_Y] = "BtnY", | ||
| 649 | [BTN_Z] = "BtnZ", [BTN_TL] = "BtnTL", | ||
| 650 | [BTN_TR] = "BtnTR", [BTN_TL2] = "BtnTL2", | ||
| 651 | [BTN_TR2] = "BtnTR2", [BTN_SELECT] = "BtnSelect", | ||
| 652 | [BTN_START] = "BtnStart", [BTN_MODE] = "BtnMode", | ||
| 653 | [BTN_THUMBL] = "BtnThumbL", [BTN_THUMBR] = "BtnThumbR", | ||
| 654 | [BTN_TOOL_PEN] = "ToolPen", [BTN_TOOL_RUBBER] = "ToolRubber", | ||
| 655 | [BTN_TOOL_BRUSH] = "ToolBrush", [BTN_TOOL_PENCIL] = "ToolPencil", | ||
| 656 | [BTN_TOOL_AIRBRUSH] = "ToolAirbrush", [BTN_TOOL_FINGER] = "ToolFinger", | ||
| 657 | [BTN_TOOL_MOUSE] = "ToolMouse", [BTN_TOOL_LENS] = "ToolLens", | ||
| 658 | [BTN_TOUCH] = "Touch", [BTN_STYLUS] = "Stylus", | ||
| 659 | [BTN_STYLUS2] = "Stylus2", [BTN_TOOL_DOUBLETAP] = "ToolDoubleTap", | ||
| 660 | [BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_GEAR_DOWN] = "WheelBtn", | ||
| 661 | [BTN_GEAR_UP] = "Gear up", [KEY_OK] = "Ok", | ||
| 662 | [KEY_SELECT] = "Select", [KEY_GOTO] = "Goto", | ||
| 663 | [KEY_CLEAR] = "Clear", [KEY_POWER2] = "Power2", | ||
| 664 | [KEY_OPTION] = "Option", [KEY_INFO] = "Info", | ||
| 665 | [KEY_TIME] = "Time", [KEY_VENDOR] = "Vendor", | ||
| 666 | [KEY_ARCHIVE] = "Archive", [KEY_PROGRAM] = "Program", | ||
| 667 | [KEY_CHANNEL] = "Channel", [KEY_FAVORITES] = "Favorites", | ||
| 668 | [KEY_EPG] = "EPG", [KEY_PVR] = "PVR", | ||
| 669 | [KEY_MHP] = "MHP", [KEY_LANGUAGE] = "Language", | ||
| 670 | [KEY_TITLE] = "Title", [KEY_SUBTITLE] = "Subtitle", | ||
| 671 | [KEY_ANGLE] = "Angle", [KEY_ZOOM] = "Zoom", | ||
| 672 | [KEY_MODE] = "Mode", [KEY_KEYBOARD] = "Keyboard", | ||
| 673 | [KEY_SCREEN] = "Screen", [KEY_PC] = "PC", | ||
| 674 | [KEY_TV] = "TV", [KEY_TV2] = "TV2", | ||
| 675 | [KEY_VCR] = "VCR", [KEY_VCR2] = "VCR2", | ||
| 676 | [KEY_SAT] = "Sat", [KEY_SAT2] = "Sat2", | ||
| 677 | [KEY_CD] = "CD", [KEY_TAPE] = "Tape", | ||
| 678 | [KEY_RADIO] = "Radio", [KEY_TUNER] = "Tuner", | ||
| 679 | [KEY_PLAYER] = "Player", [KEY_TEXT] = "Text", | ||
| 680 | [KEY_DVD] = "DVD", [KEY_AUX] = "Aux", | ||
| 681 | [KEY_MP3] = "MP3", [KEY_AUDIO] = "Audio", | ||
| 682 | [KEY_VIDEO] = "Video", [KEY_DIRECTORY] = "Directory", | ||
| 683 | [KEY_LIST] = "List", [KEY_MEMO] = "Memo", | ||
| 684 | [KEY_CALENDAR] = "Calendar", [KEY_RED] = "Red", | ||
| 685 | [KEY_GREEN] = "Green", [KEY_YELLOW] = "Yellow", | ||
| 686 | [KEY_BLUE] = "Blue", [KEY_CHANNELUP] = "ChannelUp", | ||
| 687 | [KEY_CHANNELDOWN] = "ChannelDown", [KEY_FIRST] = "First", | ||
| 688 | [KEY_LAST] = "Last", [KEY_AB] = "AB", | ||
| 689 | [KEY_NEXT] = "Next", [KEY_RESTART] = "Restart", | ||
| 690 | [KEY_SLOW] = "Slow", [KEY_SHUFFLE] = "Shuffle", | ||
| 691 | [KEY_BREAK] = "Break", [KEY_PREVIOUS] = "Previous", | ||
| 692 | [KEY_DIGITS] = "Digits", [KEY_TEEN] = "TEEN", | ||
| 693 | [KEY_TWEN] = "TWEN", [KEY_DEL_EOL] = "DeleteEOL", | ||
| 694 | [KEY_DEL_EOS] = "DeleteEOS", [KEY_INS_LINE] = "InsertLine", | ||
| 695 | [KEY_DEL_LINE] = "DeleteLine", | ||
| 696 | [KEY_SEND] = "Send", [KEY_REPLY] = "Reply", | ||
| 697 | [KEY_FORWARDMAIL] = "ForwardMail", [KEY_SAVE] = "Save", | ||
| 698 | [KEY_DOCUMENTS] = "Documents", [KEY_SPELLCHECK] = "SpellCheck", | ||
| 699 | [KEY_LOGOFF] = "Logoff", | ||
| 700 | [KEY_FN] = "Fn", [KEY_FN_ESC] = "Fn+ESC", | ||
| 701 | [KEY_FN_1] = "Fn+1", [KEY_FN_2] = "Fn+2", | ||
| 702 | [KEY_FN_B] = "Fn+B", [KEY_FN_D] = "Fn+D", | ||
| 703 | [KEY_FN_E] = "Fn+E", [KEY_FN_F] = "Fn+F", | ||
| 704 | [KEY_FN_S] = "Fn+S", | ||
| 705 | [KEY_FN_F1] = "Fn+F1", [KEY_FN_F2] = "Fn+F2", | ||
| 706 | [KEY_FN_F3] = "Fn+F3", [KEY_FN_F4] = "Fn+F4", | ||
| 707 | [KEY_FN_F5] = "Fn+F5", [KEY_FN_F6] = "Fn+F6", | ||
| 708 | [KEY_FN_F7] = "Fn+F7", [KEY_FN_F8] = "Fn+F8", | ||
| 709 | [KEY_FN_F9] = "Fn+F9", [KEY_FN_F10] = "Fn+F10", | ||
| 710 | [KEY_FN_F11] = "Fn+F11", [KEY_FN_F12] = "Fn+F12", | ||
| 711 | [KEY_KBDILLUMTOGGLE] = "KbdIlluminationToggle", | ||
| 712 | [KEY_KBDILLUMDOWN] = "KbdIlluminationDown", | ||
| 713 | [KEY_KBDILLUMUP] = "KbdIlluminationUp", | ||
| 714 | [KEY_SWITCHVIDEOMODE] = "SwitchVideoMode", | ||
| 715 | }; | ||
| 716 | |||
| 717 | static const char *relatives[REL_MAX + 1] = { | ||
| 718 | [REL_X] = "X", [REL_Y] = "Y", | ||
| 719 | [REL_Z] = "Z", [REL_RX] = "Rx", | ||
| 720 | [REL_RY] = "Ry", [REL_RZ] = "Rz", | ||
| 721 | [REL_HWHEEL] = "HWheel", [REL_DIAL] = "Dial", | ||
| 722 | [REL_WHEEL] = "Wheel", [REL_MISC] = "Misc", | ||
| 723 | }; | ||
| 724 | |||
| 725 | static const char *absolutes[ABS_MAX + 1] = { | ||
| 726 | [ABS_X] = "X", [ABS_Y] = "Y", | ||
| 727 | [ABS_Z] = "Z", [ABS_RX] = "Rx", | ||
| 728 | [ABS_RY] = "Ry", [ABS_RZ] = "Rz", | ||
| 729 | [ABS_THROTTLE] = "Throttle", [ABS_RUDDER] = "Rudder", | ||
| 730 | [ABS_WHEEL] = "Wheel", [ABS_GAS] = "Gas", | ||
| 731 | [ABS_BRAKE] = "Brake", [ABS_HAT0X] = "Hat0X", | ||
| 732 | [ABS_HAT0Y] = "Hat0Y", [ABS_HAT1X] = "Hat1X", | ||
| 733 | [ABS_HAT1Y] = "Hat1Y", [ABS_HAT2X] = "Hat2X", | ||
| 734 | [ABS_HAT2Y] = "Hat2Y", [ABS_HAT3X] = "Hat3X", | ||
| 735 | [ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure", | ||
| 736 | [ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt", | ||
| 737 | [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "Tool Width", | ||
| 738 | [ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc", | ||
| 739 | }; | ||
| 740 | |||
| 741 | static const char *misc[MSC_MAX + 1] = { | ||
| 742 | [MSC_SERIAL] = "Serial", [MSC_PULSELED] = "Pulseled", | ||
| 743 | [MSC_GESTURE] = "Gesture", [MSC_RAW] = "RawData" | ||
| 744 | }; | ||
| 745 | |||
| 746 | static const char *leds[LED_MAX + 1] = { | ||
| 747 | [LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock", | ||
| 748 | [LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose", | ||
| 749 | [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", | ||
| 750 | [LED_SUSPEND] = "Suspend", [LED_MUTE] = "Mute", | ||
| 751 | [LED_MISC] = "Misc", | ||
| 752 | }; | ||
| 753 | |||
| 754 | static const char *repeats[REP_MAX + 1] = { | ||
| 755 | [REP_DELAY] = "Delay", [REP_PERIOD] = "Period" | ||
| 756 | }; | ||
| 757 | |||
| 758 | static const char *sounds[SND_MAX + 1] = { | ||
| 759 | [SND_CLICK] = "Click", [SND_BELL] = "Bell", | ||
| 760 | [SND_TONE] = "Tone" | ||
| 761 | }; | ||
| 762 | |||
| 763 | static const char **names[EV_MAX + 1] = { | ||
| 764 | [EV_SYN] = syncs, [EV_KEY] = keys, | ||
| 765 | [EV_REL] = relatives, [EV_ABS] = absolutes, | ||
| 766 | [EV_MSC] = misc, [EV_LED] = leds, | ||
| 767 | [EV_SND] = sounds, [EV_REP] = repeats, | ||
| 768 | }; | ||
| 769 | |||
| 770 | void hid_resolv_event(__u8 type, __u16 code) { | ||
| 771 | |||
| 772 | if (!hid_debug) | ||
| 773 | return; | ||
| 774 | |||
| 775 | printk("%s.%s", events[type] ? events[type] : "?", | ||
| 776 | names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); | ||
| 777 | } | ||
| 778 | EXPORT_SYMBOL_GPL(hid_resolv_event); | ||
diff --git a/usr/src/dkms_source_tree/hid-input-quirks.c b/usr/src/dkms_source_tree/hid-input-quirks.c new file mode 100644 index 0000000..16feea0 --- /dev/null +++ b/usr/src/dkms_source_tree/hid-input-quirks.c | |||
| @@ -0,0 +1,484 @@ | |||
| 1 | /* | ||
| 2 | * HID-input usage mapping quirks | ||
| 3 | * | ||
| 4 | * This is used to handle HID-input mappings for devices violating | ||
| 5 | * HUT 1.12 specification. | ||
| 6 | * | ||
| 7 | * Copyright (c) 2007-2008 Jiri Kosina | ||
| 8 | */ | ||
| 9 | |||
| 10 | /* | ||
| 11 | * This program is free software; you can redistribute it and/or modify it | ||
| 12 | * under the terms of the GNU General Public License as published by the Free | ||
| 13 | * Software Foundation; either version 2 of the License | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/input.h> | ||
| 17 | #include <linux/hid.h> | ||
| 18 | |||
| 19 | #define map_abs(c) do { usage->code = c; usage->type = EV_ABS; *bit = input->absbit; *max = ABS_MAX; } while (0) | ||
| 20 | #define map_rel(c) do { usage->code = c; usage->type = EV_REL; *bit = input->relbit; *max = REL_MAX; } while (0) | ||
| 21 | #define map_key(c) do { usage->code = c; usage->type = EV_KEY; *bit = input->keybit; *max = KEY_MAX; } while (0) | ||
| 22 | #define map_led(c) do { usage->code = c; usage->type = EV_LED; *bit = input->ledbit; *max = LED_MAX; } while (0) | ||
| 23 | |||
| 24 | #define map_abs_clear(c) do { map_abs(c); clear_bit(c, *bit); } while (0) | ||
| 25 | #define map_key_clear(c) do { map_key(c); clear_bit(c, *bit); } while (0) | ||
| 26 | |||
| 27 | static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input, | ||
| 28 | unsigned long **bit, int *max) | ||
| 29 | { | ||
| 30 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) | ||
| 31 | return 0; | ||
| 32 | |||
| 33 | switch (usage->hid & HID_USAGE) { | ||
| 34 | case 0x03a: map_key_clear(KEY_SOUND); break; | ||
| 35 | case 0x03b: map_key_clear(KEY_CAMERA); break; | ||
| 36 | case 0x03c: map_key_clear(KEY_DOCUMENTS); break; | ||
| 37 | default: | ||
| 38 | return 0; | ||
| 39 | } | ||
| 40 | return 1; | ||
| 41 | } | ||
| 42 | |||
| 43 | static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *input, | ||
| 44 | unsigned long **bit, int *max) | ||
| 45 | { | ||
| 46 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) | ||
| 47 | return 0; | ||
| 48 | |||
| 49 | switch (usage->hid & HID_USAGE) { | ||
| 50 | case 0x301: map_key_clear(KEY_PROG1); break; | ||
| 51 | case 0x302: map_key_clear(KEY_PROG2); break; | ||
| 52 | case 0x303: map_key_clear(KEY_PROG3); break; | ||
| 53 | default: | ||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | return 1; | ||
| 57 | } | ||
| 58 | |||
| 59 | static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_dev *input, | ||
| 60 | unsigned long **bit, int *max) | ||
| 61 | { | ||
| 62 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) | ||
| 63 | return 0; | ||
| 64 | |||
| 65 | set_bit(EV_REP, input->evbit); | ||
| 66 | switch(usage->hid & HID_USAGE) { | ||
| 67 | /* Reported on Logitech Ultra X Media Remote */ | ||
| 68 | case 0x004: map_key_clear(KEY_AGAIN); break; | ||
| 69 | case 0x00d: map_key_clear(KEY_HOME); break; | ||
| 70 | case 0x024: map_key_clear(KEY_SHUFFLE); break; | ||
| 71 | case 0x025: map_key_clear(KEY_TV); break; | ||
| 72 | case 0x026: map_key_clear(KEY_MENU); break; | ||
| 73 | case 0x031: map_key_clear(KEY_AUDIO); break; | ||
| 74 | case 0x032: map_key_clear(KEY_TEXT); break; | ||
| 75 | case 0x033: map_key_clear(KEY_LAST); break; | ||
| 76 | case 0x047: map_key_clear(KEY_MP3); break; | ||
| 77 | case 0x048: map_key_clear(KEY_DVD); break; | ||
| 78 | case 0x049: map_key_clear(KEY_MEDIA); break; | ||
| 79 | case 0x04a: map_key_clear(KEY_VIDEO); break; | ||
| 80 | case 0x04b: map_key_clear(KEY_ANGLE); break; | ||
| 81 | case 0x04c: map_key_clear(KEY_LANGUAGE); break; | ||
| 82 | case 0x04d: map_key_clear(KEY_SUBTITLE); break; | ||
| 83 | case 0x051: map_key_clear(KEY_RED); break; | ||
| 84 | case 0x052: map_key_clear(KEY_CLOSE); break; | ||
| 85 | |||
| 86 | default: | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | return 1; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input, | ||
| 93 | unsigned long **bit, int *max) | ||
| 94 | { | ||
| 95 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) | ||
| 96 | return 0; | ||
| 97 | |||
| 98 | set_bit(EV_REP, input->evbit); | ||
| 99 | switch(usage->hid & HID_USAGE) { | ||
| 100 | /* Reported on Gyration MCE Remote */ | ||
| 101 | case 0x00d: map_key_clear(KEY_HOME); break; | ||
| 102 | case 0x024: map_key_clear(KEY_DVD); break; | ||
| 103 | case 0x025: map_key_clear(KEY_PVR); break; | ||
| 104 | case 0x046: map_key_clear(KEY_MEDIA); break; | ||
| 105 | case 0x047: map_key_clear(KEY_MP3); break; | ||
| 106 | case 0x049: map_key_clear(KEY_CAMERA); break; | ||
| 107 | case 0x04a: map_key_clear(KEY_VIDEO); break; | ||
| 108 | |||
| 109 | default: | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | return 1; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input, | ||
| 116 | unsigned long **bit, int *max) | ||
| 117 | { | ||
| 118 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) | ||
| 119 | return 0; | ||
| 120 | |||
| 121 | set_bit(EV_REP, input->evbit); | ||
| 122 | switch (usage->hid & HID_USAGE) { | ||
| 123 | case 0xff01: map_key_clear(BTN_1); break; | ||
| 124 | case 0xff02: map_key_clear(BTN_2); break; | ||
| 125 | case 0xff03: map_key_clear(BTN_3); break; | ||
| 126 | case 0xff04: map_key_clear(BTN_4); break; | ||
| 127 | case 0xff05: map_key_clear(BTN_5); break; | ||
| 128 | case 0xff06: map_key_clear(BTN_6); break; | ||
| 129 | case 0xff07: map_key_clear(BTN_7); break; | ||
| 130 | case 0xff08: map_key_clear(BTN_8); break; | ||
| 131 | case 0xff09: map_key_clear(BTN_9); break; | ||
| 132 | case 0xff0a: map_key_clear(BTN_A); break; | ||
| 133 | case 0xff0b: map_key_clear(BTN_B); break; | ||
| 134 | default: | ||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | return 1; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev *input, | ||
| 141 | unsigned long **bit, int *max) | ||
| 142 | { | ||
| 143 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) | ||
| 144 | return 0; | ||
| 145 | |||
| 146 | switch(usage->hid & HID_USAGE) { | ||
| 147 | case 0xfd06: map_key_clear(KEY_CHAT); break; | ||
| 148 | case 0xfd07: map_key_clear(KEY_PHONE); break; | ||
| 149 | case 0xff05: | ||
| 150 | set_bit(EV_REP, input->evbit); | ||
| 151 | map_key_clear(KEY_F13); | ||
| 152 | set_bit(KEY_F14, input->keybit); | ||
| 153 | set_bit(KEY_F15, input->keybit); | ||
| 154 | set_bit(KEY_F16, input->keybit); | ||
| 155 | set_bit(KEY_F17, input->keybit); | ||
| 156 | set_bit(KEY_F18, input->keybit); | ||
| 157 | default: | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | return 1; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_dev *input, | ||
| 164 | unsigned long **bit, int *max) | ||
| 165 | { | ||
| 166 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) | ||
| 167 | return 0; | ||
| 168 | |||
| 169 | set_bit(EV_REP, input->evbit); | ||
| 170 | switch(usage->hid & HID_USAGE) { | ||
| 171 | case 0xfd08: map_key_clear(KEY_FORWARD); break; | ||
| 172 | case 0xfd09: map_key_clear(KEY_BACK); break; | ||
| 173 | case 0xfd0b: map_key_clear(KEY_PLAYPAUSE); break; | ||
| 174 | case 0xfd0e: map_key_clear(KEY_CLOSE); break; | ||
| 175 | case 0xfd0f: map_key_clear(KEY_PLAY); break; | ||
| 176 | default: | ||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | return 1; | ||
| 180 | } | ||
| 181 | |||
| 182 | static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *input, | ||
| 183 | unsigned long **bit, int *max) | ||
| 184 | { | ||
| 185 | if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) && | ||
| 186 | ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)) | ||
| 187 | return 0; | ||
| 188 | |||
| 189 | if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) | ||
| 190 | switch(usage->hid & HID_USAGE) { | ||
| 191 | case 0x05a: map_key_clear(KEY_TEXT); break; | ||
| 192 | case 0x05b: map_key_clear(KEY_RED); break; | ||
| 193 | case 0x05c: map_key_clear(KEY_GREEN); break; | ||
| 194 | case 0x05d: map_key_clear(KEY_YELLOW); break; | ||
| 195 | case 0x05e: map_key_clear(KEY_BLUE); break; | ||
| 196 | default: | ||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) | ||
| 201 | switch(usage->hid & HID_USAGE) { | ||
| 202 | case 0x0f6: map_key_clear(KEY_NEXT); break; | ||
| 203 | case 0x0fa: map_key_clear(KEY_BACK); break; | ||
| 204 | default: | ||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | return 1; | ||
| 208 | } | ||
| 209 | |||
| 210 | static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *input, | ||
| 211 | unsigned long **bit, int *max) | ||
| 212 | { | ||
| 213 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) | ||
| 214 | return 0; | ||
| 215 | |||
| 216 | switch (usage->hid & HID_USAGE) { | ||
| 217 | case 0x1001: map_key_clear(KEY_MESSENGER); break; | ||
| 218 | case 0x1003: map_key_clear(KEY_SOUND); break; | ||
| 219 | case 0x1004: map_key_clear(KEY_VIDEO); break; | ||
| 220 | case 0x1005: map_key_clear(KEY_AUDIO); break; | ||
| 221 | case 0x100a: map_key_clear(KEY_DOCUMENTS); break; | ||
| 222 | case 0x1011: map_key_clear(KEY_PREVIOUSSONG); break; | ||
| 223 | case 0x1012: map_key_clear(KEY_NEXTSONG); break; | ||
| 224 | case 0x1013: map_key_clear(KEY_CAMERA); break; | ||
| 225 | case 0x1014: map_key_clear(KEY_MESSENGER); break; | ||
| 226 | case 0x1015: map_key_clear(KEY_RECORD); break; | ||
| 227 | case 0x1016: map_key_clear(KEY_PLAYER); break; | ||
| 228 | case 0x1017: map_key_clear(KEY_EJECTCD); break; | ||
| 229 | case 0x1018: map_key_clear(KEY_MEDIA); break; | ||
| 230 | case 0x1019: map_key_clear(KEY_PROG1); break; | ||
| 231 | case 0x101a: map_key_clear(KEY_PROG2); break; | ||
| 232 | case 0x101b: map_key_clear(KEY_PROG3); break; | ||
| 233 | case 0x101f: map_key_clear(KEY_ZOOMIN); break; | ||
| 234 | case 0x1020: map_key_clear(KEY_ZOOMOUT); break; | ||
| 235 | case 0x1021: map_key_clear(KEY_ZOOMRESET); break; | ||
| 236 | case 0x1023: map_key_clear(KEY_CLOSE); break; | ||
| 237 | case 0x1027: map_key_clear(KEY_MENU); break; | ||
| 238 | /* this one is marked as 'Rotate' */ | ||
| 239 | case 0x1028: map_key_clear(KEY_ANGLE); break; | ||
| 240 | case 0x1029: map_key_clear(KEY_SHUFFLE); break; | ||
| 241 | case 0x102a: map_key_clear(KEY_BACK); break; | ||
| 242 | case 0x102b: map_key_clear(KEY_CYCLEWINDOWS); break; | ||
| 243 | case 0x1041: map_key_clear(KEY_BATTERY); break; | ||
| 244 | case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; | ||
| 245 | case 0x1043: map_key_clear(KEY_SPREADSHEET); break; | ||
| 246 | case 0x1044: map_key_clear(KEY_PRESENTATION); break; | ||
| 247 | case 0x1045: map_key_clear(KEY_UNDO); break; | ||
| 248 | case 0x1046: map_key_clear(KEY_REDO); break; | ||
| 249 | case 0x1047: map_key_clear(KEY_PRINT); break; | ||
| 250 | case 0x1048: map_key_clear(KEY_SAVE); break; | ||
| 251 | case 0x1049: map_key_clear(KEY_PROG1); break; | ||
| 252 | case 0x104a: map_key_clear(KEY_PROG2); break; | ||
| 253 | case 0x104b: map_key_clear(KEY_PROG3); break; | ||
| 254 | case 0x104c: map_key_clear(KEY_PROG4); break; | ||
| 255 | |||
| 256 | default: | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | return 1; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *input, | ||
| 263 | unsigned long **bit, int *max) | ||
| 264 | { | ||
| 265 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) | ||
| 266 | return 0; | ||
| 267 | |||
| 268 | switch (usage->hid & HID_USAGE) { | ||
| 269 | case 0x156: map_key_clear(KEY_WORDPROCESSOR); break; | ||
| 270 | case 0x157: map_key_clear(KEY_SPREADSHEET); break; | ||
| 271 | case 0x158: map_key_clear(KEY_PRESENTATION); break; | ||
| 272 | case 0x15c: map_key_clear(KEY_STOP); break; | ||
| 273 | |||
| 274 | default: | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | return 1; | ||
| 278 | } | ||
| 279 | |||
| 280 | static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input, | ||
| 281 | unsigned long **bit, int *max) | ||
| 282 | { | ||
| 283 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) | ||
| 284 | return 0; | ||
| 285 | |||
| 286 | switch (usage->hid & HID_USAGE) { | ||
| 287 | case 0x230: map_key(BTN_MOUSE); break; | ||
| 288 | case 0x231: map_rel(REL_WHEEL); break; | ||
| 289 | /* | ||
| 290 | * this keyboard has a scrollwheel implemented in | ||
| 291 | * totally broken way. We map this usage temporarily | ||
| 292 | * to HWHEEL and handle it in the event quirk handler | ||
| 293 | */ | ||
| 294 | case 0x232: map_rel(REL_HWHEEL); break; | ||
| 295 | |||
| 296 | default: | ||
| 297 | return 0; | ||
| 298 | } | ||
| 299 | return 1; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input, | ||
| 303 | unsigned long **bit, int *max) | ||
| 304 | { | ||
| 305 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) | ||
| 306 | return 0; | ||
| 307 | |||
| 308 | switch (usage->hid & HID_USAGE) { | ||
| 309 | case 0x2003: map_key_clear(KEY_ZOOMIN); break; | ||
| 310 | case 0x2103: map_key_clear(KEY_ZOOMOUT); break; | ||
| 311 | default: | ||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | return 1; | ||
| 315 | } | ||
| 316 | |||
| 317 | #define VENDOR_ID_BELKIN 0x1020 | ||
| 318 | #define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006 | ||
| 319 | |||
| 320 | #define VENDOR_ID_CHERRY 0x046a | ||
| 321 | #define DEVICE_ID_CHERRY_CYMOTION 0x0023 | ||
| 322 | |||
| 323 | #define VENDOR_ID_CHICONY 0x04f2 | ||
| 324 | #define DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 | ||
| 325 | |||
| 326 | #define VENDOR_ID_EZKEY 0x0518 | ||
| 327 | #define DEVICE_ID_BTC_8193 0x0002 | ||
| 328 | |||
| 329 | #define VENDOR_ID_GYRATION 0x0c16 | ||
| 330 | #define DEVICE_ID_GYRATION_REMOTE 0x0002 | ||
| 331 | |||
| 332 | #define VENDOR_ID_LOGITECH 0x046d | ||
| 333 | #define DEVICE_ID_LOGITECH_RECEIVER 0xc101 | ||
| 334 | #define DEVICE_ID_S510_RECEIVER 0xc50c | ||
| 335 | #define DEVICE_ID_S510_RECEIVER_2 0xc517 | ||
| 336 | #define DEVICE_ID_MX3000_RECEIVER 0xc513 | ||
| 337 | |||
| 338 | #define VENDOR_ID_MICROSOFT 0x045e | ||
| 339 | #define DEVICE_ID_MS4K 0x00db | ||
| 340 | #define DEVICE_ID_MS6K 0x00f9 | ||
| 341 | #define DEVICE_IS_MS_PRESENTER_8K_BT 0x0701 | ||
| 342 | #define DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 | ||
| 343 | |||
| 344 | #define VENDOR_ID_MONTEREY 0x0566 | ||
| 345 | #define DEVICE_ID_GENIUS_KB29E 0x3004 | ||
| 346 | |||
| 347 | #define VENDOR_ID_PETALYNX 0x18b1 | ||
| 348 | #define DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 | ||
| 349 | |||
| 350 | #define VENDOR_ID_SUNPLUS 0x04fc | ||
| 351 | #define DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 | ||
| 352 | |||
| 353 | static const struct hid_input_blacklist { | ||
| 354 | __u16 idVendor; | ||
| 355 | __u16 idProduct; | ||
| 356 | int (*quirk)(struct hid_usage *, struct input_dev *, unsigned long **, int *); | ||
| 357 | } hid_input_blacklist[] = { | ||
| 358 | { VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd }, | ||
| 359 | |||
| 360 | { VENDOR_ID_CHERRY, DEVICE_ID_CHERRY_CYMOTION, quirk_cherry_cymotion }, | ||
| 361 | |||
| 362 | { VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad }, | ||
| 363 | |||
| 364 | { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 }, | ||
| 365 | |||
| 366 | { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, | ||
| 367 | |||
| 368 | { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote }, | ||
| 369 | { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless }, | ||
| 370 | { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless }, | ||
| 371 | { VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless }, | ||
| 372 | |||
| 373 | { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb }, | ||
| 374 | { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb }, | ||
| 375 | { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k }, | ||
| 376 | { VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k }, | ||
| 377 | |||
| 378 | { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, | ||
| 379 | |||
| 380 | { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, | ||
| 381 | |||
| 382 | { VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop }, | ||
| 383 | |||
| 384 | { 0, 0, NULL } | ||
| 385 | }; | ||
| 386 | |||
| 387 | int hidinput_mapping_quirks(struct hid_usage *usage, | ||
| 388 | struct input_dev *input, | ||
| 389 | unsigned long **bit, int *max) | ||
| 390 | { | ||
| 391 | struct hid_device *device = input_get_drvdata(input); | ||
| 392 | int i = 0; | ||
| 393 | |||
| 394 | while (hid_input_blacklist[i].quirk) { | ||
| 395 | if (hid_input_blacklist[i].idVendor == device->vendor && | ||
| 396 | hid_input_blacklist[i].idProduct == device->product) | ||
| 397 | return hid_input_blacklist[i].quirk(usage, input, bit, max); | ||
| 398 | i++; | ||
| 399 | } | ||
| 400 | return 0; | ||
| 401 | } | ||
| 402 | |||
| 403 | int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) | ||
| 404 | { | ||
| 405 | struct input_dev *input; | ||
| 406 | |||
| 407 | input = field->hidinput->input; | ||
| 408 | |||
| 409 | if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) | ||
| 410 | || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) { | ||
| 411 | if (value) hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON; | ||
| 412 | else hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON; | ||
| 413 | return 1; | ||
| 414 | } | ||
| 415 | |||
| 416 | if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) && | ||
| 417 | (usage->type == EV_REL) && | ||
| 418 | (usage->code == REL_WHEEL)) { | ||
| 419 | hid->delayed_value = value; | ||
| 420 | return 1; | ||
| 421 | } | ||
| 422 | |||
| 423 | if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) && | ||
| 424 | (usage->hid == 0x000100b8)) { | ||
| 425 | input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, hid->delayed_value); | ||
| 426 | return 1; | ||
| 427 | } | ||
| 428 | |||
| 429 | if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) { | ||
| 430 | input_event(input, usage->type, usage->code, -value); | ||
| 431 | return 1; | ||
| 432 | } | ||
| 433 | |||
| 434 | if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) { | ||
| 435 | input_event(input, usage->type, REL_HWHEEL, value); | ||
| 436 | return 1; | ||
| 437 | } | ||
| 438 | |||
| 439 | if ((hid->quirks & HID_QUIRK_APPLE_HAS_FN) && hidinput_apple_event(hid, input, usage, value)) | ||
| 440 | return 1; | ||
| 441 | |||
| 442 | /* Handling MS keyboards special buttons */ | ||
| 443 | if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && | ||
| 444 | usage->hid == (HID_UP_MSVENDOR | 0xff05)) { | ||
| 445 | int key = 0; | ||
| 446 | static int last_key = 0; | ||
| 447 | switch (value) { | ||
| 448 | case 0x01: key = KEY_F14; break; | ||
| 449 | case 0x02: key = KEY_F15; break; | ||
| 450 | case 0x04: key = KEY_F16; break; | ||
| 451 | case 0x08: key = KEY_F17; break; | ||
| 452 | case 0x10: key = KEY_F18; break; | ||
| 453 | default: break; | ||
| 454 | } | ||
| 455 | if (key) { | ||
| 456 | input_event(input, usage->type, key, 1); | ||
| 457 | last_key = key; | ||
| 458 | } else { | ||
| 459 | input_event(input, usage->type, last_key, 0); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | /* handle the temporary quirky mapping to HWHEEL */ | ||
| 464 | if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT && | ||
| 465 | usage->type == EV_REL && usage->code == REL_HWHEEL) { | ||
| 466 | input_event(input, usage->type, REL_WHEEL, -value); | ||
| 467 | return 1; | ||
| 468 | } | ||
| 469 | |||
| 470 | /* Gyration MCE remote "Sleep" key */ | ||
| 471 | if (hid->vendor == VENDOR_ID_GYRATION && | ||
| 472 | hid->product == DEVICE_ID_GYRATION_REMOTE && | ||
| 473 | (usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK && | ||
| 474 | (usage->hid & 0xff) == 0x82) { | ||
| 475 | input_event(input, usage->type, usage->code, 1); | ||
| 476 | input_sync(input); | ||
| 477 | input_event(input, usage->type, usage->code, 0); | ||
| 478 | input_sync(input); | ||
| 479 | return 1; | ||
| 480 | } | ||
| 481 | return 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | |||
diff --git a/usr/src/dkms_source_tree/hid-input.c b/usr/src/dkms_source_tree/hid-input.c new file mode 100644 index 0000000..782048d --- /dev/null +++ b/usr/src/dkms_source_tree/hid-input.c | |||
| @@ -0,0 +1,1086 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000-2001 Vojtech Pavlik | ||
| 3 | * Copyright (c) 2006-2007 Jiri Kosina | ||
| 4 | * | ||
| 5 | * HID to Linux Input mapping | ||
| 6 | */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 22 | * | ||
| 23 | * Should you need to contact me, the author, you can do so either by | ||
| 24 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
| 25 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <linux/kernel.h> | ||
| 31 | |||
| 32 | #include <linux/hid.h> | ||
| 33 | #include <linux/hid-debug.h> | ||
| 34 | |||
| 35 | static int hid_apple_fnmode = 1; | ||
| 36 | module_param_named(pb_fnmode, hid_apple_fnmode, int, 0644); | ||
| 37 | MODULE_PARM_DESC(pb_fnmode, | ||
| 38 | "Mode of fn key on Apple keyboards (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)"); | ||
| 39 | |||
| 40 | #define unk KEY_UNKNOWN | ||
| 41 | |||
| 42 | static const unsigned char hid_keyboard[256] = { | ||
| 43 | 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, | ||
| 44 | 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, | ||
| 45 | 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, | ||
| 46 | 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, | ||
| 47 | 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, | ||
| 48 | 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, | ||
| 49 | 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, | ||
| 50 | 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, | ||
| 51 | 115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk, | ||
| 52 | 122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, | ||
| 53 | unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, | ||
| 54 | unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk, | ||
| 55 | unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, | ||
| 56 | unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, | ||
| 57 | 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, | ||
| 58 | 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk | ||
| 59 | }; | ||
| 60 | |||
| 61 | /* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */ | ||
| 62 | #define LOGITECH_EXPANDED_KEYMAP_SIZE 80 | ||
| 63 | static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = { | ||
| 64 | 0,216, 0,213,175,156, 0, 0, 0, 0, | ||
| 65 | 144, 0, 0, 0, 0, 0, 0, 0, 0,212, | ||
| 66 | 174,167,152,161,112, 0, 0, 0,154, 0, | ||
| 67 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 68 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 69 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 70 | 0, 0, 0, 0, 0,183,184,185,186,187, | ||
| 71 | 188,189,190,191,192,193,194, 0, 0, 0 | ||
| 72 | }; | ||
| 73 | |||
| 74 | static const struct { | ||
| 75 | __s32 x; | ||
| 76 | __s32 y; | ||
| 77 | } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; | ||
| 78 | |||
| 79 | #define map_abs(c) do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0) | ||
| 80 | #define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0) | ||
| 81 | #define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0) | ||
| 82 | #define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0) | ||
| 83 | |||
| 84 | #define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0) | ||
| 85 | #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) | ||
| 86 | |||
| 87 | #ifdef CONFIG_USB_HIDINPUT_POWERBOOK | ||
| 88 | |||
| 89 | struct hidinput_key_translation { | ||
| 90 | u16 from; | ||
| 91 | u16 to; | ||
| 92 | u8 flags; | ||
| 93 | }; | ||
| 94 | |||
| 95 | #define APPLE_FLAG_FKEY 0x01 | ||
| 96 | |||
| 97 | static struct hidinput_key_translation apple_fn_keys[] = { | ||
| 98 | { KEY_BACKSPACE, KEY_DELETE }, | ||
| 99 | { KEY_ENTER, KEY_INSERT }, | ||
| 100 | { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, | ||
| 101 | { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, | ||
| 102 | { KEY_F3, KEY_CYCLEWINDOWS, APPLE_FLAG_FKEY }, /* Exposé */ | ||
| 103 | { KEY_F4, KEY_CALC, APPLE_FLAG_FKEY }, /* Dashboard */ | ||
| 104 | { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, | ||
| 105 | { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, | ||
| 106 | { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, | ||
| 107 | { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, | ||
| 108 | { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, | ||
| 109 | { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, | ||
| 110 | { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, | ||
| 111 | { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, | ||
| 112 | { KEY_UP, KEY_PAGEUP }, | ||
| 113 | { KEY_DOWN, KEY_PAGEDOWN }, | ||
| 114 | { KEY_LEFT, KEY_HOME }, | ||
| 115 | { KEY_RIGHT, KEY_END }, | ||
| 116 | { } | ||
| 117 | }; | ||
| 118 | |||
| 119 | static struct hidinput_key_translation powerbook_fn_keys[] = { | ||
| 120 | { KEY_BACKSPACE, KEY_DELETE }, | ||
| 121 | { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, | ||
| 122 | { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, | ||
| 123 | { KEY_F3, KEY_MUTE, APPLE_FLAG_FKEY }, | ||
| 124 | { KEY_F4, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, | ||
| 125 | { KEY_F5, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, | ||
| 126 | { KEY_F6, KEY_NUMLOCK, APPLE_FLAG_FKEY }, | ||
| 127 | { KEY_F7, KEY_SWITCHVIDEOMODE, APPLE_FLAG_FKEY }, | ||
| 128 | { KEY_F8, KEY_KBDILLUMTOGGLE, APPLE_FLAG_FKEY }, | ||
| 129 | { KEY_F9, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, | ||
| 130 | { KEY_F10, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, | ||
| 131 | { KEY_UP, KEY_PAGEUP }, | ||
| 132 | { KEY_DOWN, KEY_PAGEDOWN }, | ||
| 133 | { KEY_LEFT, KEY_HOME }, | ||
| 134 | { KEY_RIGHT, KEY_END }, | ||
| 135 | { } | ||
| 136 | }; | ||
| 137 | |||
| 138 | static struct hidinput_key_translation powerbook_numlock_keys[] = { | ||
| 139 | { KEY_J, KEY_KP1 }, | ||
| 140 | { KEY_K, KEY_KP2 }, | ||
| 141 | { KEY_L, KEY_KP3 }, | ||
| 142 | { KEY_U, KEY_KP4 }, | ||
| 143 | { KEY_I, KEY_KP5 }, | ||
| 144 | { KEY_O, KEY_KP6 }, | ||
| 145 | { KEY_7, KEY_KP7 }, | ||
| 146 | { KEY_8, KEY_KP8 }, | ||
| 147 | { KEY_9, KEY_KP9 }, | ||
| 148 | { KEY_M, KEY_KP0 }, | ||
| 149 | { KEY_DOT, KEY_KPDOT }, | ||
| 150 | { KEY_SLASH, KEY_KPPLUS }, | ||
| 151 | { KEY_SEMICOLON, KEY_KPMINUS }, | ||
| 152 | { KEY_P, KEY_KPASTERISK }, | ||
| 153 | { KEY_MINUS, KEY_KPEQUAL }, | ||
| 154 | { KEY_0, KEY_KPSLASH }, | ||
| 155 | { KEY_F6, KEY_NUMLOCK }, | ||
| 156 | { KEY_KPENTER, KEY_KPENTER }, | ||
| 157 | { KEY_BACKSPACE, KEY_BACKSPACE }, | ||
| 158 | { } | ||
| 159 | }; | ||
| 160 | |||
| 161 | static struct hidinput_key_translation apple_iso_keyboard[] = { | ||
| 162 | { KEY_GRAVE, KEY_102ND }, | ||
| 163 | { KEY_102ND, KEY_GRAVE }, | ||
| 164 | { } | ||
| 165 | }; | ||
| 166 | |||
| 167 | static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from) | ||
| 168 | { | ||
| 169 | struct hidinput_key_translation *trans; | ||
| 170 | |||
| 171 | /* Look for the translation */ | ||
| 172 | for (trans = table; trans->from; trans++) | ||
| 173 | if (trans->from == from) | ||
| 174 | return trans; | ||
| 175 | |||
| 176 | return NULL; | ||
| 177 | } | ||
| 178 | |||
| 179 | int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, | ||
| 180 | struct hid_usage *usage, __s32 value) | ||
| 181 | { | ||
| 182 | struct hidinput_key_translation *trans; | ||
| 183 | |||
| 184 | if (usage->code == KEY_FN) { | ||
| 185 | if (value) hid->quirks |= HID_QUIRK_APPLE_FN_ON; | ||
| 186 | else hid->quirks &= ~HID_QUIRK_APPLE_FN_ON; | ||
| 187 | |||
| 188 | input_event(input, usage->type, usage->code, value); | ||
| 189 | |||
| 190 | return 1; | ||
| 191 | } | ||
| 192 | |||
| 193 | if (hid_apple_fnmode) { | ||
| 194 | int do_translate; | ||
| 195 | |||
| 196 | trans = find_translation((hid->product < 0x220 || | ||
| 197 | hid->product >= 0x300) ? | ||
| 198 | powerbook_fn_keys : apple_fn_keys, | ||
| 199 | usage->code); | ||
| 200 | if (trans) { | ||
| 201 | if (test_bit(usage->code, hid->apple_pressed_fn)) | ||
| 202 | do_translate = 1; | ||
| 203 | else if (trans->flags & APPLE_FLAG_FKEY) | ||
| 204 | do_translate = | ||
| 205 | (hid_apple_fnmode == 2 && (hid->quirks & HID_QUIRK_APPLE_FN_ON)) || | ||
| 206 | (hid_apple_fnmode == 1 && !(hid->quirks & HID_QUIRK_APPLE_FN_ON)); | ||
| 207 | else | ||
| 208 | do_translate = (hid->quirks & HID_QUIRK_APPLE_FN_ON); | ||
| 209 | |||
| 210 | if (do_translate) { | ||
| 211 | if (value) | ||
| 212 | set_bit(usage->code, hid->apple_pressed_fn); | ||
| 213 | else | ||
| 214 | clear_bit(usage->code, hid->apple_pressed_fn); | ||
| 215 | |||
| 216 | input_event(input, usage->type, trans->to, value); | ||
| 217 | |||
| 218 | return 1; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | if (hid->quirks & HID_QUIRK_APPLE_NUMLOCK_EMULATION && ( | ||
| 223 | test_bit(usage->code, hid->pb_pressed_numlock) || | ||
| 224 | test_bit(LED_NUML, input->led))) { | ||
| 225 | trans = find_translation(powerbook_numlock_keys, usage->code); | ||
| 226 | |||
| 227 | if (trans) { | ||
| 228 | if (value) | ||
| 229 | set_bit(usage->code, hid->pb_pressed_numlock); | ||
| 230 | else | ||
| 231 | clear_bit(usage->code, hid->pb_pressed_numlock); | ||
| 232 | |||
| 233 | input_event(input, usage->type, trans->to, value); | ||
| 234 | } | ||
| 235 | |||
| 236 | return 1; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | if (hid->quirks & HID_QUIRK_APPLE_ISO_KEYBOARD) { | ||
| 241 | trans = find_translation(apple_iso_keyboard, usage->code); | ||
| 242 | if (trans) { | ||
| 243 | input_event(input, usage->type, trans->to, value); | ||
| 244 | return 1; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static void hidinput_apple_setup(struct input_dev *input) | ||
| 252 | { | ||
| 253 | struct hidinput_key_translation *trans; | ||
| 254 | |||
| 255 | set_bit(KEY_NUMLOCK, input->keybit); | ||
| 256 | |||
| 257 | /* Enable all needed keys */ | ||
| 258 | for (trans = apple_fn_keys; trans->from; trans++) | ||
| 259 | set_bit(trans->to, input->keybit); | ||
| 260 | |||
| 261 | for (trans = powerbook_fn_keys; trans->from; trans++) | ||
| 262 | set_bit(trans->to, input->keybit); | ||
| 263 | |||
| 264 | for (trans = powerbook_numlock_keys; trans->from; trans++) | ||
| 265 | set_bit(trans->to, input->keybit); | ||
| 266 | |||
| 267 | for (trans = apple_iso_keyboard; trans->from; trans++) | ||
| 268 | set_bit(trans->to, input->keybit); | ||
| 269 | |||
| 270 | } | ||
| 271 | #else | ||
| 272 | inline int hidinput_apple_event(struct hid_device *hid, | ||
| 273 | struct input_dev *input, | ||
| 274 | struct hid_usage *usage, __s32 value) | ||
| 275 | { | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | static inline void hidinput_apple_setup(struct input_dev *input) | ||
| 280 | { | ||
| 281 | } | ||
| 282 | #endif | ||
| 283 | |||
| 284 | static inline int match_scancode(int code, int scancode) | ||
| 285 | { | ||
| 286 | if (scancode == 0) | ||
| 287 | return 1; | ||
| 288 | return ((code & (HID_USAGE_PAGE | HID_USAGE)) == scancode); | ||
| 289 | } | ||
| 290 | |||
| 291 | static inline int match_keycode(int code, int keycode) | ||
| 292 | { | ||
| 293 | if (keycode == 0) | ||
| 294 | return 1; | ||
| 295 | return (code == keycode); | ||
| 296 | } | ||
| 297 | |||
| 298 | static struct hid_usage *hidinput_find_key(struct hid_device *hid, | ||
| 299 | int scancode, int keycode) | ||
| 300 | { | ||
| 301 | int i, j, k; | ||
| 302 | struct hid_report *report; | ||
| 303 | struct hid_usage *usage; | ||
| 304 | |||
| 305 | for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { | ||
| 306 | list_for_each_entry(report, &hid->report_enum[k].report_list, list) { | ||
| 307 | for (i = 0; i < report->maxfield; i++) { | ||
| 308 | for ( j = 0; j < report->field[i]->maxusage; j++) { | ||
| 309 | usage = report->field[i]->usage + j; | ||
| 310 | if (usage->type == EV_KEY && | ||
| 311 | match_scancode(usage->hid, scancode) && | ||
| 312 | match_keycode(usage->code, keycode)) | ||
| 313 | return usage; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | } | ||
| 317 | } | ||
| 318 | return NULL; | ||
| 319 | } | ||
| 320 | |||
| 321 | static int hidinput_getkeycode(struct input_dev *dev, int scancode, | ||
| 322 | int *keycode) | ||
| 323 | { | ||
| 324 | struct hid_device *hid = input_get_drvdata(dev); | ||
| 325 | struct hid_usage *usage; | ||
| 326 | |||
| 327 | usage = hidinput_find_key(hid, scancode, 0); | ||
| 328 | if (usage) { | ||
| 329 | *keycode = usage->code; | ||
| 330 | return 0; | ||
| 331 | } | ||
| 332 | return -EINVAL; | ||
| 333 | } | ||
| 334 | |||
| 335 | static int hidinput_setkeycode(struct input_dev *dev, int scancode, | ||
| 336 | int keycode) | ||
| 337 | { | ||
| 338 | struct hid_device *hid = input_get_drvdata(dev); | ||
| 339 | struct hid_usage *usage; | ||
| 340 | int old_keycode; | ||
| 341 | |||
| 342 | if (keycode < 0 || keycode > KEY_MAX) | ||
| 343 | return -EINVAL; | ||
| 344 | |||
| 345 | usage = hidinput_find_key(hid, scancode, 0); | ||
| 346 | if (usage) { | ||
| 347 | old_keycode = usage->code; | ||
| 348 | usage->code = keycode; | ||
| 349 | |||
| 350 | clear_bit(old_keycode, dev->keybit); | ||
| 351 | set_bit(usage->code, dev->keybit); | ||
| 352 | dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); | ||
| 353 | /* Set the keybit for the old keycode if the old keycode is used | ||
| 354 | * by another key */ | ||
| 355 | if (hidinput_find_key (hid, 0, old_keycode)) | ||
| 356 | set_bit(old_keycode, dev->keybit); | ||
| 357 | |||
| 358 | return 0; | ||
| 359 | } | ||
| 360 | |||
| 361 | return -EINVAL; | ||
| 362 | } | ||
| 363 | |||
| 364 | |||
| 365 | static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, | ||
| 366 | struct hid_usage *usage) | ||
| 367 | { | ||
| 368 | struct input_dev *input = hidinput->input; | ||
| 369 | struct hid_device *device = input_get_drvdata(input); | ||
| 370 | int max = 0, code, ret; | ||
| 371 | unsigned long *bit = NULL; | ||
| 372 | |||
| 373 | field->hidinput = hidinput; | ||
| 374 | |||
| 375 | dbg_hid("Mapping: "); | ||
| 376 | hid_resolv_usage(usage->hid); | ||
| 377 | dbg_hid_line(" ---> "); | ||
| 378 | |||
| 379 | if (field->flags & HID_MAIN_ITEM_CONSTANT) | ||
| 380 | goto ignore; | ||
| 381 | |||
| 382 | /* only LED usages are supported in output fields */ | ||
| 383 | if (field->report_type == HID_OUTPUT_REPORT && | ||
| 384 | (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { | ||
| 385 | dbg_hid_line(" [non-LED output field] "); | ||
| 386 | goto ignore; | ||
| 387 | } | ||
| 388 | |||
| 389 | /* handle input mappings for quirky devices */ | ||
| 390 | ret = hidinput_mapping_quirks(usage, input, &bit, &max); | ||
| 391 | if (ret) | ||
| 392 | goto mapped; | ||
| 393 | |||
| 394 | switch (usage->hid & HID_USAGE_PAGE) { | ||
| 395 | |||
| 396 | case HID_UP_UNDEFINED: | ||
| 397 | goto ignore; | ||
| 398 | |||
| 399 | case HID_UP_KEYBOARD: | ||
| 400 | |||
| 401 | set_bit(EV_REP, input->evbit); | ||
| 402 | |||
| 403 | if ((usage->hid & HID_USAGE) < 256) { | ||
| 404 | if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore; | ||
| 405 | map_key_clear(hid_keyboard[usage->hid & HID_USAGE]); | ||
| 406 | } else | ||
| 407 | map_key(KEY_UNKNOWN); | ||
| 408 | |||
| 409 | break; | ||
| 410 | |||
| 411 | case HID_UP_BUTTON: | ||
| 412 | |||
| 413 | code = ((usage->hid - 1) & 0xf); | ||
| 414 | |||
| 415 | switch (field->application) { | ||
| 416 | case HID_GD_MOUSE: | ||
| 417 | case HID_GD_POINTER: code += 0x110; break; | ||
| 418 | case HID_GD_JOYSTICK: code += 0x120; break; | ||
| 419 | case HID_GD_GAMEPAD: code += 0x130; break; | ||
| 420 | default: | ||
| 421 | switch (field->physical) { | ||
| 422 | case HID_GD_MOUSE: | ||
| 423 | case HID_GD_POINTER: code += 0x110; break; | ||
| 424 | case HID_GD_JOYSTICK: code += 0x120; break; | ||
| 425 | case HID_GD_GAMEPAD: code += 0x130; break; | ||
| 426 | default: code += 0x100; | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | /* Special handling for Logitech Cordless Desktop */ | ||
| 431 | if (field->application != HID_GD_MOUSE) { | ||
| 432 | if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) { | ||
| 433 | int hid = usage->hid & HID_USAGE; | ||
| 434 | if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0) | ||
| 435 | code = logitech_expanded_keymap[hid]; | ||
| 436 | } | ||
| 437 | } else { | ||
| 438 | if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) { | ||
| 439 | int hid = usage->hid & HID_USAGE; | ||
| 440 | if (hid == 7 || hid == 8) | ||
| 441 | goto ignore; | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | map_key(code); | ||
| 446 | break; | ||
| 447 | |||
| 448 | |||
| 449 | case HID_UP_SIMULATION: | ||
| 450 | |||
| 451 | switch (usage->hid & 0xffff) { | ||
| 452 | case 0xba: map_abs(ABS_RUDDER); break; | ||
| 453 | case 0xbb: map_abs(ABS_THROTTLE); break; | ||
| 454 | case 0xc4: map_abs(ABS_GAS); break; | ||
| 455 | case 0xc5: map_abs(ABS_BRAKE); break; | ||
| 456 | case 0xc8: map_abs(ABS_WHEEL); break; | ||
| 457 | default: goto ignore; | ||
| 458 | } | ||
| 459 | break; | ||
| 460 | |||
| 461 | case HID_UP_GENDESK: | ||
| 462 | |||
| 463 | if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ | ||
| 464 | switch (usage->hid & 0xf) { | ||
| 465 | case 0x1: map_key_clear(KEY_POWER); break; | ||
| 466 | case 0x2: map_key_clear(KEY_SLEEP); break; | ||
| 467 | case 0x3: map_key_clear(KEY_WAKEUP); break; | ||
| 468 | default: goto unknown; | ||
| 469 | } | ||
| 470 | break; | ||
| 471 | } | ||
| 472 | |||
| 473 | if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ | ||
| 474 | switch (usage->hid) { | ||
| 475 | case HID_GD_UP: usage->hat_dir = 1; break; | ||
| 476 | case HID_GD_DOWN: usage->hat_dir = 5; break; | ||
| 477 | case HID_GD_RIGHT: usage->hat_dir = 3; break; | ||
| 478 | case HID_GD_LEFT: usage->hat_dir = 7; break; | ||
| 479 | default: goto unknown; | ||
| 480 | } | ||
| 481 | if (field->dpad) { | ||
| 482 | map_abs(field->dpad); | ||
| 483 | goto ignore; | ||
| 484 | } | ||
| 485 | map_abs(ABS_HAT0X); | ||
| 486 | break; | ||
| 487 | } | ||
| 488 | |||
| 489 | switch (usage->hid) { | ||
| 490 | |||
| 491 | /* These usage IDs map directly to the usage codes. */ | ||
| 492 | case HID_GD_X: case HID_GD_Y: case HID_GD_Z: | ||
| 493 | case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: | ||
| 494 | case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: | ||
| 495 | if (field->flags & HID_MAIN_ITEM_RELATIVE) | ||
| 496 | map_rel(usage->hid & 0xf); | ||
| 497 | else | ||
| 498 | map_abs(usage->hid & 0xf); | ||
| 499 | break; | ||
| 500 | |||
| 501 | case HID_GD_HATSWITCH: | ||
| 502 | usage->hat_min = field->logical_minimum; | ||
| 503 | usage->hat_max = field->logical_maximum; | ||
| 504 | map_abs(ABS_HAT0X); | ||
| 505 | break; | ||
| 506 | |||
| 507 | case HID_GD_START: map_key_clear(BTN_START); break; | ||
| 508 | case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; | ||
| 509 | |||
| 510 | default: goto unknown; | ||
| 511 | } | ||
| 512 | |||
| 513 | break; | ||
| 514 | |||
| 515 | case HID_UP_LED: | ||
| 516 | |||
| 517 | switch (usage->hid & 0xffff) { /* HID-Value: */ | ||
| 518 | case 0x01: map_led (LED_NUML); break; /* "Num Lock" */ | ||
| 519 | case 0x02: map_led (LED_CAPSL); break; /* "Caps Lock" */ | ||
| 520 | case 0x03: map_led (LED_SCROLLL); break; /* "Scroll Lock" */ | ||
| 521 | case 0x04: map_led (LED_COMPOSE); break; /* "Compose" */ | ||
| 522 | case 0x05: map_led (LED_KANA); break; /* "Kana" */ | ||
| 523 | case 0x27: map_led (LED_SLEEP); break; /* "Stand-By" */ | ||
| 524 | case 0x4c: map_led (LED_SUSPEND); break; /* "System Suspend" */ | ||
| 525 | case 0x09: map_led (LED_MUTE); break; /* "Mute" */ | ||
| 526 | case 0x4b: map_led (LED_MISC); break; /* "Generic Indicator" */ | ||
| 527 | case 0x19: map_led (LED_MAIL); break; /* "Message Waiting" */ | ||
| 528 | case 0x4d: map_led (LED_CHARGING); break; /* "External Power Connected" */ | ||
| 529 | |||
| 530 | default: goto ignore; | ||
| 531 | } | ||
| 532 | break; | ||
| 533 | |||
| 534 | case HID_UP_DIGITIZER: | ||
| 535 | |||
| 536 | switch (usage->hid & 0xff) { | ||
| 537 | |||
| 538 | case 0x30: /* TipPressure */ | ||
| 539 | if (!test_bit(BTN_TOUCH, input->keybit)) { | ||
| 540 | device->quirks |= HID_QUIRK_NOTOUCH; | ||
| 541 | set_bit(EV_KEY, input->evbit); | ||
| 542 | set_bit(BTN_TOUCH, input->keybit); | ||
| 543 | } | ||
| 544 | |||
| 545 | map_abs_clear(ABS_PRESSURE); | ||
| 546 | break; | ||
| 547 | |||
| 548 | case 0x32: /* InRange */ | ||
| 549 | switch (field->physical & 0xff) { | ||
| 550 | case 0x21: map_key(BTN_TOOL_MOUSE); break; | ||
| 551 | case 0x22: map_key(BTN_TOOL_FINGER); break; | ||
| 552 | default: map_key(BTN_TOOL_PEN); break; | ||
| 553 | } | ||
| 554 | break; | ||
| 555 | |||
| 556 | case 0x3c: /* Invert */ | ||
| 557 | map_key_clear(BTN_TOOL_RUBBER); | ||
| 558 | break; | ||
| 559 | |||
| 560 | case 0x33: /* Touch */ | ||
| 561 | case 0x42: /* TipSwitch */ | ||
| 562 | case 0x43: /* TipSwitch2 */ | ||
| 563 | device->quirks &= ~HID_QUIRK_NOTOUCH; | ||
| 564 | map_key_clear(BTN_TOUCH); | ||
| 565 | break; | ||
| 566 | |||
| 567 | case 0x44: /* BarrelSwitch */ | ||
| 568 | map_key_clear(BTN_STYLUS); | ||
| 569 | break; | ||
| 570 | |||
| 571 | default: goto unknown; | ||
| 572 | } | ||
| 573 | break; | ||
| 574 | |||
| 575 | case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ | ||
| 576 | |||
| 577 | switch (usage->hid & HID_USAGE) { | ||
| 578 | case 0x000: goto ignore; | ||
| 579 | case 0x034: map_key_clear(KEY_SLEEP); break; | ||
| 580 | case 0x036: map_key_clear(BTN_MISC); break; | ||
| 581 | |||
| 582 | case 0x040: map_key_clear(KEY_MENU); break; | ||
| 583 | case 0x045: map_key_clear(KEY_RADIO); break; | ||
| 584 | |||
| 585 | case 0x083: map_key_clear(KEY_LAST); break; | ||
| 586 | case 0x088: map_key_clear(KEY_PC); break; | ||
| 587 | case 0x089: map_key_clear(KEY_TV); break; | ||
| 588 | case 0x08a: map_key_clear(KEY_WWW); break; | ||
| 589 | case 0x08b: map_key_clear(KEY_DVD); break; | ||
| 590 | case 0x08c: map_key_clear(KEY_PHONE); break; | ||
| 591 | case 0x08d: map_key_clear(KEY_PROGRAM); break; | ||
| 592 | case 0x08e: map_key_clear(KEY_VIDEOPHONE); break; | ||
| 593 | case 0x08f: map_key_clear(KEY_GAMES); break; | ||
| 594 | case 0x090: map_key_clear(KEY_MEMO); break; | ||
| 595 | case 0x091: map_key_clear(KEY_CD); break; | ||
| 596 | case 0x092: map_key_clear(KEY_VCR); break; | ||
| 597 | case 0x093: map_key_clear(KEY_TUNER); break; | ||
| 598 | case 0x094: map_key_clear(KEY_EXIT); break; | ||
| 599 | case 0x095: map_key_clear(KEY_HELP); break; | ||
| 600 | case 0x096: map_key_clear(KEY_TAPE); break; | ||
| 601 | case 0x097: map_key_clear(KEY_TV2); break; | ||
| 602 | case 0x098: map_key_clear(KEY_SAT); break; | ||
| 603 | case 0x09a: map_key_clear(KEY_PVR); break; | ||
| 604 | |||
| 605 | case 0x09c: map_key_clear(KEY_CHANNELUP); break; | ||
| 606 | case 0x09d: map_key_clear(KEY_CHANNELDOWN); break; | ||
| 607 | case 0x0a0: map_key_clear(KEY_VCR2); break; | ||
| 608 | |||
| 609 | case 0x0b0: map_key_clear(KEY_PLAY); break; | ||
| 610 | case 0x0b1: map_key_clear(KEY_PAUSE); break; | ||
| 611 | case 0x0b2: map_key_clear(KEY_RECORD); break; | ||
| 612 | case 0x0b3: map_key_clear(KEY_FASTFORWARD); break; | ||
| 613 | case 0x0b4: map_key_clear(KEY_REWIND); break; | ||
| 614 | case 0x0b5: map_key_clear(KEY_NEXTSONG); break; | ||
| 615 | case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break; | ||
| 616 | case 0x0b7: map_key_clear(KEY_STOPCD); break; | ||
| 617 | case 0x0b8: map_key_clear(KEY_EJECTCD); break; | ||
| 618 | case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT); break; | ||
| 619 | |||
| 620 | case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; | ||
| 621 | case 0x0e0: map_abs_clear(ABS_VOLUME); break; | ||
| 622 | case 0x0e2: map_key_clear(KEY_MUTE); break; | ||
| 623 | case 0x0e5: map_key_clear(KEY_BASSBOOST); break; | ||
| 624 | case 0x0e9: map_key_clear(KEY_VOLUMEUP); break; | ||
| 625 | case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; | ||
| 626 | |||
| 627 | case 0x182: map_key_clear(KEY_BOOKMARKS); break; | ||
| 628 | case 0x183: map_key_clear(KEY_CONFIG); break; | ||
| 629 | case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; | ||
| 630 | case 0x185: map_key_clear(KEY_EDITOR); break; | ||
| 631 | case 0x186: map_key_clear(KEY_SPREADSHEET); break; | ||
| 632 | case 0x187: map_key_clear(KEY_GRAPHICSEDITOR); break; | ||
| 633 | case 0x188: map_key_clear(KEY_PRESENTATION); break; | ||
| 634 | case 0x189: map_key_clear(KEY_DATABASE); break; | ||
| 635 | case 0x18a: map_key_clear(KEY_MAIL); break; | ||
| 636 | case 0x18b: map_key_clear(KEY_NEWS); break; | ||
| 637 | case 0x18c: map_key_clear(KEY_VOICEMAIL); break; | ||
| 638 | case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; | ||
| 639 | case 0x18e: map_key_clear(KEY_CALENDAR); break; | ||
| 640 | case 0x191: map_key_clear(KEY_FINANCE); break; | ||
| 641 | case 0x192: map_key_clear(KEY_CALC); break; | ||
| 642 | case 0x194: map_key_clear(KEY_FILE); break; | ||
| 643 | case 0x196: map_key_clear(KEY_WWW); break; | ||
| 644 | case 0x19c: map_key_clear(KEY_LOGOFF); break; | ||
| 645 | case 0x19e: map_key_clear(KEY_COFFEE); break; | ||
| 646 | case 0x1a6: map_key_clear(KEY_HELP); break; | ||
| 647 | case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; | ||
| 648 | case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; | ||
| 649 | case 0x1b6: map_key_clear(KEY_MEDIA); break; | ||
| 650 | case 0x1b7: map_key_clear(KEY_SOUND); break; | ||
| 651 | case 0x1bc: map_key_clear(KEY_MESSENGER); break; | ||
| 652 | case 0x1bd: map_key_clear(KEY_INFO); break; | ||
| 653 | case 0x201: map_key_clear(KEY_NEW); break; | ||
| 654 | case 0x202: map_key_clear(KEY_OPEN); break; | ||
| 655 | case 0x203: map_key_clear(KEY_CLOSE); break; | ||
| 656 | case 0x204: map_key_clear(KEY_EXIT); break; | ||
| 657 | case 0x207: map_key_clear(KEY_SAVE); break; | ||
| 658 | case 0x208: map_key_clear(KEY_PRINT); break; | ||
| 659 | case 0x209: map_key_clear(KEY_PROPS); break; | ||
| 660 | case 0x21a: map_key_clear(KEY_UNDO); break; | ||
| 661 | case 0x21b: map_key_clear(KEY_COPY); break; | ||
| 662 | case 0x21c: map_key_clear(KEY_CUT); break; | ||
| 663 | case 0x21d: map_key_clear(KEY_PASTE); break; | ||
| 664 | case 0x21f: map_key_clear(KEY_FIND); break; | ||
| 665 | case 0x221: map_key_clear(KEY_SEARCH); break; | ||
| 666 | case 0x222: map_key_clear(KEY_GOTO); break; | ||
| 667 | case 0x223: map_key_clear(KEY_HOMEPAGE); break; | ||
| 668 | case 0x224: map_key_clear(KEY_BACK); break; | ||
| 669 | case 0x225: map_key_clear(KEY_FORWARD); break; | ||
| 670 | case 0x226: map_key_clear(KEY_STOP); break; | ||
| 671 | case 0x227: map_key_clear(KEY_REFRESH); break; | ||
| 672 | case 0x22a: map_key_clear(KEY_BOOKMARKS); break; | ||
| 673 | case 0x22d: map_key_clear(KEY_ZOOMIN); break; | ||
| 674 | case 0x22e: map_key_clear(KEY_ZOOMOUT); break; | ||
| 675 | case 0x22f: map_key_clear(KEY_ZOOMRESET); break; | ||
| 676 | case 0x233: map_key_clear(KEY_SCROLLUP); break; | ||
| 677 | case 0x234: map_key_clear(KEY_SCROLLDOWN); break; | ||
| 678 | case 0x238: map_rel(REL_HWHEEL); break; | ||
| 679 | case 0x25f: map_key_clear(KEY_CANCEL); break; | ||
| 680 | case 0x279: map_key_clear(KEY_REDO); break; | ||
| 681 | |||
| 682 | case 0x289: map_key_clear(KEY_REPLY); break; | ||
| 683 | case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; | ||
| 684 | case 0x28c: map_key_clear(KEY_SEND); break; | ||
| 685 | |||
| 686 | default: goto ignore; | ||
| 687 | } | ||
| 688 | break; | ||
| 689 | |||
| 690 | case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ | ||
| 691 | |||
| 692 | set_bit(EV_REP, input->evbit); | ||
| 693 | switch (usage->hid & HID_USAGE) { | ||
| 694 | case 0x021: map_key_clear(KEY_PRINT); break; | ||
| 695 | case 0x070: map_key_clear(KEY_HP); break; | ||
| 696 | case 0x071: map_key_clear(KEY_CAMERA); break; | ||
| 697 | case 0x072: map_key_clear(KEY_SOUND); break; | ||
| 698 | case 0x073: map_key_clear(KEY_QUESTION); break; | ||
| 699 | case 0x080: map_key_clear(KEY_EMAIL); break; | ||
| 700 | case 0x081: map_key_clear(KEY_CHAT); break; | ||
| 701 | case 0x082: map_key_clear(KEY_SEARCH); break; | ||
| 702 | case 0x083: map_key_clear(KEY_CONNECT); break; | ||
| 703 | case 0x084: map_key_clear(KEY_FINANCE); break; | ||
| 704 | case 0x085: map_key_clear(KEY_SPORT); break; | ||
| 705 | case 0x086: map_key_clear(KEY_SHOP); break; | ||
| 706 | default: goto ignore; | ||
| 707 | } | ||
| 708 | break; | ||
| 709 | |||
| 710 | case HID_UP_MSVENDOR: | ||
| 711 | |||
| 712 | goto ignore; | ||
| 713 | |||
| 714 | case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */ | ||
| 715 | |||
| 716 | set_bit(EV_REP, input->evbit); | ||
| 717 | switch(usage->hid & HID_USAGE) { | ||
| 718 | case 0x003: | ||
| 719 | /* The fn key on Apple USB keyboards */ | ||
| 720 | map_key_clear(KEY_FN); | ||
| 721 | hidinput_apple_setup(input); | ||
| 722 | break; | ||
| 723 | |||
| 724 | default: goto ignore; | ||
| 725 | } | ||
| 726 | break; | ||
| 727 | |||
| 728 | case HID_UP_LOGIVENDOR: | ||
| 729 | |||
| 730 | goto ignore; | ||
| 731 | |||
| 732 | case HID_UP_PID: | ||
| 733 | |||
| 734 | switch(usage->hid & HID_USAGE) { | ||
| 735 | case 0xa4: map_key_clear(BTN_DEAD); break; | ||
| 736 | default: goto ignore; | ||
| 737 | } | ||
| 738 | break; | ||
| 739 | |||
| 740 | default: | ||
| 741 | unknown: | ||
| 742 | if (field->report_size == 1) { | ||
| 743 | if (field->report->type == HID_OUTPUT_REPORT) { | ||
| 744 | map_led(LED_MISC); | ||
| 745 | break; | ||
| 746 | } | ||
| 747 | map_key(BTN_MISC); | ||
| 748 | break; | ||
| 749 | } | ||
| 750 | if (field->flags & HID_MAIN_ITEM_RELATIVE) { | ||
| 751 | map_rel(REL_MISC); | ||
| 752 | break; | ||
| 753 | } | ||
| 754 | map_abs(ABS_MISC); | ||
| 755 | break; | ||
| 756 | } | ||
| 757 | |||
| 758 | mapped: | ||
| 759 | if (device->quirks & HID_QUIRK_MIGHTYMOUSE) { | ||
| 760 | if (usage->hid == HID_GD_Z) | ||
| 761 | map_rel(REL_HWHEEL); | ||
| 762 | else if (usage->code == BTN_1) | ||
| 763 | map_key(BTN_2); | ||
| 764 | else if (usage->code == BTN_2) | ||
| 765 | map_key(BTN_1); | ||
| 766 | } | ||
| 767 | |||
| 768 | if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 | | ||
| 769 | HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) && | ||
| 770 | (usage->code == REL_WHEEL)) | ||
| 771 | set_bit(REL_HWHEEL, bit); | ||
| 772 | |||
| 773 | if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) | ||
| 774 | || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) | ||
| 775 | goto ignore; | ||
| 776 | |||
| 777 | if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) && | ||
| 778 | usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE)) | ||
| 779 | field->flags &= ~HID_MAIN_ITEM_RELATIVE; | ||
| 780 | |||
| 781 | set_bit(usage->type, input->evbit); | ||
| 782 | |||
| 783 | if (device->quirks & HID_QUIRK_DUPLICATE_USAGES && | ||
| 784 | (usage->type == EV_KEY || | ||
| 785 | usage->type == EV_REL || | ||
| 786 | usage->type == EV_ABS)) | ||
| 787 | clear_bit(usage->code, bit); | ||
| 788 | |||
| 789 | while (usage->code <= max && test_and_set_bit(usage->code, bit)) | ||
| 790 | usage->code = find_next_zero_bit(bit, max + 1, usage->code); | ||
| 791 | |||
| 792 | if (usage->code > max) | ||
| 793 | goto ignore; | ||
| 794 | |||
| 795 | |||
| 796 | if (usage->type == EV_ABS) { | ||
| 797 | |||
| 798 | int a = field->logical_minimum; | ||
| 799 | int b = field->logical_maximum; | ||
| 800 | |||
| 801 | if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) { | ||
| 802 | a = field->logical_minimum = 0; | ||
| 803 | b = field->logical_maximum = 255; | ||
| 804 | } | ||
| 805 | |||
| 806 | if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK) | ||
| 807 | input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); | ||
| 808 | else input_set_abs_params(input, usage->code, a, b, 0, 0); | ||
| 809 | |||
| 810 | } | ||
| 811 | |||
| 812 | if (usage->type == EV_ABS && | ||
| 813 | (usage->hat_min < usage->hat_max || usage->hat_dir)) { | ||
| 814 | int i; | ||
| 815 | for (i = usage->code; i < usage->code + 2 && i <= max; i++) { | ||
| 816 | input_set_abs_params(input, i, -1, 1, 0, 0); | ||
| 817 | set_bit(i, input->absbit); | ||
| 818 | } | ||
| 819 | if (usage->hat_dir && !field->dpad) | ||
| 820 | field->dpad = usage->code; | ||
| 821 | } | ||
| 822 | |||
| 823 | /* for those devices which produce Consumer volume usage as relative, | ||
| 824 | * we emulate pressing volumeup/volumedown appropriate number of times | ||
| 825 | * in hidinput_hid_event() | ||
| 826 | */ | ||
| 827 | if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && | ||
| 828 | (usage->code == ABS_VOLUME)) { | ||
| 829 | set_bit(KEY_VOLUMEUP, input->keybit); | ||
| 830 | set_bit(KEY_VOLUMEDOWN, input->keybit); | ||
| 831 | } | ||
| 832 | |||
| 833 | if (usage->type == EV_KEY) { | ||
| 834 | set_bit(EV_MSC, input->evbit); | ||
| 835 | set_bit(MSC_SCAN, input->mscbit); | ||
| 836 | } | ||
| 837 | |||
| 838 | hid_resolv_event(usage->type, usage->code); | ||
| 839 | |||
| 840 | dbg_hid_line("\n"); | ||
| 841 | |||
| 842 | return; | ||
| 843 | |||
| 844 | ignore: | ||
| 845 | dbg_hid_line("IGNORED\n"); | ||
| 846 | return; | ||
| 847 | } | ||
| 848 | |||
| 849 | void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) | ||
| 850 | { | ||
| 851 | struct input_dev *input; | ||
| 852 | unsigned *quirks = &hid->quirks; | ||
| 853 | |||
| 854 | if (!field->hidinput) | ||
| 855 | return; | ||
| 856 | |||
| 857 | input = field->hidinput->input; | ||
| 858 | |||
| 859 | if (!usage->type) | ||
| 860 | return; | ||
| 861 | |||
| 862 | /* handle input events for quirky devices */ | ||
| 863 | if (hidinput_event_quirks(hid, field, usage, value)) | ||
| 864 | return; | ||
| 865 | |||
| 866 | if (usage->hat_min < usage->hat_max || usage->hat_dir) { | ||
| 867 | int hat_dir = usage->hat_dir; | ||
| 868 | if (!hat_dir) | ||
| 869 | hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; | ||
| 870 | if (hat_dir < 0 || hat_dir > 8) hat_dir = 0; | ||
| 871 | input_event(input, usage->type, usage->code , hid_hat_to_axis[hat_dir].x); | ||
| 872 | input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y); | ||
| 873 | return; | ||
| 874 | } | ||
| 875 | |||
| 876 | if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */ | ||
| 877 | *quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT); | ||
| 878 | return; | ||
| 879 | } | ||
| 880 | |||
| 881 | if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */ | ||
| 882 | if (value) { | ||
| 883 | input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1); | ||
| 884 | return; | ||
| 885 | } | ||
| 886 | input_event(input, usage->type, usage->code, 0); | ||
| 887 | input_event(input, usage->type, BTN_TOOL_RUBBER, 0); | ||
| 888 | return; | ||
| 889 | } | ||
| 890 | |||
| 891 | if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */ | ||
| 892 | int a = field->logical_minimum; | ||
| 893 | int b = field->logical_maximum; | ||
| 894 | input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3)); | ||
| 895 | } | ||
| 896 | |||
| 897 | if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */ | ||
| 898 | dbg_hid("Maximum Effects - %d\n",value); | ||
| 899 | return; | ||
| 900 | } | ||
| 901 | |||
| 902 | if (usage->hid == (HID_UP_PID | 0x7fUL)) { | ||
| 903 | dbg_hid("PID Pool Report\n"); | ||
| 904 | return; | ||
| 905 | } | ||
| 906 | |||
| 907 | if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ | ||
| 908 | return; | ||
| 909 | |||
| 910 | if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && | ||
| 911 | (usage->code == ABS_VOLUME)) { | ||
| 912 | int count = abs(value); | ||
| 913 | int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN; | ||
| 914 | int i; | ||
| 915 | |||
| 916 | for (i = 0; i < count; i++) { | ||
| 917 | input_event(input, EV_KEY, direction, 1); | ||
| 918 | input_sync(input); | ||
| 919 | input_event(input, EV_KEY, direction, 0); | ||
| 920 | input_sync(input); | ||
| 921 | } | ||
| 922 | return; | ||
| 923 | } | ||
| 924 | |||
| 925 | /* report the usage code as scancode if the key status has changed */ | ||
| 926 | if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value) | ||
| 927 | input_event(input, EV_MSC, MSC_SCAN, usage->hid); | ||
| 928 | |||
| 929 | input_event(input, usage->type, usage->code, value); | ||
| 930 | |||
| 931 | if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) | ||
| 932 | input_event(input, usage->type, usage->code, 0); | ||
| 933 | } | ||
| 934 | |||
| 935 | void hidinput_report_event(struct hid_device *hid, struct hid_report *report) | ||
| 936 | { | ||
| 937 | struct hid_input *hidinput; | ||
| 938 | |||
| 939 | list_for_each_entry(hidinput, &hid->inputs, list) | ||
| 940 | input_sync(hidinput->input); | ||
| 941 | } | ||
| 942 | EXPORT_SYMBOL_GPL(hidinput_report_event); | ||
| 943 | |||
| 944 | int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field) | ||
| 945 | { | ||
| 946 | struct hid_report *report; | ||
| 947 | int i, j; | ||
| 948 | |||
| 949 | list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) { | ||
| 950 | for (i = 0; i < report->maxfield; i++) { | ||
| 951 | *field = report->field[i]; | ||
| 952 | for (j = 0; j < (*field)->maxusage; j++) | ||
| 953 | if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) | ||
| 954 | return j; | ||
| 955 | } | ||
| 956 | } | ||
| 957 | return -1; | ||
| 958 | } | ||
| 959 | EXPORT_SYMBOL_GPL(hidinput_find_field); | ||
| 960 | |||
| 961 | static int hidinput_open(struct input_dev *dev) | ||
| 962 | { | ||
| 963 | struct hid_device *hid = input_get_drvdata(dev); | ||
| 964 | |||
| 965 | return hid->hid_open(hid); | ||
| 966 | } | ||
| 967 | |||
| 968 | static void hidinput_close(struct input_dev *dev) | ||
| 969 | { | ||
| 970 | struct hid_device *hid = input_get_drvdata(dev); | ||
| 971 | |||
| 972 | hid->hid_close(hid); | ||
| 973 | } | ||
| 974 | |||
| 975 | /* | ||
| 976 | * Register the input device; print a message. | ||
| 977 | * Configure the input layer interface | ||
| 978 | * Read all reports and initialize the absolute field values. | ||
| 979 | */ | ||
| 980 | |||
| 981 | int hidinput_connect(struct hid_device *hid) | ||
| 982 | { | ||
| 983 | struct hid_report *report; | ||
| 984 | struct hid_input *hidinput = NULL; | ||
| 985 | struct input_dev *input_dev; | ||
| 986 | int i, j, k; | ||
| 987 | int max_report_type = HID_OUTPUT_REPORT; | ||
| 988 | |||
| 989 | if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT) | ||
| 990 | return -1; | ||
| 991 | |||
| 992 | INIT_LIST_HEAD(&hid->inputs); | ||
| 993 | |||
| 994 | for (i = 0; i < hid->maxcollection; i++) | ||
| 995 | if (hid->collection[i].type == HID_COLLECTION_APPLICATION || | ||
| 996 | hid->collection[i].type == HID_COLLECTION_PHYSICAL) | ||
| 997 | if (IS_INPUT_APPLICATION(hid->collection[i].usage)) | ||
| 998 | break; | ||
| 999 | |||
| 1000 | if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0) | ||
| 1001 | return -1; | ||
| 1002 | |||
| 1003 | if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) | ||
| 1004 | max_report_type = HID_INPUT_REPORT; | ||
| 1005 | |||
| 1006 | for (k = HID_INPUT_REPORT; k <= max_report_type; k++) | ||
| 1007 | list_for_each_entry(report, &hid->report_enum[k].report_list, list) { | ||
| 1008 | |||
| 1009 | if (!report->maxfield) | ||
| 1010 | continue; | ||
| 1011 | |||
| 1012 | if (!hidinput) { | ||
| 1013 | hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); | ||
| 1014 | input_dev = input_allocate_device(); | ||
| 1015 | if (!hidinput || !input_dev) { | ||
| 1016 | kfree(hidinput); | ||
| 1017 | input_free_device(input_dev); | ||
| 1018 | err_hid("Out of memory during hid input probe"); | ||
| 1019 | goto out_unwind; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | input_set_drvdata(input_dev, hid); | ||
| 1023 | input_dev->event = hid->hidinput_input_event; | ||
| 1024 | input_dev->open = hidinput_open; | ||
| 1025 | input_dev->close = hidinput_close; | ||
| 1026 | input_dev->setkeycode = hidinput_setkeycode; | ||
| 1027 | input_dev->getkeycode = hidinput_getkeycode; | ||
| 1028 | |||
| 1029 | input_dev->name = hid->name; | ||
| 1030 | input_dev->phys = hid->phys; | ||
| 1031 | input_dev->uniq = hid->uniq; | ||
| 1032 | input_dev->id.bustype = hid->bus; | ||
| 1033 | input_dev->id.vendor = hid->vendor; | ||
| 1034 | input_dev->id.product = hid->product; | ||
| 1035 | input_dev->id.version = hid->version; | ||
| 1036 | input_dev->dev.parent = hid->dev; | ||
| 1037 | hidinput->input = input_dev; | ||
| 1038 | list_add_tail(&hidinput->list, &hid->inputs); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | for (i = 0; i < report->maxfield; i++) | ||
| 1042 | for (j = 0; j < report->field[i]->maxusage; j++) | ||
| 1043 | hidinput_configure_usage(hidinput, report->field[i], | ||
| 1044 | report->field[i]->usage + j); | ||
| 1045 | |||
| 1046 | if (hid->quirks & HID_QUIRK_MULTI_INPUT) { | ||
| 1047 | /* This will leave hidinput NULL, so that it | ||
| 1048 | * allocates another one if we have more inputs on | ||
| 1049 | * the same interface. Some devices (e.g. Happ's | ||
| 1050 | * UGCI) cram a lot of unrelated inputs into the | ||
| 1051 | * same interface. */ | ||
| 1052 | hidinput->report = report; | ||
| 1053 | if (input_register_device(hidinput->input)) | ||
| 1054 | goto out_cleanup; | ||
| 1055 | hidinput = NULL; | ||
| 1056 | } | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | if (hidinput && input_register_device(hidinput->input)) | ||
| 1060 | goto out_cleanup; | ||
| 1061 | |||
| 1062 | return 0; | ||
| 1063 | |||
| 1064 | out_cleanup: | ||
| 1065 | input_free_device(hidinput->input); | ||
| 1066 | kfree(hidinput); | ||
| 1067 | out_unwind: | ||
| 1068 | /* unwind the ones we already registered */ | ||
| 1069 | hidinput_disconnect(hid); | ||
| 1070 | |||
| 1071 | return -1; | ||
| 1072 | } | ||
| 1073 | EXPORT_SYMBOL_GPL(hidinput_connect); | ||
| 1074 | |||
| 1075 | void hidinput_disconnect(struct hid_device *hid) | ||
| 1076 | { | ||
| 1077 | struct hid_input *hidinput, *next; | ||
| 1078 | |||
| 1079 | list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { | ||
| 1080 | list_del(&hidinput->list); | ||
| 1081 | input_unregister_device(hidinput->input); | ||
| 1082 | kfree(hidinput); | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | EXPORT_SYMBOL_GPL(hidinput_disconnect); | ||
| 1086 | |||
diff --git a/usr/src/dkms_source_tree/hidraw.c b/usr/src/dkms_source_tree/hidraw.c new file mode 100644 index 0000000..c40f040 --- /dev/null +++ b/usr/src/dkms_source_tree/hidraw.c | |||
| @@ -0,0 +1,415 @@ | |||
| 1 | /* | ||
| 2 | * HID raw devices, giving access to raw HID events. | ||
| 3 | * | ||
| 4 | * In comparison to hiddev, this device does not process the | ||
| 5 | * hid events at all (no parsing, no lookups). This lets applications | ||
| 6 | * to work on raw hid events as they want to, and avoids a need to | ||
| 7 | * use a transport-specific userspace libhid/libusb libraries. | ||
| 8 | * | ||
| 9 | * Copyright (c) 2007 Jiri Kosina | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * This program is free software; you can redistribute it and/or modify it | ||
| 14 | * under the terms and conditions of the GNU General Public License, | ||
| 15 | * version 2, as published by the Free Software Foundation. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along with | ||
| 18 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/fs.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/errno.h> | ||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/init.h> | ||
| 27 | #include <linux/cdev.h> | ||
| 28 | #include <linux/poll.h> | ||
| 29 | #include <linux/device.h> | ||
| 30 | #include <linux/major.h> | ||
| 31 | #include <linux/hid.h> | ||
| 32 | #include <linux/mutex.h> | ||
| 33 | #include <linux/smp_lock.h> | ||
| 34 | |||
| 35 | #include <linux/hidraw.h> | ||
| 36 | |||
| 37 | static int hidraw_major; | ||
| 38 | static struct cdev hidraw_cdev; | ||
| 39 | static struct class *hidraw_class; | ||
| 40 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; | ||
| 41 | static DEFINE_SPINLOCK(minors_lock); | ||
| 42 | |||
| 43 | static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | ||
| 44 | { | ||
| 45 | struct hidraw_list *list = file->private_data; | ||
| 46 | int ret = 0, len; | ||
| 47 | char *report; | ||
| 48 | DECLARE_WAITQUEUE(wait, current); | ||
| 49 | |||
| 50 | while (ret == 0) { | ||
| 51 | |||
| 52 | mutex_lock(&list->read_mutex); | ||
| 53 | |||
| 54 | if (list->head == list->tail) { | ||
| 55 | add_wait_queue(&list->hidraw->wait, &wait); | ||
| 56 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 57 | |||
| 58 | while (list->head == list->tail) { | ||
| 59 | if (file->f_flags & O_NONBLOCK) { | ||
| 60 | ret = -EAGAIN; | ||
| 61 | break; | ||
| 62 | } | ||
| 63 | if (signal_pending(current)) { | ||
| 64 | ret = -ERESTARTSYS; | ||
| 65 | break; | ||
| 66 | } | ||
| 67 | if (!list->hidraw->exist) { | ||
| 68 | ret = -EIO; | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | |||
| 72 | /* allow O_NONBLOCK to work well from other threads */ | ||
| 73 | mutex_unlock(&list->read_mutex); | ||
| 74 | schedule(); | ||
| 75 | mutex_lock(&list->read_mutex); | ||
| 76 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 77 | } | ||
| 78 | |||
| 79 | set_current_state(TASK_RUNNING); | ||
| 80 | remove_wait_queue(&list->hidraw->wait, &wait); | ||
| 81 | } | ||
| 82 | |||
| 83 | if (ret) | ||
| 84 | goto out; | ||
| 85 | |||
| 86 | report = list->buffer[list->tail].value; | ||
| 87 | len = list->buffer[list->tail].len > count ? | ||
| 88 | count : list->buffer[list->tail].len; | ||
| 89 | |||
| 90 | if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { | ||
| 91 | ret = -EFAULT; | ||
| 92 | goto out; | ||
| 93 | } | ||
| 94 | ret += len; | ||
| 95 | |||
| 96 | kfree(list->buffer[list->tail].value); | ||
| 97 | list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); | ||
| 98 | } | ||
| 99 | out: | ||
| 100 | mutex_unlock(&list->read_mutex); | ||
| 101 | return ret; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* the first byte is expected to be a report number */ | ||
| 105 | static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) | ||
| 106 | { | ||
| 107 | unsigned int minor = iminor(file->f_path.dentry->d_inode); | ||
| 108 | /* FIXME: What stops hidraw_table going NULL */ | ||
| 109 | struct hid_device *dev = hidraw_table[minor]->hid; | ||
| 110 | __u8 *buf; | ||
| 111 | int ret = 0; | ||
| 112 | |||
| 113 | if (!dev->hid_output_raw_report) | ||
| 114 | return -ENODEV; | ||
| 115 | |||
| 116 | if (count > HID_MIN_BUFFER_SIZE) { | ||
| 117 | printk(KERN_WARNING "hidraw: pid %d passed too large report\n", | ||
| 118 | task_pid_nr(current)); | ||
| 119 | return -EINVAL; | ||
| 120 | } | ||
| 121 | |||
| 122 | if (count < 2) { | ||
| 123 | printk(KERN_WARNING "hidraw: pid %d passed too short report\n", | ||
| 124 | task_pid_nr(current)); | ||
| 125 | return -EINVAL; | ||
| 126 | } | ||
| 127 | |||
| 128 | buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); | ||
| 129 | if (!buf) | ||
| 130 | return -ENOMEM; | ||
| 131 | |||
| 132 | if (copy_from_user(buf, buffer, count)) { | ||
| 133 | ret = -EFAULT; | ||
| 134 | goto out; | ||
| 135 | } | ||
| 136 | |||
| 137 | ret = dev->hid_output_raw_report(dev, buf, count); | ||
| 138 | out: | ||
| 139 | kfree(buf); | ||
| 140 | return ret; | ||
| 141 | } | ||
| 142 | |||
| 143 | static unsigned int hidraw_poll(struct file *file, poll_table *wait) | ||
| 144 | { | ||
| 145 | struct hidraw_list *list = file->private_data; | ||
| 146 | |||
| 147 | poll_wait(file, &list->hidraw->wait, wait); | ||
| 148 | if (list->head != list->tail) | ||
| 149 | return POLLIN | POLLRDNORM; | ||
| 150 | if (!list->hidraw->exist) | ||
| 151 | return POLLERR | POLLHUP; | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static int hidraw_open(struct inode *inode, struct file *file) | ||
| 156 | { | ||
| 157 | unsigned int minor = iminor(inode); | ||
| 158 | struct hidraw *dev; | ||
| 159 | struct hidraw_list *list; | ||
| 160 | int err = 0; | ||
| 161 | |||
| 162 | lock_kernel(); | ||
| 163 | if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { | ||
| 164 | err = -ENOMEM; | ||
| 165 | goto out; | ||
| 166 | } | ||
| 167 | |||
| 168 | spin_lock(&minors_lock); | ||
| 169 | if (!hidraw_table[minor]) { | ||
| 170 | printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", | ||
| 171 | minor); | ||
| 172 | kfree(list); | ||
| 173 | err = -ENODEV; | ||
| 174 | goto out_unlock; | ||
| 175 | } | ||
| 176 | |||
| 177 | list->hidraw = hidraw_table[minor]; | ||
| 178 | mutex_init(&list->read_mutex); | ||
| 179 | list_add_tail(&list->node, &hidraw_table[minor]->list); | ||
| 180 | file->private_data = list; | ||
| 181 | |||
| 182 | dev = hidraw_table[minor]; | ||
| 183 | if (!dev->open++) | ||
| 184 | dev->hid->hid_open(dev->hid); | ||
| 185 | |||
| 186 | out_unlock: | ||
| 187 | spin_unlock(&minors_lock); | ||
| 188 | out: | ||
| 189 | unlock_kernel(); | ||
| 190 | return err; | ||
| 191 | |||
| 192 | } | ||
| 193 | |||
| 194 | static int hidraw_release(struct inode * inode, struct file * file) | ||
| 195 | { | ||
| 196 | unsigned int minor = iminor(inode); | ||
| 197 | struct hidraw *dev; | ||
| 198 | struct hidraw_list *list = file->private_data; | ||
| 199 | |||
| 200 | if (!hidraw_table[minor]) { | ||
| 201 | printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", | ||
| 202 | minor); | ||
| 203 | return -ENODEV; | ||
| 204 | } | ||
| 205 | |||
| 206 | list_del(&list->node); | ||
| 207 | dev = hidraw_table[minor]; | ||
| 208 | if (!dev->open--) { | ||
| 209 | if (list->hidraw->exist) | ||
| 210 | dev->hid->hid_close(dev->hid); | ||
| 211 | else | ||
| 212 | kfree(list->hidraw); | ||
| 213 | } | ||
| 214 | |||
| 215 | kfree(list); | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | static long hidraw_ioctl(struct file *file, unsigned int cmd, | ||
| 221 | unsigned long arg) | ||
| 222 | { | ||
| 223 | struct inode *inode = file->f_path.dentry->d_inode; | ||
| 224 | unsigned int minor = iminor(inode); | ||
| 225 | long ret = 0; | ||
| 226 | /* FIXME: What stops hidraw_table going NULL */ | ||
| 227 | struct hidraw *dev = hidraw_table[minor]; | ||
| 228 | void __user *user_arg = (void __user*) arg; | ||
| 229 | |||
| 230 | lock_kernel(); | ||
| 231 | switch (cmd) { | ||
| 232 | case HIDIOCGRDESCSIZE: | ||
| 233 | if (put_user(dev->hid->rsize, (int __user *)arg)) | ||
| 234 | ret = -EFAULT; | ||
| 235 | break; | ||
| 236 | |||
| 237 | case HIDIOCGRDESC: | ||
| 238 | { | ||
| 239 | __u32 len; | ||
| 240 | |||
| 241 | if (get_user(len, (int __user *)arg)) | ||
| 242 | ret = -EFAULT; | ||
| 243 | else if (len > HID_MAX_DESCRIPTOR_SIZE - 1) | ||
| 244 | ret = -EINVAL; | ||
| 245 | else if (copy_to_user(user_arg + offsetof( | ||
| 246 | struct hidraw_report_descriptor, | ||
| 247 | value[0]), | ||
| 248 | dev->hid->rdesc, | ||
| 249 | min(dev->hid->rsize, len))) | ||
| 250 | ret = -EFAULT; | ||
| 251 | break; | ||
| 252 | } | ||
| 253 | case HIDIOCGRAWINFO: | ||
| 254 | { | ||
| 255 | struct hidraw_devinfo dinfo; | ||
| 256 | |||
| 257 | dinfo.bustype = dev->hid->bus; | ||
| 258 | dinfo.vendor = dev->hid->vendor; | ||
| 259 | dinfo.product = dev->hid->product; | ||
| 260 | if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) | ||
| 261 | ret = -EFAULT; | ||
| 262 | break; | ||
| 263 | } | ||
| 264 | default: | ||
| 265 | ret = -ENOTTY; | ||
| 266 | } | ||
| 267 | return ret; | ||
| 268 | } | ||
| 269 | |||
| 270 | static const struct file_operations hidraw_ops = { | ||
| 271 | .owner = THIS_MODULE, | ||
| 272 | .read = hidraw_read, | ||
| 273 | .write = hidraw_write, | ||
| 274 | .poll = hidraw_poll, | ||
| 275 | .open = hidraw_open, | ||
| 276 | .release = hidraw_release, | ||
| 277 | .unlocked_ioctl = hidraw_ioctl, | ||
| 278 | }; | ||
| 279 | |||
| 280 | void hidraw_report_event(struct hid_device *hid, u8 *data, int len) | ||
| 281 | { | ||
| 282 | struct hidraw *dev = hid->hidraw; | ||
| 283 | struct hidraw_list *list; | ||
| 284 | |||
| 285 | list_for_each_entry(list, &dev->list, node) { | ||
| 286 | list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC); | ||
| 287 | list->buffer[list->head].len = len; | ||
| 288 | list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); | ||
| 289 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | ||
| 290 | } | ||
| 291 | |||
| 292 | wake_up_interruptible(&dev->wait); | ||
| 293 | } | ||
| 294 | EXPORT_SYMBOL_GPL(hidraw_report_event); | ||
| 295 | |||
| 296 | int hidraw_connect(struct hid_device *hid) | ||
| 297 | { | ||
| 298 | int minor, result; | ||
| 299 | struct hidraw *dev; | ||
| 300 | |||
| 301 | /* TODO currently we accept any HID device. This should later | ||
| 302 | * probably be fixed to accept only those devices which provide | ||
| 303 | * non-input applications | ||
| 304 | */ | ||
| 305 | |||
| 306 | dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL); | ||
| 307 | if (!dev) | ||
| 308 | return -ENOMEM; | ||
| 309 | |||
| 310 | result = -EINVAL; | ||
| 311 | |||
| 312 | spin_lock(&minors_lock); | ||
| 313 | |||
| 314 | for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) { | ||
| 315 | if (hidraw_table[minor]) | ||
| 316 | continue; | ||
| 317 | hidraw_table[minor] = dev; | ||
| 318 | result = 0; | ||
| 319 | break; | ||
| 320 | } | ||
| 321 | |||
| 322 | spin_unlock(&minors_lock); | ||
| 323 | |||
| 324 | if (result) { | ||
| 325 | kfree(dev); | ||
| 326 | goto out; | ||
| 327 | } | ||
| 328 | |||
| 329 | dev->dev = device_create_drvdata(hidraw_class, NULL, | ||
| 330 | MKDEV(hidraw_major, minor), NULL, | ||
| 331 | "%s%d", "hidraw", minor); | ||
| 332 | |||
| 333 | if (IS_ERR(dev->dev)) { | ||
| 334 | spin_lock(&minors_lock); | ||
| 335 | hidraw_table[minor] = NULL; | ||
| 336 | spin_unlock(&minors_lock); | ||
| 337 | result = PTR_ERR(dev->dev); | ||
| 338 | kfree(dev); | ||
| 339 | goto out; | ||
| 340 | } | ||
| 341 | |||
| 342 | init_waitqueue_head(&dev->wait); | ||
| 343 | INIT_LIST_HEAD(&dev->list); | ||
| 344 | |||
| 345 | dev->hid = hid; | ||
| 346 | dev->minor = minor; | ||
| 347 | |||
| 348 | dev->exist = 1; | ||
| 349 | hid->hidraw = dev; | ||
| 350 | |||
| 351 | out: | ||
| 352 | return result; | ||
| 353 | |||
| 354 | } | ||
| 355 | EXPORT_SYMBOL_GPL(hidraw_connect); | ||
| 356 | |||
| 357 | void hidraw_disconnect(struct hid_device *hid) | ||
| 358 | { | ||
| 359 | struct hidraw *hidraw = hid->hidraw; | ||
| 360 | |||
| 361 | hidraw->exist = 0; | ||
| 362 | |||
| 363 | spin_lock(&minors_lock); | ||
| 364 | hidraw_table[hidraw->minor] = NULL; | ||
| 365 | spin_unlock(&minors_lock); | ||
| 366 | |||
| 367 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
| 368 | |||
| 369 | if (hidraw->open) { | ||
| 370 | hid->hid_close(hid); | ||
| 371 | wake_up_interruptible(&hidraw->wait); | ||
| 372 | } else { | ||
| 373 | kfree(hidraw); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | ||
| 377 | |||
| 378 | int __init hidraw_init(void) | ||
| 379 | { | ||
| 380 | int result; | ||
| 381 | dev_t dev_id; | ||
| 382 | |||
| 383 | result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR, | ||
| 384 | HIDRAW_MAX_DEVICES, "hidraw"); | ||
| 385 | |||
| 386 | hidraw_major = MAJOR(dev_id); | ||
| 387 | |||
| 388 | if (result < 0) { | ||
| 389 | printk(KERN_WARNING "hidraw: can't get major number\n"); | ||
| 390 | result = 0; | ||
| 391 | goto out; | ||
| 392 | } | ||
| 393 | |||
| 394 | hidraw_class = class_create(THIS_MODULE, "hidraw"); | ||
| 395 | if (IS_ERR(hidraw_class)) { | ||
| 396 | result = PTR_ERR(hidraw_class); | ||
| 397 | unregister_chrdev(hidraw_major, "hidraw"); | ||
| 398 | goto out; | ||
| 399 | } | ||
| 400 | |||
| 401 | cdev_init(&hidraw_cdev, &hidraw_ops); | ||
| 402 | cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); | ||
| 403 | out: | ||
| 404 | return result; | ||
| 405 | } | ||
| 406 | |||
| 407 | void __exit hidraw_exit(void) | ||
| 408 | { | ||
| 409 | dev_t dev_id = MKDEV(hidraw_major, 0); | ||
| 410 | |||
| 411 | cdev_del(&hidraw_cdev); | ||
| 412 | class_destroy(hidraw_class); | ||
| 413 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | ||
| 414 | |||
| 415 | } | ||
diff --git a/usr/src/dkms_source_tree/patches/10-expose-and-dashboard.patch b/usr/src/dkms_source_tree/patches/10-expose-and-dashboard.patch new file mode 100644 index 0000000..de64ac1 --- /dev/null +++ b/usr/src/dkms_source_tree/patches/10-expose-and-dashboard.patch | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c | ||
| 2 | --- a/drivers/hid/hid-input.c | ||
| 3 | +++ b/drivers/hid/hid-input.c | ||
| 4 | @@ -98,10 +98,11 @@ static struct hidinput_key_translation apple_fn_keys[] = { | ||
| 5 | |||
| 6 | static struct hidinput_key_translation apple_fn_keys[] = { | ||
| 7 | { KEY_BACKSPACE, KEY_DELETE }, | ||
| 8 | + { KEY_ENTER, KEY_INSERT }, | ||
| 9 | { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, | ||
| 10 | { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, | ||
| 11 | - { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ | ||
| 12 | - { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ | ||
| 13 | + { KEY_F3, KEY_CYCLEWINDOWS, APPLE_FLAG_FKEY }, /* Exposé */ | ||
| 14 | + { KEY_F4, KEY_CALC, APPLE_FLAG_FKEY }, /* Dashboard */ | ||
| 15 | { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, | ||
| 16 | { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, | ||
| 17 | { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, | ||
