(2006-08-06) rescue-bootcd

This commit is contained in:
2006-08-06 00:00:00 +02:00
parent 2f796b816a
commit decb062d20
21091 changed files with 7076462 additions and 0 deletions

View File

@@ -0,0 +1 @@
cmd_drivers/input/built-in.o := ld -m elf_i386 -r -o drivers/input/built-in.o drivers/input/input.o drivers/input/mousedev.o drivers/input/keyboard/built-in.o drivers/input/mouse/built-in.o

View File

@@ -0,0 +1,279 @@
cmd_drivers/input/input.o := gcc -Wp,-MD,drivers/input/.input.o.d -nostdinc -iwithprefix include -D__KERNEL__ -Iinclude -Wall -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -fno-unit-at-a-time -march=pentium-mmx -Iinclude/asm-i386/mach-default -Wdeclaration-after-statement -DKBUILD_BASENAME=input -DKBUILD_MODNAME=input -c -o drivers/input/input.o drivers/input/input.c
deps_drivers/input/input.o := \
drivers/input/input.c \
$(wildcard include/config/proc/fs.h) \
$(wildcard include/config/hotplug.h) \
include/linux/init.h \
$(wildcard include/config/modules.h) \
include/linux/config.h \
$(wildcard include/config/h.h) \
include/linux/compiler.h \
include/linux/compiler-gcc3.h \
include/linux/compiler-gcc.h \
include/linux/sched.h \
$(wildcard include/config/keys.h) \
$(wildcard include/config/schedstats.h) \
$(wildcard include/config/smp.h) \
$(wildcard include/config/numa.h) \
$(wildcard include/config/security.h) \
$(wildcard include/config/preempt.h) \
$(wildcard include/config/magic/sysrq.h) \
include/asm/param.h \
include/linux/capability.h \
include/linux/types.h \
$(wildcard include/config/uid16.h) \
include/linux/posix_types.h \
include/linux/stddef.h \
include/asm/posix_types.h \
include/asm/types.h \
$(wildcard include/config/highmem64g.h) \
$(wildcard include/config/lbd.h) \
include/linux/spinlock.h \
$(wildcard include/config/debug/spinlock.h) \
$(wildcard include/config/lockmeter.h) \
include/linux/preempt.h \
include/linux/linkage.h \
include/asm/linkage.h \
$(wildcard include/config/regparm.h) \
$(wildcard include/config/x86/alignment/16.h) \
include/linux/thread_info.h \
include/linux/bitops.h \
include/asm/bitops.h \
include/asm/thread_info.h \
$(wildcard include/config/4kstacks.h) \
$(wildcard include/config/debug/stack/usage.h) \
include/asm/page.h \
$(wildcard include/config/x86/use/3dnow.h) \
$(wildcard include/config/x86/pae.h) \
$(wildcard include/config/hugetlb/page.h) \
$(wildcard include/config/highmem4g.h) \
$(wildcard include/config/discontigmem.h) \
include/asm/processor.h \
$(wildcard include/config/mk8.h) \
$(wildcard include/config/mk7.h) \
include/asm/vm86.h \
include/asm/math_emu.h \
include/asm/sigcontext.h \
include/asm/segment.h \
include/asm/cpufeature.h \
include/asm/msr.h \
include/asm/system.h \
$(wildcard include/config/x86/cmpxchg.h) \
$(wildcard include/config/x86/oostore.h) \
include/linux/kernel.h \
$(wildcard include/config/debug/spinlock/sleep.h) \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.1/include/stdarg.h \
include/asm/byteorder.h \
$(wildcard include/config/x86/bswap.h) \
include/linux/byteorder/little_endian.h \
include/linux/byteorder/swab.h \
include/linux/byteorder/generic.h \
include/asm/bug.h \
include/asm-generic/bug.h \
include/linux/cache.h \
include/asm/cache.h \
$(wildcard include/config/x86/l1/cache/shift.h) \
include/linux/threads.h \
$(wildcard include/config/nr/cpus.h) \
include/asm/percpu.h \
include/asm-generic/percpu.h \
include/linux/stringify.h \
include/linux/timex.h \
$(wildcard include/config/time/interpolation.h) \
include/linux/time.h \
include/linux/seqlock.h \
include/asm/timex.h \
$(wildcard include/config/x86/elan.h) \
$(wildcard include/config/x86/tsc.h) \
$(wildcard include/config/x86/generic.h) \
include/linux/jiffies.h \
include/asm/div64.h \
include/linux/rbtree.h \
include/linux/cpumask.h \
$(wildcard include/config/hotplug/cpu.h) \
include/linux/bitmap.h \
include/linux/string.h \
include/asm/string.h \
include/asm/semaphore.h \
include/asm/atomic.h \
$(wildcard include/config/m386.h) \
include/linux/wait.h \
include/linux/list.h \
include/linux/prefetch.h \
include/asm/current.h \
include/linux/rwsem.h \
$(wildcard include/config/rwsem/generic/spinlock.h) \
include/asm/rwsem.h \
include/asm/ptrace.h \
$(wildcard include/config/frame/pointer.h) \
include/asm/mmu.h \
include/linux/smp.h \
include/linux/sem.h \
$(wildcard include/config/sysvipc.h) \
include/linux/ipc.h \
include/asm/ipcbuf.h \
include/asm/sembuf.h \
include/linux/signal.h \
include/asm/signal.h \
include/asm/siginfo.h \
include/asm-generic/siginfo.h \
include/linux/resource.h \
include/asm/resource.h \
include/linux/securebits.h \
include/linux/fs_struct.h \
include/linux/completion.h \
include/linux/pid.h \
include/linux/percpu.h \
include/linux/slab.h \
$(wildcard include/config/.h) \
include/linux/gfp.h \
include/linux/mmzone.h \
$(wildcard include/config/force/max/zoneorder.h) \
include/linux/numa.h \
include/linux/topology.h \
$(wildcard include/config/sched/smt.h) \
include/asm/topology.h \
include/asm-generic/topology.h \
include/linux/kmalloc_sizes.h \
$(wildcard include/config/mmu.h) \
$(wildcard include/config/large/allocs.h) \
include/linux/param.h \
include/linux/timer.h \
include/linux/aio.h \
include/linux/workqueue.h \
include/linux/aio_abi.h \
include/linux/smp_lock.h \
$(wildcard include/config/lock/kernel.h) \
include/linux/input.h \
include/linux/fs.h \
$(wildcard include/config/dnotify.h) \
$(wildcard include/config/quota.h) \
$(wildcard include/config/epoll.h) \
$(wildcard include/config/auditsyscall.h) \
include/linux/limits.h \
include/linux/kdev_t.h \
include/linux/ioctl.h \
include/asm/ioctl.h \
include/linux/dcache.h \
include/linux/rcupdate.h \
include/linux/stat.h \
include/asm/stat.h \
include/linux/prio_tree.h \
include/linux/kobject.h \
include/linux/sysfs.h \
$(wildcard include/config/sysfs.h) \
include/linux/kref.h \
include/linux/kobject_uevent.h \
$(wildcard include/config/kobject/uevent.h) \
include/linux/radix-tree.h \
include/linux/audit.h \
$(wildcard include/config/audit.h) \
include/linux/quota.h \
include/linux/errno.h \
include/asm/errno.h \
include/asm-generic/errno.h \
include/asm-generic/errno-base.h \
include/linux/dqblk_xfs.h \
include/linux/dqblk_v1.h \
include/linux/dqblk_v2.h \
include/linux/nfs_fs_i.h \
include/linux/nfs.h \
include/linux/sunrpc/msg_prot.h \
include/linux/fcntl.h \
include/asm/fcntl.h \
include/linux/err.h \
include/linux/module.h \
$(wildcard include/config/modversions.h) \
$(wildcard include/config/module/unload.h) \
$(wildcard include/config/kallsyms.h) \
include/linux/kmod.h \
$(wildcard include/config/kmod.h) \
include/linux/elf.h \
include/asm/elf.h \
include/asm/user.h \
include/linux/utsname.h \
include/linux/moduleparam.h \
include/asm/local.h \
include/asm/module.h \
$(wildcard include/config/m486.h) \
$(wildcard include/config/m586.h) \
$(wildcard include/config/m586tsc.h) \
$(wildcard include/config/m586mmx.h) \
$(wildcard include/config/m686.h) \
$(wildcard include/config/mpentiumii.h) \
$(wildcard include/config/mpentiumiii.h) \
$(wildcard include/config/mpentiumm.h) \
$(wildcard include/config/mpentium4.h) \
$(wildcard include/config/mk6.h) \
$(wildcard include/config/mcrusoe.h) \
$(wildcard include/config/mefficeon.h) \
$(wildcard include/config/mwinchipc6.h) \
$(wildcard include/config/mwinchip2.h) \
$(wildcard include/config/mwinchip3d.h) \
$(wildcard include/config/mcyrixiii.h) \
$(wildcard include/config/mviac3/2.h) \
include/linux/random.h \
include/linux/major.h \
include/linux/pm.h \
$(wildcard include/config/pm.h) \
include/linux/proc_fs.h \
$(wildcard include/config/proc/devicetree.h) \
$(wildcard include/config/proc/kcore.h) \
include/linux/interrupt.h \
$(wildcard include/config/generic/hardirqs.h) \
$(wildcard include/config/generic/irq/probe.h) \
include/linux/hardirq.h \
include/asm/hardirq.h \
include/linux/irq.h \
$(wildcard include/config/arch/s390.h) \
include/asm/irq.h \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/irqbalance.h) \
include/asm-i386/mach-default/irq_vectors.h \
include/asm-i386/mach-default/irq_vectors_limits.h \
$(wildcard include/config/pci/msi.h) \
$(wildcard include/config/x86/io/apic.h) \
include/asm/hw_irq.h \
include/linux/profile.h \
$(wildcard include/config/profiling.h) \
include/asm/sections.h \
include/asm-generic/sections.h \
include/linux/irq_cpustat.h \
include/linux/poll.h \
include/asm/poll.h \
include/linux/mm.h \
$(wildcard include/config/sysctl.h) \
$(wildcard include/config/stack/growsup.h) \
$(wildcard include/config/highmem.h) \
$(wildcard include/config/shmem.h) \
$(wildcard include/config/debug/pagealloc.h) \
$(wildcard include/config/arch/gate/area.h) \
include/asm/pgtable.h \
$(wildcard include/config/highpte.h) \
include/asm/fixmap.h \
$(wildcard include/config/x86/visws/apic.h) \
$(wildcard include/config/x86/f00f/bug.h) \
$(wildcard include/config/x86/cyclone/timer.h) \
$(wildcard include/config/acpi/boot.h) \
$(wildcard include/config/pci/mmconfig.h) \
include/asm/acpi.h \
$(wildcard include/config/acpi/pci.h) \
$(wildcard include/config/acpi/sleep.h) \
include/asm/apicdef.h \
include/asm/pgtable-2level-defs.h \
include/asm/pgtable-2level.h \
include/asm-generic/pgtable.h \
include/linux/page-flags.h \
$(wildcard include/config/swap.h) \
include/asm/uaccess.h \
$(wildcard include/config/x86/intel/usercopy.h) \
$(wildcard include/config/x86/wp/works/ok.h) \
include/linux/device.h \
include/linux/ioport.h \
include/linux/devfs_fs_kernel.h \
$(wildcard include/config/devfs/fs.h) \
drivers/input/input.o: $(deps_drivers/input/input.o)
$(deps_drivers/input/input.o):

View File

@@ -0,0 +1,261 @@
cmd_drivers/input/mousedev.o := gcc -Wp,-MD,drivers/input/.mousedev.o.d -nostdinc -iwithprefix include -D__KERNEL__ -Iinclude -Wall -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -fno-unit-at-a-time -march=pentium-mmx -Iinclude/asm-i386/mach-default -Wdeclaration-after-statement -DKBUILD_BASENAME=mousedev -DKBUILD_MODNAME=mousedev -c -o drivers/input/mousedev.o drivers/input/mousedev.c
deps_drivers/input/mousedev.o := \
drivers/input/mousedev.c \
$(wildcard include/config/input/mousedev/psaux.h) \
$(wildcard include/config/input/mousedev/screen/x.h) \
$(wildcard include/config/input/mousedev/screen/y.h) \
include/linux/slab.h \
$(wildcard include/config/.h) \
$(wildcard include/config/numa.h) \
include/linux/config.h \
$(wildcard include/config/h.h) \
include/linux/gfp.h \
include/linux/mmzone.h \
$(wildcard include/config/force/max/zoneorder.h) \
$(wildcard include/config/smp.h) \
$(wildcard include/config/discontigmem.h) \
include/linux/spinlock.h \
$(wildcard include/config/preempt.h) \
$(wildcard include/config/debug/spinlock.h) \
$(wildcard include/config/lockmeter.h) \
include/linux/preempt.h \
include/linux/linkage.h \
include/asm/linkage.h \
$(wildcard include/config/regparm.h) \
$(wildcard include/config/x86/alignment/16.h) \
include/linux/compiler.h \
include/linux/compiler-gcc3.h \
include/linux/compiler-gcc.h \
include/linux/thread_info.h \
include/linux/bitops.h \
include/asm/types.h \
$(wildcard include/config/highmem64g.h) \
$(wildcard include/config/lbd.h) \
include/asm/bitops.h \
include/asm/thread_info.h \
$(wildcard include/config/4kstacks.h) \
$(wildcard include/config/debug/stack/usage.h) \
include/asm/page.h \
$(wildcard include/config/x86/use/3dnow.h) \
$(wildcard include/config/x86/pae.h) \
$(wildcard include/config/hugetlb/page.h) \
$(wildcard include/config/highmem4g.h) \
include/asm/processor.h \
$(wildcard include/config/mk8.h) \
$(wildcard include/config/mk7.h) \
include/asm/vm86.h \
include/asm/math_emu.h \
include/asm/sigcontext.h \
include/asm/segment.h \
include/asm/cpufeature.h \
include/asm/msr.h \
include/asm/system.h \
$(wildcard include/config/x86/cmpxchg.h) \
$(wildcard include/config/x86/oostore.h) \
include/linux/kernel.h \
$(wildcard include/config/debug/spinlock/sleep.h) \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.1/include/stdarg.h \
include/linux/stddef.h \
include/linux/types.h \
$(wildcard include/config/uid16.h) \
include/linux/posix_types.h \
include/asm/posix_types.h \
include/asm/byteorder.h \
$(wildcard include/config/x86/bswap.h) \
include/linux/byteorder/little_endian.h \
include/linux/byteorder/swab.h \
include/linux/byteorder/generic.h \
include/asm/bug.h \
include/asm-generic/bug.h \
include/linux/cache.h \
include/asm/cache.h \
$(wildcard include/config/x86/l1/cache/shift.h) \
include/linux/threads.h \
$(wildcard include/config/nr/cpus.h) \
include/asm/percpu.h \
include/asm-generic/percpu.h \
include/linux/stringify.h \
include/linux/list.h \
include/linux/prefetch.h \
include/linux/wait.h \
include/asm/current.h \
include/linux/numa.h \
include/asm/atomic.h \
$(wildcard include/config/m386.h) \
include/linux/topology.h \
$(wildcard include/config/sched/smt.h) \
include/linux/cpumask.h \
$(wildcard include/config/hotplug/cpu.h) \
include/linux/bitmap.h \
include/linux/string.h \
include/asm/string.h \
include/linux/smp.h \
include/asm/topology.h \
include/asm-generic/topology.h \
include/linux/init.h \
$(wildcard include/config/modules.h) \
$(wildcard include/config/hotplug.h) \
include/linux/kmalloc_sizes.h \
$(wildcard include/config/mmu.h) \
$(wildcard include/config/large/allocs.h) \
include/linux/poll.h \
include/asm/poll.h \
include/linux/mm.h \
$(wildcard include/config/sysctl.h) \
$(wildcard include/config/stack/growsup.h) \
$(wildcard include/config/highmem.h) \
$(wildcard include/config/shmem.h) \
$(wildcard include/config/proc/fs.h) \
$(wildcard include/config/debug/pagealloc.h) \
$(wildcard include/config/arch/gate/area.h) \
include/linux/sched.h \
$(wildcard include/config/keys.h) \
$(wildcard include/config/schedstats.h) \
$(wildcard include/config/security.h) \
$(wildcard include/config/magic/sysrq.h) \
include/asm/param.h \
include/linux/capability.h \
include/linux/timex.h \
$(wildcard include/config/time/interpolation.h) \
include/linux/time.h \
include/linux/seqlock.h \
include/asm/timex.h \
$(wildcard include/config/x86/elan.h) \
$(wildcard include/config/x86/tsc.h) \
$(wildcard include/config/x86/generic.h) \
include/linux/jiffies.h \
include/asm/div64.h \
include/linux/rbtree.h \
include/asm/semaphore.h \
include/linux/rwsem.h \
$(wildcard include/config/rwsem/generic/spinlock.h) \
include/asm/rwsem.h \
include/asm/ptrace.h \
$(wildcard include/config/frame/pointer.h) \
include/asm/mmu.h \
include/linux/sem.h \
$(wildcard include/config/sysvipc.h) \
include/linux/ipc.h \
include/asm/ipcbuf.h \
include/asm/sembuf.h \
include/linux/signal.h \
include/asm/signal.h \
include/asm/siginfo.h \
include/asm-generic/siginfo.h \
include/linux/resource.h \
include/asm/resource.h \
include/linux/securebits.h \
include/linux/fs_struct.h \
include/linux/completion.h \
include/linux/pid.h \
include/linux/percpu.h \
include/linux/param.h \
include/linux/timer.h \
include/linux/aio.h \
include/linux/workqueue.h \
include/linux/aio_abi.h \
include/linux/errno.h \
include/asm/errno.h \
include/asm-generic/errno.h \
include/asm-generic/errno-base.h \
include/linux/prio_tree.h \
include/linux/fs.h \
$(wildcard include/config/dnotify.h) \
$(wildcard include/config/quota.h) \
$(wildcard include/config/epoll.h) \
$(wildcard include/config/auditsyscall.h) \
include/linux/limits.h \
include/linux/kdev_t.h \
include/linux/ioctl.h \
include/asm/ioctl.h \
include/linux/dcache.h \
include/linux/rcupdate.h \
include/linux/stat.h \
include/asm/stat.h \
include/linux/kobject.h \
include/linux/sysfs.h \
$(wildcard include/config/sysfs.h) \
include/linux/kref.h \
include/linux/kobject_uevent.h \
$(wildcard include/config/kobject/uevent.h) \
include/linux/radix-tree.h \
include/linux/audit.h \
$(wildcard include/config/audit.h) \
include/linux/quota.h \
include/linux/dqblk_xfs.h \
include/linux/dqblk_v1.h \
include/linux/dqblk_v2.h \
include/linux/nfs_fs_i.h \
include/linux/nfs.h \
include/linux/sunrpc/msg_prot.h \
include/linux/fcntl.h \
include/asm/fcntl.h \
include/linux/err.h \
include/asm/pgtable.h \
$(wildcard include/config/highpte.h) \
include/asm/fixmap.h \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/x86/io/apic.h) \
$(wildcard include/config/x86/visws/apic.h) \
$(wildcard include/config/x86/f00f/bug.h) \
$(wildcard include/config/x86/cyclone/timer.h) \
$(wildcard include/config/acpi/boot.h) \
$(wildcard include/config/pci/mmconfig.h) \
include/asm/acpi.h \
$(wildcard include/config/acpi/pci.h) \
$(wildcard include/config/acpi/sleep.h) \
include/asm/apicdef.h \
include/asm/pgtable-2level-defs.h \
include/asm/pgtable-2level.h \
include/asm-generic/pgtable.h \
include/linux/page-flags.h \
$(wildcard include/config/swap.h) \
include/asm/uaccess.h \
$(wildcard include/config/x86/intel/usercopy.h) \
$(wildcard include/config/x86/wp/works/ok.h) \
include/linux/module.h \
$(wildcard include/config/modversions.h) \
$(wildcard include/config/module/unload.h) \
$(wildcard include/config/kallsyms.h) \
include/linux/kmod.h \
$(wildcard include/config/kmod.h) \
include/linux/elf.h \
include/asm/elf.h \
include/asm/user.h \
include/linux/utsname.h \
include/linux/moduleparam.h \
include/asm/local.h \
include/asm/module.h \
$(wildcard include/config/m486.h) \
$(wildcard include/config/m586.h) \
$(wildcard include/config/m586tsc.h) \
$(wildcard include/config/m586mmx.h) \
$(wildcard include/config/m686.h) \
$(wildcard include/config/mpentiumii.h) \
$(wildcard include/config/mpentiumiii.h) \
$(wildcard include/config/mpentiumm.h) \
$(wildcard include/config/mpentium4.h) \
$(wildcard include/config/mk6.h) \
$(wildcard include/config/mcrusoe.h) \
$(wildcard include/config/mefficeon.h) \
$(wildcard include/config/mwinchipc6.h) \
$(wildcard include/config/mwinchip2.h) \
$(wildcard include/config/mwinchip3d.h) \
$(wildcard include/config/mcyrixiii.h) \
$(wildcard include/config/mviac3/2.h) \
include/linux/input.h \
include/linux/smp_lock.h \
$(wildcard include/config/lock/kernel.h) \
include/linux/random.h \
include/linux/major.h \
include/linux/device.h \
include/linux/ioport.h \
include/linux/pm.h \
$(wildcard include/config/pm.h) \
include/linux/devfs_fs_kernel.h \
$(wildcard include/config/devfs/fs.h) \
drivers/input/mousedev.o: $(deps_drivers/input/mousedev.o)
$(deps_drivers/input/mousedev.o):

View File

@@ -0,0 +1,156 @@
#
# Input device configuration
#
menu "Input device support"
config INPUT
tristate "Input devices (needed for keyboard, mouse, ...)" if EMBEDDED
default y
---help---
Say Y here if you have any input device (mouse, keyboard, tablet,
joystick, steering wheel ...) connected to your system and want
it to be available to applications. This includes standard PS/2
keyboard and mouse.
Say N here if you have a headless (no monitor, no keyboard) system.
More information is available: <file:Documentation/input/input.txt>
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called input.
comment "Userland interfaces"
config INPUT_MOUSEDEV
tristate "Mouse interface" if EMBEDDED
default y
depends on INPUT
---help---
Say Y here if you want your mouse to be accessible as char devices
13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
programs (includung SVGAlib, GPM and X) will be able to use your
mouse.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called mousedev.
config INPUT_MOUSEDEV_PSAUX
bool "Provide legacy /dev/psaux device"
default y
depends on INPUT_MOUSEDEV
---help---
Say Y here if you want your mouse also be accessible as char device
10:1 - /dev/psaux. The data available through /dev/psaux is exactly
the same as the data from /dev/input/mice.
If unsure, say Y.
config INPUT_MOUSEDEV_SCREEN_X
int "Horizontal screen resolution"
depends on INPUT_MOUSEDEV
default "1024"
help
If you're using a digitizer, or a graphic tablet, and want to use
it as a mouse then the mousedev driver needs to know the X window
screen resolution you are using to correctly scale the data. If
you're not using a digitizer, this value is ignored.
config INPUT_MOUSEDEV_SCREEN_Y
int "Vertical screen resolution"
depends on INPUT_MOUSEDEV
default "768"
help
If you're using a digitizer, or a graphic tablet, and want to use
it as a mouse then the mousedev driver needs to know the X window
screen resolution you are using to correctly scale the data. If
you're not using a digitizer, this value is ignored.
config INPUT_JOYDEV
tristate "Joystick interface"
depends on INPUT
---help---
Say Y here if you want your joystick or gamepad to be
accessible as char device 13:0+ - /dev/input/jsX device.
If unsure, say Y.
More information is available: <file:Documentation/input/joystick.txt>
To compile this driver as a module, choose M here: the
module will be called joydev.
config INPUT_TSDEV
tristate "Touchscreen interface"
depends on INPUT
---help---
Say Y here if you have an application that only can understand the
Compaq touchscreen protocol for absolute pointer data. This is
useful namely for embedded configurations.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called tsdev.
config INPUT_TSDEV_SCREEN_X
int "Horizontal screen resolution"
depends on INPUT_TSDEV
default "240"
config INPUT_TSDEV_SCREEN_Y
int "Vertical screen resolution"
depends on INPUT_TSDEV
default "320"
config INPUT_EVDEV
tristate "Event interface"
depends on INPUT
help
Say Y here if you want your input device events be accessible
under char device 13:64+ - /dev/input/eventX in a generic way.
To compile this driver as a module, choose M here: the
module will be called evdev.
config INPUT_EVBUG
tristate "Event debugging"
depends on INPUT
---help---
Say Y here if you have a problem with the input subsystem and
want all events (keypresses, mouse movements), to be output to
the system log. While this is useful for debugging, it's also
a security threat - your keypresses include your passwords, of
course.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called evbug.
comment "Input I/O drivers"
source "drivers/input/gameport/Kconfig"
source "drivers/input/serio/Kconfig"
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"
source "drivers/input/mouse/Kconfig"
source "drivers/input/joystick/Kconfig"
source "drivers/input/touchscreen/Kconfig"
source "drivers/input/misc/Kconfig"
endmenu

View File

@@ -0,0 +1,19 @@
#
# Makefile for the input core drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT) += input.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_INPUT_TSDEV) += tsdev.o
obj-$(CONFIG_INPUT_POWER) += power.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
obj-$(CONFIG_INPUT_MOUSE) += mouse/
obj-$(CONFIG_INPUT_JOYSTICK) += joystick/
obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/

Binary file not shown.

View File

@@ -0,0 +1,103 @@
/*
* $Id: evbug.c,v 1.10 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Input driver event debug module - dumps all events into syslog
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/device.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Input driver event debug module");
MODULE_LICENSE("GPL");
static char evbug_name[] = "evbug";
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value);
}
static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
struct input_handle *handle;
if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
return NULL;
memset(handle, 0, sizeof(struct input_handle));
handle->dev = dev;
handle->handler = handler;
handle->name = evbug_name;
input_open_device(handle);
printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys);
return handle;
}
static void evbug_disconnect(struct input_handle *handle)
{
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys);
input_close_device(handle);
kfree(handle);
}
static struct input_device_id evbug_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evbug_ids);
static struct input_handler evbug_handler = {
.event = evbug_event,
.connect = evbug_connect,
.disconnect = evbug_disconnect,
.name = "evbug",
.id_table = evbug_ids,
};
int __init evbug_init(void)
{
input_register_handler(&evbug_handler);
return 0;
}
void __exit evbug_exit(void)
{
input_unregister_handler(&evbug_handler);
}
module_init(evbug_init);
module_exit(evbug_exit);

View File

@@ -0,0 +1,486 @@
/*
* Event char devices, giving access to raw input device events.
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
#define EVDEV_BUFFER_SIZE 64
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/major.h>
#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/devfs_fs_kernel.h>
struct evdev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_list *grab;
struct list_head list;
};
struct evdev_list {
struct input_event buffer[EVDEV_BUFFER_SIZE];
int head;
int tail;
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
};
static struct evdev *evdev_table[EVDEV_MINORS];
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_list *list;
if (evdev->grab) {
list = evdev->grab;
do_gettimeofday(&list->buffer[list->head].time);
list->buffer[list->head].type = type;
list->buffer[list->head].code = code;
list->buffer[list->head].value = value;
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
} else
list_for_each_entry(list, &evdev->list, node) {
do_gettimeofday(&list->buffer[list->head].time);
list->buffer[list->head].type = type;
list->buffer[list->head].code = code;
list->buffer[list->head].value = value;
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&evdev->wait);
}
static int evdev_fasync(int fd, struct file *file, int on)
{
int retval;
struct evdev_list *list = file->private_data;
retval = fasync_helper(fd, file, on, &list->fasync);
return retval < 0 ? retval : 0;
}
static int evdev_flush(struct file * file)
{
struct evdev_list *list = file->private_data;
if (!list->evdev->exist) return -ENODEV;
return input_flush_device(&list->evdev->handle, file);
}
static void evdev_free(struct evdev *evdev)
{
evdev_table[evdev->minor] = NULL;
kfree(evdev);
}
static int evdev_release(struct inode * inode, struct file * file)
{
struct evdev_list *list = file->private_data;
if (list->evdev->grab == list) {
input_release_device(&list->evdev->handle);
list->evdev->grab = NULL;
}
evdev_fasync(-1, file, 0);
list_del(&list->node);
if (!--list->evdev->open) {
if (list->evdev->exist)
input_close_device(&list->evdev->handle);
else
evdev_free(list->evdev);
}
kfree(list);
return 0;
}
static int evdev_open(struct inode * inode, struct file * file)
{
struct evdev_list *list;
int i = iminor(inode) - EVDEV_MINOR_BASE;
int accept_err;
if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist)
return -ENODEV;
if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
return accept_err;
if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
return -ENOMEM;
memset(list, 0, sizeof(struct evdev_list));
list->evdev = evdev_table[i];
list_add_tail(&list->node, &evdev_table[i]->list);
file->private_data = list;
if (!list->evdev->open++)
if (list->evdev->exist)
input_open_device(&list->evdev->handle);
return 0;
}
static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
{
struct evdev_list *list = file->private_data;
struct input_event event;
int retval = 0;
if (!list->evdev->exist) return -ENODEV;
while (retval < count) {
if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
return -EFAULT;
input_event(list->evdev->handle.dev, event.type, event.code, event.value);
retval += sizeof(struct input_event);
}
return retval;
}
static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
{
struct evdev_list *list = file->private_data;
int retval;
if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(list->evdev->wait,
list->head != list->tail || (!list->evdev->exist));
if (retval)
return retval;
if (!list->evdev->exist)
return -ENODEV;
while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
if (copy_to_user(buffer + retval, list->buffer + list->tail,
sizeof(struct input_event))) return -EFAULT;
list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
retval += sizeof(struct input_event);
}
return retval;
}
/* No kernel lock - fine */
static unsigned int evdev_poll(struct file *file, poll_table *wait)
{
struct evdev_list *list = file->private_data;
poll_wait(file, &list->evdev->wait, wait);
if (list->head != list->tail)
return POLLIN | POLLRDNORM;
return 0;
}
static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct evdev_list *list = file->private_data;
struct evdev *evdev = list->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
void __user *p = (void __user *)arg;
int __user *ip = (int __user *)arg;
int i, t, u, v;
if (!evdev->exist) return -ENODEV;
switch (cmd) {
case EVIOCGVERSION:
return put_user(EV_VERSION, ip);
case EVIOCGID:
return copy_to_user(p, &dev->id, sizeof(struct input_id)) ? -EFAULT : 0;
case EVIOCGKEYCODE:
if (get_user(t, ip)) return -EFAULT;
if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL;
if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) return -EFAULT;
return 0;
case EVIOCSKEYCODE:
if (get_user(t, ip)) return -EFAULT;
if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL;
if (get_user(v, ip + 1)) return -EFAULT;
u = SET_INPUT_KEYCODE(dev, t, v);
clear_bit(u, dev->keybit);
set_bit(v, dev->keybit);
for (i = 0; i < dev->keycodemax; i++)
if (INPUT_KEYCODE(dev,i) == u)
set_bit(u, dev->keybit);
return 0;
case EVIOCSFF:
if (dev->upload_effect) {
struct ff_effect effect;
int err;
if (copy_from_user(&effect, p, sizeof(effect)))
return -EFAULT;
err = dev->upload_effect(dev, &effect);
if (put_user(effect.id, &(((struct ff_effect __user *)arg)->id)))
return -EFAULT;
return err;
}
else return -ENOSYS;
case EVIOCRMFF:
if (dev->erase_effect) {
return dev->erase_effect(dev, (int)arg);
}
else return -ENOSYS;
case EVIOCGEFFECTS:
if (put_user(dev->ff_effects_max, ip))
return -EFAULT;
return 0;
case EVIOCGRAB:
if (arg) {
if (evdev->grab)
return -EBUSY;
if (input_grab_device(&evdev->handle))
return -EBUSY;
evdev->grab = list;
return 0;
} else {
if (evdev->grab != list)
return -EINVAL;
input_release_device(&evdev->handle);
evdev->grab = NULL;
return 0;
}
default:
if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
return -EINVAL;
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
long *bits;
int len;
switch (_IOC_NR(cmd) & EV_MAX) {
case 0: bits = dev->evbit; len = EV_MAX; break;
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
case EV_REL: bits = dev->relbit; len = REL_MAX; break;
case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
case EV_FF: bits = dev->ffbit; len = FF_MAX; break;
default: return -EINVAL;
}
len = NBITS(len) * sizeof(long);
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, bits, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) {
int len;
len = NBITS(KEY_MAX) * sizeof(long);
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, dev->key, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) {
int len;
len = NBITS(LED_MAX) * sizeof(long);
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, dev->led, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) {
int len;
len = NBITS(SND_MAX) * sizeof(long);
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, dev->snd, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
int len;
if (!dev->name) return -ENOENT;
len = strlen(dev->name) + 1;
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, dev->name, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) {
int len;
if (!dev->phys) return -ENOENT;
len = strlen(dev->phys) + 1;
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, dev->phys, len) ? -EFAULT : len;
}
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) {
int len;
if (!dev->uniq) return -ENOENT;
len = strlen(dev->uniq) + 1;
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
return copy_to_user(p, dev->uniq, len) ? -EFAULT : len;
}
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
int t = _IOC_NR(cmd) & ABS_MAX;
abs.value = dev->abs[t];
abs.minimum = dev->absmin[t];
abs.maximum = dev->absmax[t];
abs.fuzz = dev->absfuzz[t];
abs.flat = dev->absflat[t];
if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
return -EFAULT;
return 0;
}
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
int t = _IOC_NR(cmd) & ABS_MAX;
if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
return -EFAULT;
dev->abs[t] = abs.value;
dev->absmin[t] = abs.minimum;
dev->absmax[t] = abs.maximum;
dev->absfuzz[t] = abs.fuzz;
dev->absflat[t] = abs.flat;
return 0;
}
}
return -EINVAL;
}
static struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.ioctl = evdev_ioctl,
.fasync = evdev_fasync,
.flush = evdev_flush
};
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
struct evdev *evdev;
int minor;
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return NULL;
}
if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
return NULL;
memset(evdev, 0, sizeof(struct evdev));
INIT_LIST_HEAD(&evdev->list);
init_waitqueue_head(&evdev->wait);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);
evdev_table[minor] = evdev;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
S_IFCHR|S_IRUGO|S_IWUSR, "input/event%d", minor);
class_simple_device_add(input_class,
MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
dev->dev, "event%d", minor);
return &evdev->handle;
}
static void evdev_disconnect(struct input_handle *handle)
{
struct evdev *evdev = handle->private;
class_simple_device_remove(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
devfs_remove("input/event%d", evdev->minor);
evdev->exist = 0;
if (evdev->open) {
input_close_device(handle);
wake_up_interruptible(&evdev->wait);
} else
evdev_free(evdev);
}
static struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
static int __init evdev_init(void)
{
input_register_handler(&evdev_handler);
return 0;
}
static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}
module_init(evdev_init);
module_exit(evdev_exit);
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Input driver event char devices");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,90 @@
#
# Gameport configuration
#
config GAMEPORT
tristate "Gameport support"
---help---
Gameport support is for the standard 15-pin PC gameport. If you
have a joystick, gamepad, gameport card, a soundcard with a gameport
or anything else that uses the gameport, say Y or M here and also to
at least one of the hardware specific drivers.
For Ensoniq AudioPCI (ES1370), AudioPCI 97 (ES1371), ESS Solo1,
S3 SonicVibes, Trident 4DWave, SiS7018, and ALi 5451 gameport
support is provided by the sound drivers, so you won't need any
from the below listed modules. You still need to say Y here.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called gameport.
# Yes, SOUND_GAMEPORT looks a bit odd. Yes, it ends up being turned on
# in every .config. Please don't touch it. It is here to handle an
# unusual dependency between GAMEPORT and sound drivers.
#
# Some sound drivers call gameport functions. If GAMEPORT is
# not selected, empty stubs are provided for the functions and all is
# well.
# If GAMEPORT is built in, everything is fine.
# If GAMEPORT is a module, however, it would need to be loaded for the
# sound driver to be able to link properly. Therefore, the sound
# driver must be a module as well in that case. Since there's no way
# to express that directly in Kconfig, we use SOUND_GAMEPORT to
# express it. SOUND_GAMEPORT boils down to "if GAMEPORT is 'm',
# anything that depends on SOUND_GAMEPORT must be 'm' as well. if
# GAMEPORT is 'y' or 'n', it can be anything".
config SOUND_GAMEPORT
tristate
default y if GAMEPORT!=m
default m if GAMEPORT=m
config GAMEPORT_NS558
tristate "Classic ISA and PnP gameport support"
depends on GAMEPORT
help
Say Y here if you have an ISA or PnP gameport.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called ns558.
config GAMEPORT_L4
tristate "PDPI Lightning 4 gamecard support"
depends on GAMEPORT
help
Say Y here if you have a PDPI Lightning 4 gamecard.
To compile this driver as a module, choose M here: the
module will be called lightning.
config GAMEPORT_EMU10K1
tristate "SB Live and Audigy gameport support"
depends on GAMEPORT
help
Say Y here if you have a SoundBlaster Live! or SoundBlaster
Audigy card and want to use its gameport.
To compile this driver as a module, choose M here: the
module will be called emu10k1-gp.
config GAMEPORT_VORTEX
tristate "Aureal Vortex, Vortex 2 gameport support"
depends on GAMEPORT
help
Say Y here if you have an Aureal Vortex 1 or 2 card and want
to use its gameport.
To compile this driver as a module, choose M here: the
module will be called vortex.
config GAMEPORT_FM801
tristate "ForteMedia FM801 gameport support"
depends on GAMEPORT
config GAMEPORT_CS461x
tristate "Crystal SoundFusion gameport support"
depends on GAMEPORT

View File

@@ -0,0 +1,13 @@
#
# Makefile for the gameport drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_GAMEPORT) += gameport.o
obj-$(CONFIG_GAMEPORT_CS461X) += cs461x.o
obj-$(CONFIG_GAMEPORT_EMU10K1) += emu10k1-gp.o
obj-$(CONFIG_GAMEPORT_FM801) += fm801-gp.o
obj-$(CONFIG_GAMEPORT_L4) += lightning.o
obj-$(CONFIG_GAMEPORT_NS558) += ns558.o
obj-$(CONFIG_GAMEPORT_VORTEX) += vortex.o

View File

@@ -0,0 +1,333 @@
/*
The all defines and part of code (such as cs461x_*) are
contributed from ALSA 0.5.8 sources.
See http://www.alsa-project.org/ for sources
Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610
*/
#include <asm/io.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#include <linux/pci.h>
MODULE_AUTHOR("Victor Krapivin <vik@belcaf.minsk.by>");
MODULE_LICENSE("GPL");
/*
These options are experimental
#define CS461X_FULL_MAP
*/
#ifndef PCI_VENDOR_ID_CIRRUS
#define PCI_VENDOR_ID_CIRRUS 0x1013
#endif
#ifndef PCI_DEVICE_ID_CIRRUS_4610
#define PCI_DEVICE_ID_CIRRUS_4610 0x6001
#endif
#ifndef PCI_DEVICE_ID_CIRRUS_4612
#define PCI_DEVICE_ID_CIRRUS_4612 0x6003
#endif
#ifndef PCI_DEVICE_ID_CIRRUS_4615
#define PCI_DEVICE_ID_CIRRUS_4615 0x6004
#endif
/* Registers */
#define BA0_JSPT 0x00000480
#define BA0_JSCTL 0x00000484
#define BA0_JSC1 0x00000488
#define BA0_JSC2 0x0000048C
#define BA0_JSIO 0x000004A0
/* Bits for JSPT */
#define JSPT_CAX 0x00000001
#define JSPT_CAY 0x00000002
#define JSPT_CBX 0x00000004
#define JSPT_CBY 0x00000008
#define JSPT_BA1 0x00000010
#define JSPT_BA2 0x00000020
#define JSPT_BB1 0x00000040
#define JSPT_BB2 0x00000080
/* Bits for JSCTL */
#define JSCTL_SP_MASK 0x00000003
#define JSCTL_SP_SLOW 0x00000000
#define JSCTL_SP_MEDIUM_SLOW 0x00000001
#define JSCTL_SP_MEDIUM_FAST 0x00000002
#define JSCTL_SP_FAST 0x00000003
#define JSCTL_ARE 0x00000004
/* Data register pairs masks */
#define JSC1_Y1V_MASK 0x0000FFFF
#define JSC1_X1V_MASK 0xFFFF0000
#define JSC1_Y1V_SHIFT 0
#define JSC1_X1V_SHIFT 16
#define JSC2_Y2V_MASK 0x0000FFFF
#define JSC2_X2V_MASK 0xFFFF0000
#define JSC2_Y2V_SHIFT 0
#define JSC2_X2V_SHIFT 16
/* JS GPIO */
#define JSIO_DAX 0x00000001
#define JSIO_DAY 0x00000002
#define JSIO_DBX 0x00000004
#define JSIO_DBY 0x00000008
#define JSIO_AXOE 0x00000010
#define JSIO_AYOE 0x00000020
#define JSIO_BXOE 0x00000040
#define JSIO_BYOE 0x00000080
/*
The card initialization code is obfuscated; the module cs461x
need to be loaded after ALSA modules initialized and something
played on the CS 4610 chip (see sources for details of CS4610
initialization code from ALSA)
*/
/* Card specific definitions */
#define CS461X_BA0_SIZE 0x2000
#define CS461X_BA1_DATA0_SIZE 0x3000
#define CS461X_BA1_DATA1_SIZE 0x3800
#define CS461X_BA1_PRG_SIZE 0x7000
#define CS461X_BA1_REG_SIZE 0x0100
#define BA1_SP_DMEM0 0x00000000
#define BA1_SP_DMEM1 0x00010000
#define BA1_SP_PMEM 0x00020000
#define BA1_SP_REG 0x00030000
#define BA1_DWORD_SIZE (13 * 1024 + 512)
#define BA1_MEMORY_COUNT 3
/*
Only one CS461x card is still suppoted; the code requires
redesign to avoid this limitatuion.
*/
static unsigned long ba0_addr;
static unsigned int *ba0;
static char phys[32];
static char name[] = "CS416x Gameport";
#ifdef CS461X_FULL_MAP
static unsigned long ba1_addr;
static union ba1_t {
struct {
unsigned int *data0;
unsigned int *data1;
unsigned int *pmem;
unsigned int *reg;
} name;
unsigned int *idx[4];
} ba1;
static void cs461x_poke(unsigned long reg, unsigned int val)
{
ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff] = val;
}
static unsigned int cs461x_peek(unsigned long reg)
{
return ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff];
}
#endif
static void cs461x_pokeBA0(unsigned long reg, unsigned int val)
{
ba0[reg >> 2] = val;
}
static unsigned int cs461x_peekBA0(unsigned long reg)
{
return ba0[reg >> 2];
}
static int cs461x_free(struct pci_dev *pdev)
{
struct gameport *port = pci_get_drvdata(pdev);
if(port){
gameport_unregister_port(port);
kfree(port);
}
if (ba0) iounmap(ba0);
#ifdef CS461X_FULL_MAP
if (ba1.name.data0) iounmap(ba1.name.data0);
if (ba1.name.data1) iounmap(ba1.name.data1);
if (ba1.name.pmem) iounmap(ba1.name.pmem);
if (ba1.name.reg) iounmap(ba1.name.reg);
#endif
return 0;
}
static void cs461x_gameport_trigger(struct gameport *gameport)
{
cs461x_pokeBA0(BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF);
}
static unsigned char cs461x_gameport_read(struct gameport *gameport)
{
return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io);
}
static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
unsigned js1, js2, jst;
js1 = cs461x_peekBA0(BA0_JSC1);
js2 = cs461x_peekBA0(BA0_JSC2);
jst = cs461x_peekBA0(BA0_JSPT);
*buttons = (~jst >> 4) & 0x0F;
axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
for(jst=0;jst<4;++jst)
if(axes[jst]==0xFFFF) axes[jst] = -1;
return 0;
}
static int cs461x_gameport_open(struct gameport *gameport, int mode)
{
switch (mode) {
case GAMEPORT_MODE_COOKED:
case GAMEPORT_MODE_RAW:
return 0;
default:
return -1;
}
return 0;
}
static struct pci_device_id cs461x_pci_tbl[] = {
{ PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */
{ PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */
{ PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl);
static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc;
struct gameport* port;
rc = pci_enable_device(pdev);
if (rc) {
printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n",
pdev->bus->number, pdev->devfn, rc);
return rc;
}
ba0_addr = pci_resource_start(pdev, 0);
#ifdef CS461X_FULL_MAP
ba1_addr = pci_resource_start(pdev, 1);
#endif
if (ba0_addr == 0 || ba0_addr == ~0
#ifdef CS461X_FULL_MAP
|| ba1_addr == 0 || ba1_addr == ~0
#endif
) {
printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr);
#ifdef CS461X_FULL_MAP
printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr);
#endif
cs461x_free(pdev);
return -ENOMEM;
}
ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE);
#ifdef CS461X_FULL_MAP
ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
ba1.name.pmem = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
ba1.name.reg = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
if (ba0 == NULL || ba1.name.data0 == NULL ||
ba1.name.data1 == NULL || ba1.name.pmem == NULL ||
ba1.name.reg == NULL) {
cs461x_free(pdev);
return -ENOMEM;
}
#else
if (ba0 == NULL){
cs461x_free(pdev);
return -ENOMEM;
}
#endif
if (!(port = kmalloc(sizeof(struct gameport), GFP_KERNEL))) {
printk(KERN_ERR "Memory allocation failed.\n");
cs461x_free(pdev);
return -ENOMEM;
}
memset(port, 0, sizeof(struct gameport));
pci_set_drvdata(pdev, port);
port->open = cs461x_gameport_open;
port->trigger = cs461x_gameport_trigger;
port->read = cs461x_gameport_read;
port->cooked_read = cs461x_gameport_cooked_read;
sprintf(phys, "pci%s/gameport0", pci_name(pdev));
port->name = name;
port->phys = phys;
port->id.bustype = BUS_PCI;
port->id.vendor = pdev->vendor;
port->id.product = pdev->device;
cs461x_pokeBA0(BA0_JSIO, 0xFF); // ?
cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
gameport_register_port(port);
printk(KERN_INFO "gameport: %s on pci%s speed %d kHz\n",
name, pci_name(pdev), port->speed);
return 0;
}
static void __devexit cs461x_pci_remove(struct pci_dev *pdev)
{
cs461x_free(pdev);
}
static struct pci_driver cs461x_pci_driver = {
.name = "CS461x_gameport",
.id_table = cs461x_pci_tbl,
.probe = cs461x_pci_probe,
.remove = __devexit_p(cs461x_pci_remove),
};
int __init cs461x_init(void)
{
return pci_module_init(&cs461x_pci_driver);
}
void __exit cs461x_exit(void)
{
pci_unregister_driver(&cs461x_pci_driver);
}
module_init(cs461x_init);
module_exit(cs461x_exit);

View File

@@ -0,0 +1,132 @@
/*
* $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $
*
* Copyright (c) 2001 Vojtech Pavlik
*/
/*
* EMU10k1 - SB Live / Audigy - gameport driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#include <linux/pci.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("EMU10k1 gameport driver");
MODULE_LICENSE("GPL");
struct emu {
struct pci_dev *dev;
struct gameport gameport;
int size;
char phys[32];
};
static struct pci_device_id emu_tbl[] = {
{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
{ 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
{ 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */
{ 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, emu_tbl);
static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int ioport, iolen;
struct emu *emu;
if (pci_enable_device(pdev))
return -EBUSY;
ioport = pci_resource_start(pdev, 0);
iolen = pci_resource_len(pdev, 0);
if (!request_region(ioport, iolen, "emu10k1-gp"))
return -EBUSY;
if (!(emu = kmalloc(sizeof(struct emu), GFP_KERNEL))) {
printk(KERN_ERR "emu10k1-gp: Memory allocation failed.\n");
release_region(ioport, iolen);
return -ENOMEM;
}
memset(emu, 0, sizeof(struct emu));
sprintf(emu->phys, "pci%s/gameport0", pci_name(pdev));
emu->size = iolen;
emu->dev = pdev;
emu->gameport.io = ioport;
emu->gameport.name = pci_name(pdev);
emu->gameport.phys = emu->phys;
emu->gameport.id.bustype = BUS_PCI;
emu->gameport.id.vendor = pdev->vendor;
emu->gameport.id.product = pdev->device;
pci_set_drvdata(pdev, emu);
gameport_register_port(&emu->gameport);
printk(KERN_INFO "gameport: pci%s speed %d kHz\n",
pci_name(pdev), emu->gameport.speed);
return 0;
}
static void __devexit emu_remove(struct pci_dev *pdev)
{
struct emu *emu = pci_get_drvdata(pdev);
gameport_unregister_port(&emu->gameport);
release_region(emu->gameport.io, emu->size);
kfree(emu);
}
static struct pci_driver emu_driver = {
.name = "Emu10k1_gameport",
.id_table = emu_tbl,
.probe = emu_probe,
.remove = __devexit_p(emu_remove),
};
int __init emu_init(void)
{
return pci_module_init(&emu_driver);
}
void __exit emu_exit(void)
{
pci_unregister_driver(&emu_driver);
}
module_init(emu_init);
module_exit(emu_exit);

View File

@@ -0,0 +1,162 @@
/*
* FM801 gameport driver for Linux
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gameport.h>
#define PCI_VENDOR_ID_FORTEMEDIA 0x1319
#define PCI_DEVICE_ID_FM801_GP 0x0802
#define HAVE_COOKED
struct fm801_gp {
struct gameport gameport;
struct resource *res_port;
char phys[32];
char name[32];
};
#ifdef HAVE_COOKED
static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
unsigned short w;
w = inw(gameport->io + 2);
*buttons = (~w >> 14) & 0x03;
axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
w = inw(gameport->io + 4);
axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
w = inw(gameport->io + 6);
*buttons |= ((~w >> 14) & 0x03) << 2;
axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
w = inw(gameport->io + 8);
axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
outw(0xff, gameport->io); /* reset */
return 0;
}
#endif
static int fm801_gp_open(struct gameport *gameport, int mode)
{
switch (mode) {
#ifdef HAVE_COOKED
case GAMEPORT_MODE_COOKED:
return 0;
#endif
case GAMEPORT_MODE_RAW:
return 0;
default:
return -1;
}
return 0;
}
static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
struct fm801_gp *gp;
if (! (gp = kmalloc(sizeof(*gp), GFP_KERNEL))) {
printk("cannot malloc for fm801-gp\n");
return -1;
}
memset(gp, 0, sizeof(*gp));
gp->gameport.open = fm801_gp_open;
#ifdef HAVE_COOKED
gp->gameport.cooked_read = fm801_gp_cooked_read;
#endif
pci_enable_device(pci);
gp->gameport.io = pci_resource_start(pci, 0);
if ((gp->res_port = request_region(gp->gameport.io, 0x10, "FM801 GP")) == NULL) {
printk("unable to grab region 0x%x-0x%x\n", gp->gameport.io, gp->gameport.io + 0x0f);
kfree(gp);
return -1;
}
gp->gameport.phys = gp->phys;
gp->gameport.name = gp->name;
gp->gameport.id.bustype = BUS_PCI;
gp->gameport.id.vendor = pci->vendor;
gp->gameport.id.product = pci->device;
pci_set_drvdata(pci, gp);
outb(0x60, gp->gameport.io + 0x0d); /* enable joystick 1 and 2 */
gameport_register_port(&gp->gameport);
printk(KERN_INFO "gameport: at pci%s speed %d kHz\n",
pci_name(pci), gp->gameport.speed);
return 0;
}
static void __devexit fm801_gp_remove(struct pci_dev *pci)
{
struct fm801_gp *gp = pci_get_drvdata(pci);
if (gp) {
gameport_unregister_port(&gp->gameport);
release_resource(gp->res_port);
kfree(gp);
}
}
static struct pci_device_id fm801_gp_id_table[] = {
{ PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0 }
};
static struct pci_driver fm801_gp_driver = {
.name = "FM801_gameport",
.id_table = fm801_gp_id_table,
.probe = fm801_gp_probe,
.remove = __devexit_p(fm801_gp_remove),
};
int __init fm801_gp_init(void)
{
return pci_module_init(&fm801_gp_driver);
}
void __exit fm801_gp_exit(void)
{
pci_unregister_driver(&fm801_gp_driver);
}
module_init(fm801_gp_init);
module_exit(fm801_gp_exit);
MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,180 @@
/*
* Generic gameport layer
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <asm/io.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/delay.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Generic gameport layer");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(gameport_register_port);
EXPORT_SYMBOL(gameport_unregister_port);
EXPORT_SYMBOL(gameport_register_device);
EXPORT_SYMBOL(gameport_unregister_device);
EXPORT_SYMBOL(gameport_open);
EXPORT_SYMBOL(gameport_close);
EXPORT_SYMBOL(gameport_rescan);
EXPORT_SYMBOL(gameport_cooked_read);
static LIST_HEAD(gameport_list);
static LIST_HEAD(gameport_dev_list);
#ifdef __i386__
#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0))
#define GET_TIME(x) do { x = get_time_pit(); } while (0)
static unsigned int get_time_pit(void)
{
extern spinlock_t i8253_lock;
unsigned long flags;
unsigned int count;
spin_lock_irqsave(&i8253_lock, flags);
outb_p(0x00, 0x43);
count = inb_p(0x40);
count |= inb_p(0x40) << 8;
spin_unlock_irqrestore(&i8253_lock, flags);
return count;
}
#endif
/*
* gameport_measure_speed() measures the gameport i/o speed.
*/
static int gameport_measure_speed(struct gameport *gameport)
{
#ifdef __i386__
unsigned int i, t, t1, t2, t3, tx;
unsigned long flags;
if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
return 0;
tx = 1 << 30;
for(i = 0; i < 50; i++) {
local_irq_save(flags);
GET_TIME(t1);
for(t = 0; t < 50; t++) gameport_read(gameport);
GET_TIME(t2);
GET_TIME(t3);
local_irq_restore(flags);
udelay(i * 10);
if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
}
gameport_close(gameport);
return 59659 / (tx < 1 ? 1 : tx);
#else
unsigned int j, t = 0;
j = jiffies; while (j == jiffies);
j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }
gameport_close(gameport);
return t * HZ / 1000;
#endif
}
static void gameport_find_dev(struct gameport *gameport)
{
struct gameport_dev *dev;
list_for_each_entry(dev, &gameport_dev_list, node) {
if (gameport->dev)
break;
if (dev->connect)
dev->connect(gameport, dev);
}
}
void gameport_rescan(struct gameport *gameport)
{
gameport_close(gameport);
gameport_find_dev(gameport);
}
void gameport_register_port(struct gameport *gameport)
{
list_add_tail(&gameport->node, &gameport_list);
gameport->speed = gameport_measure_speed(gameport);
gameport_find_dev(gameport);
}
void gameport_unregister_port(struct gameport *gameport)
{
list_del_init(&gameport->node);
if (gameport->dev && gameport->dev->disconnect)
gameport->dev->disconnect(gameport);
}
void gameport_register_device(struct gameport_dev *dev)
{
struct gameport *gameport;
list_add_tail(&dev->node, &gameport_dev_list);
list_for_each_entry(gameport, &gameport_list, node)
if (!gameport->dev && dev->connect)
dev->connect(gameport, dev);
}
void gameport_unregister_device(struct gameport_dev *dev)
{
struct gameport *gameport;
list_del_init(&dev->node);
list_for_each_entry(gameport, &gameport_list, node) {
if (gameport->dev == dev && dev->disconnect)
dev->disconnect(gameport);
gameport_find_dev(gameport);
}
}
int gameport_open(struct gameport *gameport, struct gameport_dev *dev, int mode)
{
if (gameport->open) {
if (gameport->open(gameport, mode))
return -1;
} else {
if (mode != GAMEPORT_MODE_RAW)
return -1;
}
if (gameport->dev)
return -1;
gameport->dev = dev;
return 0;
}
void gameport_close(struct gameport *gameport)
{
gameport->dev = NULL;
if (gameport->close)
gameport->close(gameport);
}

View File

@@ -0,0 +1,304 @@
/*
* $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* PDPI Lightning 4 gamecard driver for Linux.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#define L4_PORT 0x201
#define L4_SELECT_ANALOG 0xa4
#define L4_SELECT_DIGITAL 0xa5
#define L4_SELECT_SECONDARY 0xa6
#define L4_CMD_ID 0x80
#define L4_CMD_GETCAL 0x92
#define L4_CMD_SETCAL 0x93
#define L4_ID 0x04
#define L4_BUSY 0x01
#define L4_TIMEOUT 80 /* 80 us */
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
MODULE_LICENSE("GPL");
struct l4 {
struct gameport gameport;
unsigned char port;
char phys[32];
} *l4_port[8];
char l4_name[] = "PDPI Lightning 4";
/*
* l4_wait_ready() waits for the L4 to become ready.
*/
static int l4_wait_ready(void)
{
unsigned int t;
t = L4_TIMEOUT;
while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
return -(t<=0);
}
/*
* l4_cooked_read() reads data from the Lightning 4.
*/
static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
struct l4 *l4 = gameport->driver;
unsigned char status;
int i, result = -1;
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
if (inb(L4_PORT) & L4_BUSY) goto fail;
outb(l4->port & 3, L4_PORT);
if (l4_wait_ready()) goto fail;
status = inb(L4_PORT);
for (i = 0; i < 4; i++)
if (status & (1 << i)) {
if (l4_wait_ready()) goto fail;
axes[i] = inb(L4_PORT);
if (axes[i] > 252) axes[i] = -1;
}
if (status & 0x10) {
if (l4_wait_ready()) goto fail;
*buttons = inb(L4_PORT) & 0x0f;
}
result = 0;
fail: outb(L4_SELECT_ANALOG, L4_PORT);
return result;
}
static int l4_open(struct gameport *gameport, int mode)
{
struct l4 *l4 = gameport->driver;
if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
return -1;
outb(L4_SELECT_ANALOG, L4_PORT);
return 0;
}
/*
* l4_getcal() reads the L4 with calibration values.
*/
static int l4_getcal(int port, int *cal)
{
int i, result = -1;
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
if (inb(L4_PORT) & L4_BUSY) goto fail;
outb(L4_CMD_GETCAL, L4_PORT);
if (l4_wait_ready()) goto fail;
if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail;
if (l4_wait_ready()) goto fail;
outb(port & 3, L4_PORT);
for (i = 0; i < 4; i++) {
if (l4_wait_ready()) goto fail;
cal[i] = inb(L4_PORT);
}
result = 0;
fail: outb(L4_SELECT_ANALOG, L4_PORT);
return result;
}
/*
* l4_setcal() programs the L4 with calibration values.
*/
static int l4_setcal(int port, int *cal)
{
int i, result = -1;
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
if (inb(L4_PORT) & L4_BUSY) goto fail;
outb(L4_CMD_SETCAL, L4_PORT);
if (l4_wait_ready()) goto fail;
if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail;
if (l4_wait_ready()) goto fail;
outb(port & 3, L4_PORT);
for (i = 0; i < 4; i++) {
if (l4_wait_ready()) goto fail;
outb(cal[i], L4_PORT);
}
result = 0;
fail: outb(L4_SELECT_ANALOG, L4_PORT);
return result;
}
/*
* l4_calibrate() calibrates the L4 for the attached device, so
* that the device's resistance fits into the L4's 8-bit range.
*/
static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
{
int i, t;
int cal[4];
struct l4 *l4 = gameport->driver;
if (l4_getcal(l4->port, cal))
return -1;
for (i = 0; i < 4; i++) {
t = (max[i] * cal[i]) / 200;
t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
axes[i] = (axes[i] > 252) ? 252 : axes[i];
cal[i] = t;
}
if (l4_setcal(l4->port, cal))
return -1;
return 0;
}
static int __init l4_init(void)
{
int cal[4] = {255,255,255,255};
int i, j, rev, cards = 0;
struct gameport *gameport;
struct l4 *l4;
if (!request_region(L4_PORT, 1, "lightning"))
return -1;
for (i = 0; i < 2; i++) {
outb(L4_SELECT_ANALOG, L4_PORT);
outb(L4_SELECT_DIGITAL + i, L4_PORT);
if (inb(L4_PORT) & L4_BUSY) continue;
outb(L4_CMD_ID, L4_PORT);
if (l4_wait_ready()) continue;
if (inb(L4_PORT) != L4_SELECT_DIGITAL + i) continue;
if (l4_wait_ready()) continue;
if (inb(L4_PORT) != L4_ID) continue;
if (l4_wait_ready()) continue;
rev = inb(L4_PORT);
if (!rev) continue;
if (!(l4_port[i * 4] = kmalloc(sizeof(struct l4) * 4, GFP_KERNEL))) {
printk(KERN_ERR "lightning: Out of memory allocating ports.\n");
continue;
}
memset(l4_port[i * 4], 0, sizeof(struct l4) * 4);
for (j = 0; j < 4; j++) {
l4 = l4_port[i * 4 + j] = l4_port[i * 4] + j;
l4->port = i * 4 + j;
sprintf(l4->phys, "isa%04x/gameport%d", L4_PORT, 4 * i + j);
gameport = &l4->gameport;
gameport->driver = l4;
gameport->open = l4_open;
gameport->cooked_read = l4_cooked_read;
gameport->calibrate = l4_calibrate;
gameport->name = l4_name;
gameport->phys = l4->phys;
gameport->id.bustype = BUS_ISA;
if (!i && !j)
gameport->io = L4_PORT;
if (rev > 0x28) /* on 2.9+ the setcal command works correctly */
l4_setcal(l4->port, cal);
gameport_register_port(gameport);
}
printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
i ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
cards++;
}
outb(L4_SELECT_ANALOG, L4_PORT);
if (!cards) {
release_region(L4_PORT, 1);
return -1;
}
return 0;
}
static void __exit l4_exit(void)
{
int i;
int cal[4] = {59, 59, 59, 59};
for (i = 0; i < 8; i++)
if (l4_port[i]) {
l4_setcal(l4_port[i]->port, cal);
gameport_unregister_port(&l4_port[i]->gameport);
}
outb(L4_SELECT_ANALOG, L4_PORT);
release_region(L4_PORT, 1);
}
module_init(l4_init);
module_exit(l4_exit);

View File

@@ -0,0 +1,304 @@
/*
* $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
* Copyright (c) 1999 Brian Gerst
*/
/*
* NS558 based standard IBM game port driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gameport.h>
#include <linux/slab.h>
#include <linux/pnp.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver");
MODULE_LICENSE("GPL");
#define NS558_ISA 1
#define NS558_PNP 2
static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209,
0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 };
struct ns558 {
int type;
int size;
struct pnp_dev *dev;
struct list_head node;
struct gameport gameport;
char phys[32];
char name[32];
};
static LIST_HEAD(ns558_list);
/*
* ns558_isa_probe() tries to find an isa gameport at the
* specified address, and also checks for mirrors.
* A joystick must be attached for this to work.
*/
static void ns558_isa_probe(int io)
{
int i, j, b;
unsigned char c, u, v;
struct ns558 *port;
/*
* No one should be using this address.
*/
if (!request_region(io, 1, "ns558-isa"))
return;
/*
* We must not be able to write arbitrary values to the port.
* The lower two axis bits must be 1 after a write.
*/
c = inb(io);
outb(~c & ~3, io);
if (~(u = v = inb(io)) & 3) {
outb(c, io);
i = 0;
goto out;
}
/*
* After a trigger, there must be at least some bits changing.
*/
for (i = 0; i < 1000; i++) v &= inb(io);
if (u == v) {
outb(c, io);
i = 0;
goto out;
}
msleep(3);
/*
* After some time (4ms) the axes shouldn't change anymore.
*/
u = inb(io);
for (i = 0; i < 1000; i++)
if ((u ^ inb(io)) & 0xf) {
outb(c, io);
i = 0;
goto out;
}
/*
* And now find the number of mirrors of the port.
*/
for (i = 1; i < 5; i++) {
release_region(io & (-1 << (i-1)), (1 << (i-1)));
if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) /* Don't disturb anyone */
break;
outb(0xff, io & (-1 << i));
for (j = b = 0; j < 1000; j++)
if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++;
msleep(3);
if (b > 300) { /* We allow 30% difference */
release_region(io & (-1 << i), (1 << i));
break;
}
}
i--;
if (i != 4) {
if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
return;
}
if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) {
printk(KERN_ERR "ns558: Memory allocation failed.\n");
goto out;
}
memset(port, 0, sizeof(struct ns558));
port->type = NS558_ISA;
port->size = (1 << i);
port->gameport.io = io;
port->gameport.phys = port->phys;
port->gameport.name = port->name;
port->gameport.id.bustype = BUS_ISA;
sprintf(port->phys, "isa%04x/gameport0", io & (-1 << i));
sprintf(port->name, "NS558 ISA");
gameport_register_port(&port->gameport);
printk(KERN_INFO "gameport: NS558 ISA at %#x", port->gameport.io);
if (port->size > 1) printk(" size %d", port->size);
printk(" speed %d kHz\n", port->gameport.speed);
list_add(&port->node, &ns558_list);
return;
out:
release_region(io & (-1 << i), (1 << i));
}
#ifdef CONFIG_PNP
static struct pnp_device_id pnp_devids[] = {
{ .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */
{ .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */
{ .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */
{ .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */
{ .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */
{ .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */
{ .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */
{ .id = "CSC0001", .driver_data = 0 }, /* CS4232 */
{ .id = "CSC000f", .driver_data = 0 }, /* CS4236 */
{ .id = "CSC0101", .driver_data = 0 }, /* CS4327 */
{ .id = "CTL7001", .driver_data = 0 }, /* SB16 */
{ .id = "CTL7002", .driver_data = 0 }, /* AWE64 */
{ .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */
{ .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */
{ .id = "ESS0001", .driver_data = 0 }, /* ES1869 */
{ .id = "ESS0005", .driver_data = 0 }, /* ES1878 */
{ .id = "ESS6880", .driver_data = 0 }, /* ES688 */
{ .id = "IBM0012", .driver_data = 0 }, /* CS4232 */
{ .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */
{ .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */
{ .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */
{ .id = "PNPb02f", .driver_data = 0 }, /* Generic */
{ .id = "", },
};
MODULE_DEVICE_TABLE(pnp, pnp_devids);
static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
{
int ioport, iolen;
struct ns558 *port;
if (!pnp_port_valid(dev, 0)) {
printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n");
return -ENODEV;
}
ioport = pnp_port_start(dev,0);
iolen = pnp_port_len(dev,0);
if (!request_region(ioport, iolen, "ns558-pnp"))
return -EBUSY;
if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) {
printk(KERN_ERR "ns558: Memory allocation failed.\n");
return -ENOMEM;
}
memset(port, 0, sizeof(struct ns558));
port->type = NS558_PNP;
port->size = iolen;
port->dev = dev;
port->gameport.io = ioport;
port->gameport.phys = port->phys;
port->gameport.name = port->name;
port->gameport.id.bustype = BUS_ISAPNP;
port->gameport.id.version = 0x100;
sprintf(port->phys, "pnp%s/gameport0", dev->dev.bus_id);
sprintf(port->name, "%s", "NS558 PnP Gameport");
gameport_register_port(&port->gameport);
printk(KERN_INFO "gameport: NS558 PnP at pnp%s io %#x",
dev->dev.bus_id, port->gameport.io);
if (iolen > 1) printk(" size %d", iolen);
printk(" speed %d kHz\n", port->gameport.speed);
list_add_tail(&port->node, &ns558_list);
return 0;
}
static struct pnp_driver ns558_pnp_driver = {
.name = "ns558",
.id_table = pnp_devids,
.probe = ns558_pnp_probe,
};
#else
static struct pnp_driver ns558_pnp_driver;
#endif
int __init ns558_init(void)
{
int i = 0;
/*
* Probe for ISA ports.
*/
while (ns558_isa_portlist[i])
ns558_isa_probe(ns558_isa_portlist[i++]);
pnp_register_driver(&ns558_pnp_driver);
return list_empty(&ns558_list) ? -ENODEV : 0;
}
void __exit ns558_exit(void)
{
struct ns558 *port;
list_for_each_entry(port, &ns558_list, node) {
gameport_unregister_port(&port->gameport);
switch (port->type) {
#ifdef CONFIG_PNP
case NS558_PNP:
/* fall through */
#endif
case NS558_ISA:
release_region(port->gameport.io & ~(port->size - 1), port->size);
kfree(port);
break;
default:
break;
}
}
pnp_unregister_driver(&ns558_pnp_driver);
}
module_init(ns558_init);
module_exit(ns558_exit);

View File

@@ -0,0 +1,186 @@
/*
* $Id: vortex.c,v 1.5 2002/07/01 15:39:30 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Raymond Ingles
*/
/*
* Trident 4DWave and Aureal Vortex gameport driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/gameport.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Aureal Vortex and Vortex2 gameport driver");
MODULE_LICENSE("GPL");
#define VORTEX_GCR 0x0c /* Gameport control register */
#define VORTEX_LEG 0x08 /* Legacy port location */
#define VORTEX_AXD 0x10 /* Axes start */
#define VORTEX_DATA_WAIT 20 /* 20 ms */
struct vortex {
struct gameport gameport;
struct pci_dev *dev;
unsigned char __iomem *base;
unsigned char __iomem *io;
char phys[32];
};
static unsigned char vortex_read(struct gameport *gameport)
{
struct vortex *vortex = gameport->driver;
return readb(vortex->io + VORTEX_LEG);
}
static void vortex_trigger(struct gameport *gameport)
{
struct vortex *vortex = gameport->driver;
writeb(0xff, vortex->io + VORTEX_LEG);
}
static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
struct vortex *vortex = gameport->driver;
int i;
*buttons = (~readb(vortex->base + VORTEX_LEG) >> 4) & 0xf;
for (i = 0; i < 4; i++) {
axes[i] = readw(vortex->io + VORTEX_AXD + i * sizeof(u32));
if (axes[i] == 0x1fff) axes[i] = -1;
}
return 0;
}
static int vortex_open(struct gameport *gameport, int mode)
{
struct vortex *vortex = gameport->driver;
switch (mode) {
case GAMEPORT_MODE_COOKED:
writeb(0x40, vortex->io + VORTEX_GCR);
msleep(VORTEX_DATA_WAIT);
return 0;
case GAMEPORT_MODE_RAW:
writeb(0x00, vortex->io + VORTEX_GCR);
return 0;
default:
return -1;
}
return 0;
}
static int __devinit vortex_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct vortex *vortex;
int i;
if (!(vortex = kmalloc(sizeof(struct vortex), GFP_KERNEL)))
return -1;
memset(vortex, 0, sizeof(struct vortex));
vortex->dev = dev;
sprintf(vortex->phys, "pci%s/gameport0", pci_name(dev));
pci_set_drvdata(dev, vortex);
vortex->gameport.driver = vortex;
vortex->gameport.fuzz = 64;
vortex->gameport.read = vortex_read;
vortex->gameport.trigger = vortex_trigger;
vortex->gameport.cooked_read = vortex_cooked_read;
vortex->gameport.open = vortex_open;
vortex->gameport.name = pci_name(dev);
vortex->gameport.phys = vortex->phys;
vortex->gameport.id.bustype = BUS_PCI;
vortex->gameport.id.vendor = dev->vendor;
vortex->gameport.id.product = dev->device;
for (i = 0; i < 6; i++)
if (~pci_resource_flags(dev, i) & IORESOURCE_IO)
break;
pci_enable_device(dev);
vortex->base = ioremap(pci_resource_start(vortex->dev, i),
pci_resource_len(vortex->dev, i));
vortex->io = vortex->base + id->driver_data;
gameport_register_port(&vortex->gameport);
printk(KERN_INFO "gameport at pci%s speed %d kHz\n",
pci_name(dev), vortex->gameport.speed);
return 0;
}
static void __devexit vortex_remove(struct pci_dev *dev)
{
struct vortex *vortex = pci_get_drvdata(dev);
gameport_unregister_port(&vortex->gameport);
iounmap(vortex->base);
kfree(vortex);
}
static struct pci_device_id vortex_id_table[] =
{{ 0x12eb, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x11000 },
{ 0x12eb, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x28800 },
{ 0 }};
static struct pci_driver vortex_driver = {
.name = "vortex_gameport",
.id_table = vortex_id_table,
.probe = vortex_probe,
.remove = __devexit_p(vortex_remove),
};
int __init vortex_init(void)
{
return pci_module_init(&vortex_driver);
}
void __exit vortex_exit(void)
{
pci_unregister_driver(&vortex_driver);
}
module_init(vortex_init);
module_exit(vortex_exit);

View File

@@ -0,0 +1,766 @@
/*
* The input core
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/pm.h>
#include <linux/proc_fs.h>
#include <linux/kobject_uevent.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/devfs_fs_kernel.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(input_register_device);
EXPORT_SYMBOL(input_unregister_device);
EXPORT_SYMBOL(input_register_handler);
EXPORT_SYMBOL(input_unregister_handler);
EXPORT_SYMBOL(input_grab_device);
EXPORT_SYMBOL(input_release_device);
EXPORT_SYMBOL(input_open_device);
EXPORT_SYMBOL(input_close_device);
EXPORT_SYMBOL(input_accept_process);
EXPORT_SYMBOL(input_flush_device);
EXPORT_SYMBOL(input_event);
EXPORT_SYMBOL(input_class);
#define INPUT_DEVICES 256
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
static struct input_handler *input_table[8];
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_bus_input_dir;
DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
static int input_devices_state;
#endif
static inline unsigned int ms_to_jiffies(unsigned int ms)
{
unsigned int j;
j = (ms * HZ + 500) / 1000;
return (j > 0) ? j : 1;
}
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
if (dev->pm_dev)
pm_access(dev->pm_dev);
if (type > EV_MAX || !test_bit(type, dev->evbit))
return;
add_mouse_randomness((type << 4) ^ code ^ (code >> 4) ^ value);
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
if (dev->event) dev->event(dev, type, code, value);
break;
case SYN_REPORT:
if (dev->sync) return;
dev->sync = 1;
break;
}
break;
case EV_KEY:
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;
if (value == 2)
break;
change_bit(code, dev->key);
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + ms_to_jiffies(dev->rep[REP_DELAY]));
}
break;
case EV_ABS:
if (code > ABS_MAX || !test_bit(code, dev->absbit))
return;
if (dev->absfuzz[code]) {
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
return;
if ((value > dev->abs[code] - dev->absfuzz[code]) &&
(value < dev->abs[code] + dev->absfuzz[code]))
value = (dev->abs[code] * 3 + value) >> 2;
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] << 1)))
value = (dev->abs[code] + value) >> 1;
}
if (dev->abs[code] == value)
return;
dev->abs[code] = value;
break;
case EV_REL:
if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
return;
break;
case EV_MSC:
if (code > MSC_MAX || !test_bit(code, dev->mscbit))
return;
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_LED:
if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
return;
change_bit(code, dev->led);
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_SND:
if (code > SND_MAX || !test_bit(code, dev->sndbit))
return;
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_REP:
if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
dev->rep[code] = value;
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_FF:
if (dev->event) dev->event(dev, type, code, value);
break;
}
if (type != EV_SYN)
dev->sync = 0;
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
}
static void input_repeat_key(unsigned long data)
{
struct input_dev *dev = (void *) data;
if (!test_bit(dev->repeat_key, dev->key))
return;
input_event(dev, EV_KEY, dev->repeat_key, 2);
input_sync(dev);
mod_timer(&dev->timer, jiffies + ms_to_jiffies(dev->rep[REP_PERIOD]));
}
int input_accept_process(struct input_handle *handle, struct file *file)
{
if (handle->dev->accept)
return handle->dev->accept(handle->dev, file);
return 0;
}
int input_grab_device(struct input_handle *handle)
{
if (handle->dev->grab)
return -EBUSY;
handle->dev->grab = handle;
return 0;
}
void input_release_device(struct input_handle *handle)
{
if (handle->dev->grab == handle)
handle->dev->grab = NULL;
}
int input_open_device(struct input_handle *handle)
{
if (handle->dev->pm_dev)
pm_access(handle->dev->pm_dev);
handle->open++;
if (handle->dev->open)
return handle->dev->open(handle->dev);
return 0;
}
int input_flush_device(struct input_handle* handle, struct file* file)
{
if (handle->dev->flush)
return handle->dev->flush(handle->dev, file);
return 0;
}
void input_close_device(struct input_handle *handle)
{
input_release_device(handle);
if (handle->dev->pm_dev)
pm_dev_idle(handle->dev->pm_dev);
if (handle->dev->close)
handle->dev->close(handle->dev);
handle->open--;
}
static void input_link_handle(struct input_handle *handle)
{
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handle->handler->h_list);
}
#define MATCH_BIT(bit, max) \
for (i = 0; i < NBITS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != NBITS(max)) \
continue;
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
int i;
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->id.bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->id.vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->id.product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->id.version != dev->id.version)
continue;
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
return id;
}
return NULL;
}
/*
* Input hotplugging interface - loading event handlers based on
* device bitfields.
*/
#ifdef CONFIG_HOTPLUG
/*
* Input hotplugging invokes what /proc/sys/kernel/hotplug says
* (normally /sbin/hotplug) when input devices get added or removed.
*
* This invokes a user mode policy agent, typically helping to load driver
* or other modules, configure the device, and more. Drivers can provide
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
*
*/
#define SPRINTF_BIT_A(bit, name, max) \
do { \
envp[i++] = scratch; \
scratch += sprintf(scratch, name); \
for (j = NBITS(max) - 1; j >= 0; j--) \
if (dev->bit[j]) break; \
for (; j >= 0; j--) \
scratch += sprintf(scratch, "%lx ", dev->bit[j]); \
scratch++; \
} while (0)
#define SPRINTF_BIT_A2(bit, name, max, ev) \
do { \
if (test_bit(ev, dev->evbit)) \
SPRINTF_BIT_A(bit, name, max); \
} while (0)
static void input_call_hotplug(char *verb, struct input_dev *dev)
{
char *argv[3], **envp, *buf, *scratch;
int i = 0, j, value;
if (!hotplug_path[0]) {
printk(KERN_ERR "input.c: calling hotplug without a hotplug agent defined\n");
return;
}
if (in_interrupt()) {
printk(KERN_ERR "input.c: calling hotplug from interrupt\n");
return;
}
if (!current->fs->root) {
printk(KERN_WARNING "input.c: calling hotplug without valid filesystem\n");
return;
}
if (!(envp = (char **) kmalloc(20 * sizeof(char *), GFP_KERNEL))) {
printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
return;
}
if (!(buf = kmalloc(1024, GFP_KERNEL))) {
kfree (envp);
printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
return;
}
argv[0] = hotplug_path;
argv[1] = "input";
argv[2] = NULL;
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
scratch = buf;
envp[i++] = scratch;
scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
envp[i++] = scratch;
scratch += sprintf(scratch, "PRODUCT=%x/%x/%x/%x",
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version) + 1;
if (dev->name) {
envp[i++] = scratch;
scratch += sprintf(scratch, "NAME=%s", dev->name) + 1;
}
if (dev->phys) {
envp[i++] = scratch;
scratch += sprintf(scratch, "PHYS=%s", dev->phys) + 1;
}
SPRINTF_BIT_A(evbit, "EV=", EV_MAX);
SPRINTF_BIT_A2(keybit, "KEY=", KEY_MAX, EV_KEY);
SPRINTF_BIT_A2(relbit, "REL=", REL_MAX, EV_REL);
SPRINTF_BIT_A2(absbit, "ABS=", ABS_MAX, EV_ABS);
SPRINTF_BIT_A2(mscbit, "MSC=", MSC_MAX, EV_MSC);
SPRINTF_BIT_A2(ledbit, "LED=", LED_MAX, EV_LED);
SPRINTF_BIT_A2(sndbit, "SND=", SND_MAX, EV_SND);
SPRINTF_BIT_A2(ffbit, "FF=", FF_MAX, EV_FF);
envp[i++] = NULL;
#ifdef INPUT_DEBUG
printk(KERN_DEBUG "input.c: calling %s %s [%s %s %s %s %s]\n",
argv[0], argv[1], envp[0], envp[1], envp[2], envp[3], envp[4]);
#endif
value = call_usermodehelper(argv [0], argv, envp, 0);
kfree(buf);
kfree(envp);
#ifdef INPUT_DEBUG
if (value != 0)
printk(KERN_DEBUG "input.c: hotplug returned %d\n", value);
#endif
}
#endif
void input_register_device(struct input_dev *dev)
{
struct input_handle *handle;
struct input_handler *handler;
struct input_device_id *id;
set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
INIT_LIST_HEAD(&dev->h_list);
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
#ifdef CONFIG_HOTPLUG
input_call_hotplug("add", dev);
#endif
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
void input_unregister_device(struct input_dev *dev)
{
struct list_head * node, * next;
if (!dev) return;
if (dev->pm_dev)
pm_unregister(dev->pm_dev);
del_timer_sync(&dev->timer);
list_for_each_safe(node, next, &dev->h_list) {
struct input_handle * handle = to_handle(node);
list_del_init(&handle->d_node);
list_del_init(&handle->h_node);
handle->handler->disconnect(handle);
}
#ifdef CONFIG_HOTPLUG
input_call_hotplug("remove", dev);
#endif
list_del_init(&dev->node);
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
void input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
struct input_handle *handle;
struct input_device_id *id;
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
void input_unregister_handler(struct input_handler *handler)
{
struct list_head * node, * next;
list_for_each_safe(node, next, &handler->h_list) {
struct input_handle * handle = to_handle_h(node);
list_del_init(&handle->h_node);
list_del_init(&handle->d_node);
handler->disconnect(handle);
}
list_del_init(&handler->node);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = NULL;
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];
struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))
return -ENODEV;
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
static struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
#ifdef CONFIG_PROC_FS
#define SPRINTF_BIT_B(bit, name, max) \
do { \
len += sprintf(buf + len, "B: %s", name); \
for (i = NBITS(max) - 1; i >= 0; i--) \
if (dev->bit[i]) break; \
for (; i >= 0; i--) \
len += sprintf(buf + len, "%lx ", dev->bit[i]); \
len += sprintf(buf + len, "\n"); \
} while (0)
#define SPRINTF_BIT_B2(bit, name, max, ev) \
do { \
if (test_bit(ev, dev->evbit)) \
SPRINTF_BIT_B(bit, name, max); \
} while (0)
static unsigned int input_devices_poll(struct file *file, poll_table *wait)
{
int state = input_devices_state;
poll_wait(file, &input_devices_poll_wait, wait);
if (state != input_devices_state)
return POLLIN | POLLRDNORM;
return 0;
}
static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
{
struct input_dev *dev;
struct input_handle *handle;
off_t at = 0;
int i, len, cnt = 0;
list_for_each_entry(dev, &input_dev_list, node) {
len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
len += sprintf(buf + len, "H: Handlers=");
list_for_each_entry(handle, &dev->h_list, d_node)
len += sprintf(buf + len, "%s ", handle->name);
len += sprintf(buf + len, "\n");
SPRINTF_BIT_B(evbit, "EV=", EV_MAX);
SPRINTF_BIT_B2(keybit, "KEY=", KEY_MAX, EV_KEY);
SPRINTF_BIT_B2(relbit, "REL=", REL_MAX, EV_REL);
SPRINTF_BIT_B2(absbit, "ABS=", ABS_MAX, EV_ABS);
SPRINTF_BIT_B2(mscbit, "MSC=", MSC_MAX, EV_MSC);
SPRINTF_BIT_B2(ledbit, "LED=", LED_MAX, EV_LED);
SPRINTF_BIT_B2(sndbit, "SND=", SND_MAX, EV_SND);
SPRINTF_BIT_B2(ffbit, "FF=", FF_MAX, EV_FF);
len += sprintf(buf + len, "\n");
at += len;
if (at >= pos) {
if (!*start) {
*start = buf + (pos - (at - len));
cnt = at - pos;
} else cnt += len;
buf += len;
if (cnt >= count)
break;
}
}
if (&dev->node == &input_dev_list)
*eof = 1;
return (count > cnt) ? cnt : count;
}
static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
{
struct input_handler *handler;
off_t at = 0;
int len = 0, cnt = 0;
int i = 0;
list_for_each_entry(handler, &input_handler_list, node) {
if (handler->fops)
len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
i++, handler->name, handler->minor);
else
len = sprintf(buf, "N: Number=%d Name=%s\n",
i++, handler->name);
at += len;
if (at >= pos) {
if (!*start) {
*start = buf + (pos - (at - len));
cnt = at - pos;
} else cnt += len;
buf += len;
if (cnt >= count)
break;
}
}
if (&handler->node == &input_handler_list)
*eof = 1;
return (count > cnt) ? cnt : count;
}
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("input", proc_bus);
if (proc_bus_input_dir == NULL)
return -ENOMEM;
proc_bus_input_dir->owner = THIS_MODULE;
entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
if (entry == NULL) {
remove_proc_entry("input", proc_bus);
return -ENOMEM;
}
entry->owner = THIS_MODULE;
entry->proc_fops->poll = input_devices_poll;
entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
if (entry == NULL) {
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
return -ENOMEM;
}
entry->owner = THIS_MODULE;
return 0;
}
#else /* !CONFIG_PROC_FS */
static inline int input_proc_init(void) { return 0; }
#endif
struct class_simple *input_class;
static int __init input_init(void)
{
int retval = -ENOMEM;
input_class = class_simple_create(THIS_MODULE, "input");
if (IS_ERR(input_class))
return PTR_ERR(input_class);
input_proc_init();
retval = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (retval) {
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("handlers", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
class_simple_destroy(input_class);
return retval;
}
retval = devfs_mk_dir("input");
if (retval) {
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("handlers", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
unregister_chrdev(INPUT_MAJOR, "input");
class_simple_destroy(input_class);
}
return retval;
}
static void __exit input_exit(void)
{
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("handlers", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
devfs_remove("input");
unregister_chrdev(INPUT_MAJOR, "input");
class_simple_destroy(input_class);
}
subsys_initcall(input_init);
module_exit(input_exit);

Binary file not shown.

View File

@@ -0,0 +1,530 @@
/*
* Joystick device driver for the input driver suite.
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 1999 Colin Van Dyke
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <asm/io.h>
#include <asm/system.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/joystick.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/devfs_fs_kernel.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Joystick device interfaces");
MODULE_SUPPORTED_DEVICE("input/js");
MODULE_LICENSE("GPL");
#define JOYDEV_MINOR_BASE 0
#define JOYDEV_MINORS 16
#define JOYDEV_BUFFER_SIZE 64
#define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ)
struct joydev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct list_head list;
struct js_corr corr[ABS_MAX];
struct JS_DATA_SAVE_TYPE glue;
int nabs;
int nkey;
__u16 keymap[KEY_MAX - BTN_MISC];
__u16 keypam[KEY_MAX - BTN_MISC];
__u8 absmap[ABS_MAX];
__u8 abspam[ABS_MAX];
__s16 abs[ABS_MAX];
};
struct joydev_list {
struct js_event buffer[JOYDEV_BUFFER_SIZE];
int head;
int tail;
int startup;
struct fasync_struct *fasync;
struct joydev *joydev;
struct list_head node;
};
static struct joydev *joydev_table[JOYDEV_MINORS];
static int joydev_correct(int value, struct js_corr *corr)
{
switch (corr->type) {
case JS_CORR_NONE:
break;
case JS_CORR_BROKEN:
value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
((corr->coef[2] * (value - corr->coef[0])) >> 14);
break;
default:
return 0;
}
if (value < -32767) return -32767;
if (value > 32767) return 32767;
return value;
}
static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct joydev *joydev = handle->private;
struct joydev_list *list;
struct js_event event;
switch (type) {
case EV_KEY:
if (code < BTN_MISC || value == 2) return;
event.type = JS_EVENT_BUTTON;
event.number = joydev->keymap[code - BTN_MISC];
event.value = value;
break;
case EV_ABS:
event.type = JS_EVENT_AXIS;
event.number = joydev->absmap[code];
event.value = joydev_correct(value, joydev->corr + event.number);
if (event.value == joydev->abs[event.number]) return;
joydev->abs[event.number] = event.value;
break;
default:
return;
}
event.time = MSECS(jiffies);
list_for_each_entry(list, &joydev->list, node) {
memcpy(list->buffer + list->head, &event, sizeof(struct js_event));
if (list->startup == joydev->nabs + joydev->nkey)
if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
list->startup = 0;
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&joydev->wait);
}
static int joydev_fasync(int fd, struct file *file, int on)
{
int retval;
struct joydev_list *list = file->private_data;
retval = fasync_helper(fd, file, on, &list->fasync);
return retval < 0 ? retval : 0;
}
static void joydev_free(struct joydev *joydev)
{
joydev_table[joydev->minor] = NULL;
kfree(joydev);
}
static int joydev_release(struct inode * inode, struct file * file)
{
struct joydev_list *list = file->private_data;
joydev_fasync(-1, file, 0);
list_del(&list->node);
if (!--list->joydev->open) {
if (list->joydev->exist)
input_close_device(&list->joydev->handle);
else
joydev_free(list->joydev);
}
kfree(list);
return 0;
}
static int joydev_open(struct inode *inode, struct file *file)
{
struct joydev_list *list;
int i = iminor(inode) - JOYDEV_MINOR_BASE;
if (i >= JOYDEV_MINORS || !joydev_table[i])
return -ENODEV;
if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL)))
return -ENOMEM;
memset(list, 0, sizeof(struct joydev_list));
list->joydev = joydev_table[i];
list_add_tail(&list->node, &joydev_table[i]->list);
file->private_data = list;
if (!list->joydev->open++)
if (list->joydev->exist)
input_open_device(&list->joydev->handle);
return 0;
}
static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct joydev_list *list = file->private_data;
struct joydev *joydev = list->joydev;
struct input_dev *input = joydev->handle.dev;
int retval = 0;
if (!list->joydev->exist)
return -ENODEV;
if (count < sizeof(struct js_event))
return -EINVAL;
if (count == sizeof(struct JS_DATA_TYPE)) {
struct JS_DATA_TYPE data;
int i;
for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
return -EFAULT;
list->startup = 0;
list->tail = list->head;
return sizeof(struct JS_DATA_TYPE);
}
if (list->startup == joydev->nabs + joydev->nkey
&& list->head == list->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(list->joydev->wait,
!list->joydev->exist ||
list->startup < joydev->nabs + joydev->nkey ||
list->head != list->tail);
if (retval)
return retval;
if (!list->joydev->exist)
return -ENODEV;
while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
struct js_event event;
event.time = MSECS(jiffies);
if (list->startup < joydev->nkey) {
event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
event.number = list->startup;
event.value = !!test_bit(joydev->keypam[event.number], input->key);
} else {
event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
event.number = list->startup - joydev->nkey;
event.value = joydev->abs[event.number];
}
if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
return -EFAULT;
list->startup++;
retval += sizeof(struct js_event);
}
while (list->head != list->tail && retval + sizeof(struct js_event) <= count) {
if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event)))
return -EFAULT;
list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
retval += sizeof(struct js_event);
}
return retval;
}
/* No kernel lock - fine */
static unsigned int joydev_poll(struct file *file, poll_table *wait)
{
struct joydev_list *list = file->private_data;
poll_wait(file, &list->joydev->wait, wait);
if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey)
return POLLIN | POLLRDNORM;
return 0;
}
static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct joydev_list *list = file->private_data;
struct joydev *joydev = list->joydev;
struct input_dev *dev = joydev->handle.dev;
void __user *argp = (void __user *)arg;
int i, j;
if (!joydev->exist) return -ENODEV;
switch (cmd) {
case JS_SET_CAL:
return copy_from_user(&joydev->glue.JS_CORR, argp,
sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
case JS_GET_CAL:
return copy_to_user(argp, &joydev->glue.JS_CORR,
sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
case JS_SET_TIMEOUT:
return get_user(joydev->glue.JS_TIMEOUT, (int __user *) arg);
case JS_GET_TIMEOUT:
return put_user(joydev->glue.JS_TIMEOUT, (int __user *) arg);
case JS_SET_TIMELIMIT:
return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
case JS_GET_TIMELIMIT:
return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
case JS_SET_ALL:
return copy_from_user(&joydev->glue, argp,
sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
case JS_GET_ALL:
return copy_to_user(argp, &joydev->glue,
sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
case JSIOCGVERSION:
return put_user(JS_VERSION, (__u32 __user *) arg);
case JSIOCGAXES:
return put_user(joydev->nabs, (__u8 __user *) arg);
case JSIOCGBUTTONS:
return put_user(joydev->nkey, (__u8 __user *) arg);
case JSIOCSCORR:
if (copy_from_user(joydev->corr, argp,
sizeof(struct js_corr) * joydev->nabs))
return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
j = joydev->abspam[i];
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
}
return 0;
case JSIOCGCORR:
return copy_to_user(argp, joydev->corr,
sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
case JSIOCSAXMAP:
if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * ABS_MAX))
return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
if (joydev->abspam[i] > ABS_MAX) return -EINVAL;
joydev->absmap[joydev->abspam[i]] = i;
}
return 0;
case JSIOCGAXMAP:
return copy_to_user(argp, joydev->abspam,
sizeof(__u8) * ABS_MAX) ? -EFAULT : 0;
case JSIOCSBTNMAP:
if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC)))
return -EFAULT;
for (i = 0; i < joydev->nkey; i++) {
if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL;
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
}
return 0;
case JSIOCGBTNMAP:
return copy_to_user(argp, joydev->keypam,
sizeof(__u16) * (KEY_MAX - BTN_MISC)) ? -EFAULT : 0;
default:
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
int len;
if (!dev->name) return 0;
len = strlen(dev->name) + 1;
if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
if (copy_to_user(argp, dev->name, len)) return -EFAULT;
return len;
}
}
return -EINVAL;
}
static struct file_operations joydev_fops = {
.owner = THIS_MODULE,
.read = joydev_read,
.write = joydev_write,
.poll = joydev_poll,
.open = joydev_open,
.release = joydev_release,
.ioctl = joydev_ioctl,
.fasync = joydev_fasync,
};
static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
struct joydev *joydev;
int i, j, t, minor;
for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
if (minor == JOYDEV_MINORS) {
printk(KERN_ERR "joydev: no more free joydev devices\n");
return NULL;
}
if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL)))
return NULL;
memset(joydev, 0, sizeof(struct joydev));
INIT_LIST_HEAD(&joydev->list);
init_waitqueue_head(&joydev->wait);
joydev->minor = minor;
joydev->exist = 1;
joydev->handle.dev = dev;
joydev->handle.name = joydev->name;
joydev->handle.handler = handler;
joydev->handle.private = joydev;
sprintf(joydev->name, "js%d", minor);
for (i = 0; i < ABS_MAX; i++)
if (test_bit(i, dev->absbit)) {
joydev->absmap[i] = joydev->nabs;
joydev->abspam[joydev->nabs] = i;
joydev->nabs++;
}
for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++)
if (test_bit(i + BTN_MISC, dev->keybit)) {
joydev->keymap[i] = joydev->nkey;
joydev->keypam[joydev->nkey] = i + BTN_MISC;
joydev->nkey++;
}
for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++)
if (test_bit(i + BTN_MISC, dev->keybit)) {
joydev->keymap[i] = joydev->nkey;
joydev->keypam[joydev->nkey] = i + BTN_MISC;
joydev->nkey++;
}
for (i = 0; i < joydev->nabs; i++) {
j = joydev->abspam[i];
if (dev->absmax[j] == dev->absmin[j]) {
joydev->corr[i].type = JS_CORR_NONE;
joydev->abs[i] = dev->abs[j];
continue;
}
joydev->corr[i].type = JS_CORR_BROKEN;
joydev->corr[i].prec = dev->absfuzz[j];
joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j])))
continue;
joydev->corr[i].coef[2] = (1 << 29) / t;
joydev->corr[i].coef[3] = (1 << 29) / t;
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
}
joydev_table[minor] = joydev;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
S_IFCHR|S_IRUGO|S_IWUSR, "input/js%d", minor);
class_simple_device_add(input_class,
MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
dev->dev, "js%d", minor);
return &joydev->handle;
}
static void joydev_disconnect(struct input_handle *handle)
{
struct joydev *joydev = handle->private;
class_simple_device_remove(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor));
devfs_remove("input/js%d", joydev->minor);
joydev->exist = 0;
if (joydev->open)
input_close_device(handle);
else
joydev_free(joydev);
}
static struct input_device_id joydev_blacklist[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT(EV_KEY) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
}, /* Avoid itouchpads, touchscreens and tablets */
{ }, /* Terminating entry */
};
static struct input_device_id joydev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) },
},
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_WHEEL) },
},
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_THROTTLE) },
},
{ }, /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, joydev_ids);
static struct input_handler joydev_handler = {
.event = joydev_event,
.connect = joydev_connect,
.disconnect = joydev_disconnect,
.fops = &joydev_fops,
.minor = JOYDEV_MINOR_BASE,
.name = "joydev",
.id_table = joydev_ids,
.blacklist = joydev_blacklist,
};
static int __init joydev_init(void)
{
input_register_handler(&joydev_handler);
return 0;
}
static void __exit joydev_exit(void)
{
input_unregister_handler(&joydev_handler);
}
module_init(joydev_init);
module_exit(joydev_exit);

View File

@@ -0,0 +1,260 @@
#
# Joystick driver configuration
#
config INPUT_JOYSTICK
bool "Joysticks"
depends on INPUT
help
If you have a joystick, 6dof controller, gamepad, steering wheel,
weapon control system or something like that you can say Y here
and the list of supported devices will be displayed. This option
doesn't affect the kernel.
Please read the file <file:Documentation/input/joystick.txt> which
contains more information.
config JOYSTICK_ANALOG
tristate "Classic PC analog joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
---help---
Say Y here if you have a joystick that connects to the PC
gameport. In addition to the usual PC analog joystick, this driver
supports many extensions, including joysticks with throttle control,
with rudders, additional hats and buttons compatible with CH
Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or
Saitek Cyborg joysticks.
Please read the file <file:Documentation/input/joystick.txt> which
contains more information.
To compile this driver as a module, choose M here: the
module will be called analog.
config JOYSTICK_A3D
tristate "Assasin 3D and MadCatz Panther devices"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have an FPGaming or MadCatz controller using the
A3D protocol over the PC gameport.
To compile this driver as a module, choose M here: the
module will be called a3d.
config JOYSTICK_ADI
tristate "Logitech ADI digital joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a Logitech controller using the ADI
protocol over the PC gameport.
To compile this driver as a module, choose M here: the
module will be called adi.
config JOYSTICK_COBRA
tristate "Creative Labs Blaster Cobra gamepad"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a Creative Labs Blaster Cobra gamepad.
To compile this driver as a module, choose M here: the
module will be called cobra.
config JOYSTICK_GF2K
tristate "Genius Flight2000 Digital joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a Genius Flight2000 or MaxFighter digitally
communicating joystick or gamepad.
To compile this driver as a module, choose M here: the
module will be called gf2k.
config JOYSTICK_GRIP
tristate "Gravis GrIP joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a Gravis controller using the GrIP protocol
over the PC gameport.
To compile this driver as a module, choose M here: the
module will be called grip.
config JOYSTICK_GRIP_MP
tristate "Gravis GrIP MultiPort"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have the original Gravis GrIP MultiPort, a hub
that connects to the gameport and you connect gamepads to it.
To compile this driver as a module, choose M here: the
module will be called grip_mp.
config JOYSTICK_GUILLEMOT
tristate "Guillemot joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a Guillemot joystick using a digital
protocol over the PC gameport.
To compile this driver as a module, choose M here: the
module will be called guillemot.
config JOYSTICK_INTERACT
tristate "InterAct digital joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have an InterAct gameport or joystick
communicating digitally over the gameport.
To compile this driver as a module, choose M here: the
module will be called interact.
config JOYSTICK_SIDEWINDER
tristate "Microsoft SideWinder digital joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a Microsoft controller using the Digital
Overdrive protocol over PC gameport.
To compile this driver as a module, choose M here: the
module will be called sidewinder.
config JOYSTICK_TMDC
tristate "ThrustMaster DirectConnect joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you have a ThrustMaster controller using the
DirectConnect (BSP) protocol over the PC gameport.
To compile this driver as a module, choose M here: the
module will be called tmdc.
source "drivers/input/joystick/iforce/Kconfig"
config JOYSTICK_WARRIOR
tristate "Logitech WingMan Warrior joystick"
depends on INPUT && INPUT_JOYSTICK
select SERIO
help
Say Y here if you have a Logitech WingMan Warrior joystick connected
to your computer's serial port.
To compile this driver as a module, choose M here: the
module will be called warrior.
config JOYSTICK_MAGELLAN
tristate "LogiCad3d Magellan/SpaceMouse 6dof controllers"
depends on INPUT && INPUT_JOYSTICK
select SERIO
help
Say Y here if you have a Magellan or Space Mouse 6DOF controller
connected to your computer's serial port.
To compile this driver as a module, choose M here: the
module will be called magellan.
config JOYSTICK_SPACEORB
tristate "SpaceTec SpaceOrb/Avenger 6dof controllers"
depends on INPUT && INPUT_JOYSTICK
select SERIO
help
Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF
controller connected to your computer's serial port.
To compile this driver as a module, choose M here: the
module will be called spaceorb.
config JOYSTICK_SPACEBALL
tristate "SpaceTec SpaceBall 6dof controllers"
depends on INPUT && INPUT_JOYSTICK
select SERIO
help
Say Y here if you have a SpaceTec SpaceBall 2003/3003/4000 FLX
controller connected to your computer's serial port. For the
SpaceBall 4000 USB model, use the USB HID driver.
To compile this driver as a module, choose M here: the
module will be called spaceball.
config JOYSTICK_STINGER
tristate "Gravis Stinger gamepad"
depends on INPUT && INPUT_JOYSTICK
select SERIO
help
Say Y here if you have a Gravis Stinger connected to one of your
serial ports.
To compile this driver as a module, choose M here: the
module will be called stinger.
config JOYSTICK_TWIDDLER
tristate "Twiddler as a joystick"
depends on INPUT && INPUT_JOYSTICK
select SERIO
help
Say Y here if you have a Handykey Twiddler connected to your
computer's serial port and want to use it as a joystick.
To compile this driver as a module, choose M here: the
module will be called twidjoy.
config JOYSTICK_DB9
tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && PARPORT
---help---
Say Y here if you have a Sega Master System gamepad, Sega Genesis
gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
Commodore, Amstrad CPC joystick connected to your parallel port.
For more information on how to use the driver please read
<file:Documentation/input/joystick-parport.txt>.
To compile this driver as a module, choose M here: the
module will be called db9.
config JOYSTICK_GAMECON
tristate "Multisystem, NES, SNES, N64, PSX joysticks and gamepads"
depends on INPUT && INPUT_JOYSTICK && PARPORT
---help---
Say Y here if you have a Nintendo Entertainment System gamepad,
Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad,
Sony PlayStation gamepad or a Multisystem -- Atari, Amiga,
Commodore, Amstrad CPC joystick connected to your parallel port.
For more information on how to use the driver please read
<file:Documentation/input/joystick-parport.txt>.
To compile this driver as a module, choose M here: the
module will be called gamecon.
config JOYSTICK_TURBOGRAFX
tristate "Multisystem joysticks via TurboGraFX device"
depends on INPUT && INPUT_JOYSTICK && PARPORT
help
Say Y here if you have the TurboGraFX interface by Steffen Schwenke,
and want to use it with Multisystem -- Atari, Amiga, Commodore,
Amstrad CPC joystick. For more information on how to use the driver
please read <file:Documentation/input/joystick-parport.txt>.
To compile this driver as a module, choose M here: the
module will be called turbografx.
config JOYSTICK_AMIGA
tristate "Amiga joysticks"
depends on AMIGA && INPUT && INPUT_JOYSTICK
help
Say Y here if you have an Amiga with a digital joystick connected
to it.
To compile this driver as a module, choose M here: the
module will be called amijoy.
config JOYSTICK_JOYDUMP
tristate "Gameport data dumper"
depends on INPUT && INPUT_JOYSTICK && GAMEPORT
help
Say Y here if you want to dump data from your joystick into the system
log for debugging purposes. Say N if you are making a production
configuration or aren't sure.
To compile this driver as a module, choose M here: the
module will be called joydump.

View File

@@ -0,0 +1,30 @@
#
# Makefile for the input core drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
obj-$(CONFIG_JOYSTICK_ADI) += adi.o
obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o
obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP) += grip.o
obj-$(CONFIG_JOYSTICK_GRIP_MP) += grip_mp.o
obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o
obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o
obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o
obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o
obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/

View File

@@ -0,0 +1,405 @@
/*
* $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* FP-Gaming Assasin 3D joystick driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("FP-Gaming Assasin 3D joystick driver");
MODULE_LICENSE("GPL");
#define A3D_MAX_START 400 /* 400 us */
#define A3D_MAX_STROBE 60 /* 40 us */
#define A3D_DELAY_READ 3 /* 3 ms */
#define A3D_MAX_LENGTH 40 /* 40*3 bits */
#define A3D_REFRESH_TIME HZ/50 /* 20 ms */
#define A3D_MODE_A3D 1 /* Assassin 3D */
#define A3D_MODE_PAN 2 /* Panther */
#define A3D_MODE_OEM 3 /* Panther OEM version */
#define A3D_MODE_PXL 4 /* Panther XL */
char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther",
"MadCatz Panther XL", "MadCatz Panther XL w/ rudder" };
struct a3d {
struct gameport *gameport;
struct gameport adc;
struct input_dev dev;
struct timer_list timer;
int axes[4];
int buttons;
int mode;
int length;
int used;
int reads;
int bads;
char phys[32];
char adcphys[32];
};
/*
* a3d_read_packet() reads an Assassin 3D packet.
*/
static int a3d_read_packet(struct gameport *gameport, int length, char *data)
{
unsigned long flags;
unsigned char u, v;
unsigned int t, s;
int i;
i = 0;
t = gameport_time(gameport, A3D_MAX_START);
s = gameport_time(gameport, A3D_MAX_STROBE);
local_irq_save(flags);
gameport_trigger(gameport);
v = gameport_read(gameport);
while (t > 0 && i < length) {
t--;
u = v; v = gameport_read(gameport);
if (~v & u & 0x10) {
data[i++] = v >> 5;
t = s;
}
}
local_irq_restore(flags);
return i;
}
/*
* a3d_csum() computes checksum of triplet packet
*/
static int a3d_csum(char *data, int count)
{
int i, csum = 0;
for (i = 0; i < count - 2; i++) csum += data[i];
return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
}
static void a3d_read(struct a3d *a3d, unsigned char *data)
{
struct input_dev *dev = &a3d->dev;
switch (a3d->mode) {
case A3D_MODE_A3D:
case A3D_MODE_OEM:
case A3D_MODE_PAN:
input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7));
input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7));
input_report_key(dev, BTN_RIGHT, data[2] & 1);
input_report_key(dev, BTN_LEFT, data[3] & 2);
input_report_key(dev, BTN_MIDDLE, data[3] & 4);
input_sync(dev);
a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
a3d->buttons = ((data[3] << 3) | data[4]) & 0xf;
return;
case A3D_MODE_PXL:
input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7));
input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7));
input_report_key(dev, BTN_RIGHT, data[2] & 1);
input_report_key(dev, BTN_LEFT, data[3] & 2);
input_report_key(dev, BTN_MIDDLE, data[3] & 4);
input_report_key(dev, BTN_SIDE, data[7] & 2);
input_report_key(dev, BTN_EXTRA, data[7] & 4);
input_report_abs(dev, ABS_X, ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128);
input_report_abs(dev, ABS_Y, ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128);
input_report_abs(dev, ABS_RUDDER, ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128);
input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128);
input_report_abs(dev, ABS_HAT0X, ( data[5] & 1) - ((data[5] >> 2) & 1));
input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1));
input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3] & 1));
input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4] & 1));
input_report_key(dev, BTN_TRIGGER, data[8] & 1);
input_report_key(dev, BTN_THUMB, data[8] & 2);
input_report_key(dev, BTN_TOP, data[8] & 4);
input_report_key(dev, BTN_PINKIE, data[7] & 1);
input_sync(dev);
return;
}
}
/*
* a3d_timer() reads and analyzes A3D joystick data.
*/
static void a3d_timer(unsigned long private)
{
struct a3d *a3d = (void *) private;
unsigned char data[A3D_MAX_LENGTH];
a3d->reads++;
if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length
|| data[0] != a3d->mode || a3d_csum(data, a3d->length))
a3d->bads++; else a3d_read(a3d, data);
mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME);
}
/*
* a3d_adc_cooked_read() copies the acis and button data to the
* callers arrays. It could do the read itself, but the caller could
* call this more than 50 times a second, which would use too much CPU.
*/
int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons)
{
struct a3d *a3d = gameport->driver;
int i;
for (i = 0; i < 4; i++)
axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1;
*buttons = a3d->buttons;
return 0;
}
/*
* a3d_adc_open() is the gameport open routine. It refuses to serve
* any but cooked data.
*/
int a3d_adc_open(struct gameport *gameport, int mode)
{
struct a3d *a3d = gameport->driver;
if (mode != GAMEPORT_MODE_COOKED)
return -1;
if (!a3d->used++)
mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME);
return 0;
}
/*
* a3d_adc_close() is a callback from the input close routine.
*/
static void a3d_adc_close(struct gameport *gameport)
{
struct a3d *a3d = gameport->driver;
if (!--a3d->used)
del_timer(&a3d->timer);
}
/*
* a3d_open() is a callback from the input open routine.
*/
static int a3d_open(struct input_dev *dev)
{
struct a3d *a3d = dev->private;
if (!a3d->used++)
mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME);
return 0;
}
/*
* a3d_close() is a callback from the input close routine.
*/
static void a3d_close(struct input_dev *dev)
{
struct a3d *a3d = dev->private;
if (!--a3d->used)
del_timer(&a3d->timer);
}
/*
* a3d_connect() probes for A3D joysticks.
*/
static void a3d_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct a3d *a3d;
unsigned char data[A3D_MAX_LENGTH];
int i;
if (!(a3d = kmalloc(sizeof(struct a3d), GFP_KERNEL)))
return;
memset(a3d, 0, sizeof(struct a3d));
gameport->private = a3d;
a3d->gameport = gameport;
init_timer(&a3d->timer);
a3d->timer.data = (long) a3d;
a3d->timer.function = a3d_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data);
if (!i || a3d_csum(data, i))
goto fail2;
a3d->mode = data[0];
if (!a3d->mode || a3d->mode > 5) {
printk(KERN_WARNING "a3d.c: Unknown A3D device detected "
"(%s, id=%d), contact <vojtech@ucw.cz>\n", gameport->phys, a3d->mode);
goto fail2;
}
sprintf(a3d->phys, "%s/input0", gameport->phys);
sprintf(a3d->adcphys, "%s/gameport0", gameport->phys);
if (a3d->mode == A3D_MODE_PXL) {
int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER };
a3d->length = 33;
init_input_dev(&a3d->dev);
a3d->dev.evbit[0] |= BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL);
a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
a3d->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_RUDDER)
| BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) | BIT(ABS_HAT1X) | BIT(ABS_HAT1Y);
a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE)
| BIT(BTN_SIDE) | BIT(BTN_EXTRA);
a3d->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_PINKIE);
a3d_read(a3d, data);
for (i = 0; i < 4; i++) {
if (i < 2) {
a3d->dev.absmin[axes[i]] = 48;
a3d->dev.absmax[axes[i]] = a3d->dev.abs[axes[i]] * 2 - 48;
a3d->dev.absflat[axes[i]] = 8;
} else {
a3d->dev.absmin[axes[i]] = 2;
a3d->dev.absmax[axes[i]] = 253;
}
a3d->dev.absmin[ABS_HAT0X + i] = -1;
a3d->dev.absmax[ABS_HAT0X + i] = 1;
}
} else {
a3d->length = 29;
init_input_dev(&a3d->dev);
a3d->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL);
a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE);
a3d->adc.driver = a3d;
a3d->adc.open = a3d_adc_open;
a3d->adc.close = a3d_adc_close;
a3d->adc.cooked_read = a3d_adc_cooked_read;
a3d->adc.fuzz = 1;
a3d->adc.name = a3d_names[a3d->mode];
a3d->adc.phys = a3d->adcphys;
a3d->adc.id.bustype = BUS_GAMEPORT;
a3d->adc.id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
a3d->adc.id.product = a3d->mode;
a3d->adc.id.version = 0x0100;
a3d_read(a3d, data);
gameport_register_port(&a3d->adc);
printk(KERN_INFO "gameport: %s on %s\n", a3d_names[a3d->mode], gameport->phys);
}
a3d->dev.private = a3d;
a3d->dev.open = a3d_open;
a3d->dev.close = a3d_close;
a3d->dev.name = a3d_names[a3d->mode];
a3d->dev.phys = a3d->phys;
a3d->dev.id.bustype = BUS_GAMEPORT;
a3d->dev.id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
a3d->dev.id.product = a3d->mode;
a3d->dev.id.version = 0x0100;
input_register_device(&a3d->dev);
printk(KERN_INFO "input: %s on %s\n", a3d_names[a3d->mode], a3d->phys);
return;
fail2: gameport_close(gameport);
fail1: kfree(a3d);
}
static void a3d_disconnect(struct gameport *gameport)
{
struct a3d *a3d = gameport->private;
input_unregister_device(&a3d->dev);
if (a3d->mode < A3D_MODE_PXL)
gameport_unregister_port(&a3d->adc);
gameport_close(gameport);
kfree(a3d);
}
static struct gameport_dev a3d_dev = {
.connect = a3d_connect,
.disconnect = a3d_disconnect,
};
int __init a3d_init(void)
{
gameport_register_device(&a3d_dev);
return 0;
}
void __exit a3d_exit(void)
{
gameport_unregister_device(&a3d_dev);
}
module_init(a3d_init);
module_exit(a3d_exit);

View File

@@ -0,0 +1,564 @@
/*
* $Id: adi.c,v 1.23 2002/01/22 20:26:17 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* Logitech ADI joystick family driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/gameport.h>
#include <linux/init.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Logitech ADI joystick family driver");
MODULE_LICENSE("GPL");
/*
* Times, array sizes, flags, ids.
*/
#define ADI_MAX_START 200 /* Trigger to packet timeout [200us] */
#define ADI_MAX_STROBE 40 /* Single bit timeout [40us] */
#define ADI_REFRESH_TIME HZ/50 /* How often to poll the joystick [20 ms] */
#define ADI_INIT_DELAY 10 /* Delay after init packet [10ms] */
#define ADI_DATA_DELAY 4 /* Delay after data packet [4ms] */
#define ADI_MAX_LENGTH 256
#define ADI_MIN_LENGTH 8
#define ADI_MIN_LEN_LENGTH 10
#define ADI_MIN_ID_LENGTH 66
#define ADI_MAX_NAME_LENGTH 48
#define ADI_MAX_CNAME_LENGTH 16
#define ADI_MAX_PHYS_LENGTH 32
#define ADI_FLAG_HAT 0x04
#define ADI_FLAG_10BIT 0x08
#define ADI_ID_TPD 0x01
#define ADI_ID_WGP 0x06
#define ADI_ID_WGPE 0x08
#define ADI_ID_MAX 0x0a
/*
* Names, buttons, axes ...
*/
static char *adi_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2",
"WingMan Interceptor", "WingMan Formula", "WingMan GamePad",
"WingMan Extreme Digital 3D", "WingMan GamePad Extreme",
"WingMan GamePad USB", "Unknown Device %#x" };
static char adi_wmgpe_abs[] = { ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y };
static char adi_wmi_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
static char adi_wmed3d_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y };
static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT };
static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA };
static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 };
static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs,
adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs };
static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key,
adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key };
/*
* Hat to axis conversion arrays.
*/
static struct {
int x;
int y;
} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
/*
* Per-port information.
*/
struct adi {
struct input_dev dev;
int length;
int ret;
int idx;
unsigned char id;
char buttons;
char axes10;
char axes8;
signed char pad;
char hats;
char *abs;
short *key;
char name[ADI_MAX_NAME_LENGTH];
char cname[ADI_MAX_CNAME_LENGTH];
char phys[ADI_MAX_PHYS_LENGTH];
unsigned char data[ADI_MAX_LENGTH];
};
struct adi_port {
struct gameport *gameport;
struct timer_list timer;
struct adi adi[2];
int bad;
int reads;
int used;
};
/*
* adi_read_packet() reads a Logitech ADI packet.
*/
static void adi_read_packet(struct adi_port *port)
{
struct adi *adi = port->adi;
struct gameport *gameport = port->gameport;
unsigned char u, v, w, x, z;
int t[2], s[2], i;
unsigned long flags;
for (i = 0; i < 2; i++) {
adi[i].ret = -1;
t[i] = gameport_time(gameport, ADI_MAX_START);
s[i] = 0;
}
local_irq_save(flags);
gameport_trigger(gameport);
v = z = gameport_read(gameport);
do {
u = v;
w = u ^ (v = x = gameport_read(gameport));
for (i = 0; i < 2; i++, w >>= 2, x >>= 2) {
t[i]--;
if ((w & 0x30) && s[i]) {
if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) {
adi[i].data[++adi[i].ret] = w;
t[i] = gameport_time(gameport, ADI_MAX_STROBE);
} else t[i] = 0;
} else if (!(x & 0x30)) s[i] = 1;
}
} while (t[0] > 0 || t[1] > 0);
local_irq_restore(flags);
return;
}
/*
* adi_move_bits() detects a possible 2-stream mode, and moves
* the bits accordingly.
*/
static void adi_move_bits(struct adi_port *port, int length)
{
int i;
struct adi *adi = port->adi;
adi[0].idx = adi[1].idx = 0;
if (adi[0].ret <= 0 || adi[1].ret <= 0) return;
if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return;
for (i = 1; i <= adi[1].ret; i++)
adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i];
adi[0].ret += adi[1].ret;
adi[1].ret = -1;
}
/*
* adi_get_bits() gathers bits from the data packet.
*/
static inline int adi_get_bits(struct adi *adi, int count)
{
int bits = 0;
int i;
if ((adi->idx += count) > adi->ret) return 0;
for (i = 0; i < count; i++)
bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i;
return bits;
}
/*
* adi_decode() decodes Logitech joystick data into input events.
*/
static int adi_decode(struct adi *adi)
{
struct input_dev *dev = &adi->dev;
char *abs = adi->abs;
short *key = adi->key;
int i, t;
if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4)))
return -1;
for (i = 0; i < adi->axes10; i++)
input_report_abs(dev, *abs++, adi_get_bits(adi, 10));
for (i = 0; i < adi->axes8; i++)
input_report_abs(dev, *abs++, adi_get_bits(adi, 8));
for (i = 0; i < adi->buttons && i < 63; i++) {
if (i == adi->pad) {
t = adi_get_bits(adi, 4);
input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t & 1));
input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1));
}
input_report_key(dev, *key++, adi_get_bits(adi, 1));
}
for (i = 0; i < adi->hats; i++) {
if ((t = adi_get_bits(adi, 4)) > 8) t = 0;
input_report_abs(dev, *abs++, adi_hat_to_axis[t].x);
input_report_abs(dev, *abs++, adi_hat_to_axis[t].y);
}
for (i = 63; i < adi->buttons; i++)
input_report_key(dev, *key++, adi_get_bits(adi, 1));
input_sync(dev);
return 0;
}
/*
* adi_read() reads the data packet and decodes it.
*/
static int adi_read(struct adi_port *port)
{
int i;
int result = 0;
adi_read_packet(port);
adi_move_bits(port, port->adi[0].length);
for (i = 0; i < 2; i++)
if (port->adi[i].length)
result |= adi_decode(port->adi + i);
return result;
}
/*
* adi_timer() repeatedly polls the Logitech joysticks.
*/
static void adi_timer(unsigned long data)
{
struct adi_port *port = (void *) data;
port->bad -= adi_read(port);
port->reads++;
mod_timer(&port->timer, jiffies + ADI_REFRESH_TIME);
}
/*
* adi_open() is a callback from the input open routine.
*/
static int adi_open(struct input_dev *dev)
{
struct adi_port *port = dev->private;
if (!port->used++)
mod_timer(&port->timer, jiffies + ADI_REFRESH_TIME);
return 0;
}
/*
* adi_close() is a callback from the input close routine.
*/
static void adi_close(struct input_dev *dev)
{
struct adi_port *port = dev->private;
if (!--port->used)
del_timer(&port->timer);
}
/*
* adi_init_digital() sends a trigger & delay sequence
* to reset and initialize a Logitech joystick into digital mode.
*/
static void adi_init_digital(struct gameport *gameport)
{
int seq[] = { 3, -2, -3, 10, -6, -11, -7, -9, 11, 0 };
int i;
for (i = 0; seq[i]; i++) {
gameport_trigger(gameport);
if (seq[i] > 0) msleep(seq[i]);
if (seq[i] < 0) mdelay(-seq[i]);
}
}
static void adi_id_decode(struct adi *adi, struct adi_port *port)
{
int i, t;
if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */
return;
if (adi->ret < (t = adi_get_bits(adi, 10))) {
printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret);
return;
}
adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4);
if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++;
adi->length = adi_get_bits(adi, 10);
if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) {
printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length);
adi->length = 0;
return;
}
adi->axes8 = adi_get_bits(adi, 4);
adi->buttons = adi_get_bits(adi, 6);
if (adi_get_bits(adi, 6) != 8 && adi->hats) {
printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n");
adi->length = 0;
return;
}
adi->buttons += adi_get_bits(adi, 6);
adi->hats += adi_get_bits(adi, 4);
i = adi_get_bits(adi, 4);
if (t & ADI_FLAG_10BIT) {
adi->axes10 = adi->axes8 - i;
adi->axes8 = i;
}
t = adi_get_bits(adi, 4);
for (i = 0; i < t; i++)
adi->cname[i] = adi_get_bits(adi, 8);
adi->cname[i] = 0;
t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4;
if (adi->length != t && adi->length != t + (t & 1)) {
printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length);
adi->length = 0;
return;
}
switch (adi->id) {
case ADI_ID_TPD:
adi->pad = 4;
adi->buttons -= 4;
break;
case ADI_ID_WGP:
adi->pad = 0;
adi->buttons -= 4;
break;
default:
adi->pad = -1;
break;
}
}
static void adi_init_input(struct adi *adi, struct adi_port *port, int half)
{
int i, t;
char buf[ADI_MAX_NAME_LENGTH];
if (!adi->length) return;
init_input_dev(&adi->dev);
t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX;
sprintf(buf, adi_names[t], adi->id);
sprintf(adi->name, "Logitech %s", buf);
sprintf(adi->phys, "%s/input%d", port->gameport->phys, half);
adi->abs = adi_abs[t];
adi->key = adi_key[t];
adi->dev.open = adi_open;
adi->dev.close = adi_close;
adi->dev.name = adi->name;
adi->dev.phys = adi->phys;
adi->dev.id.bustype = BUS_GAMEPORT;
adi->dev.id.vendor = GAMEPORT_ID_VENDOR_LOGITECH;
adi->dev.id.product = adi->id;
adi->dev.id.version = 0x0100;
adi->dev.private = port;
adi->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++)
set_bit(adi->abs[i], adi->dev.absbit);
for (i = 0; i < adi->buttons; i++)
set_bit(adi->key[i], adi->dev.keybit);
}
static void adi_init_center(struct adi *adi)
{
int i, t, x;
if (!adi->length) return;
for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) {
t = adi->abs[i];
x = adi->dev.abs[t];
if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE) {
if (i < adi->axes10) x = 512; else x = 128;
}
if (i < adi->axes10) {
adi->dev.absmax[t] = x * 2 - 64;
adi->dev.absmin[t] = 64;
adi->dev.absfuzz[t] = 2;
adi->dev.absflat[t] = 16;
continue;
}
if (i < adi->axes10 + adi->axes8) {
adi->dev.absmax[t] = x * 2 - 48;
adi->dev.absmin[t] = 48;
adi->dev.absfuzz[t] = 1;
adi->dev.absflat[t] = 16;
continue;
}
adi->dev.absmax[t] = 1;
adi->dev.absmin[t] = -1;
}
}
/*
* adi_connect() probes for Logitech ADI joysticks.
*/
static void adi_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct adi_port *port;
int i;
if (!(port = kmalloc(sizeof(struct adi_port), GFP_KERNEL)))
return;
memset(port, 0, sizeof(struct adi_port));
gameport->private = port;
port->gameport = gameport;
init_timer(&port->timer);
port->timer.data = (long) port;
port->timer.function = adi_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) {
kfree(port);
return;
}
adi_init_digital(gameport);
adi_read_packet(port);
if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH)
adi_move_bits(port, adi_get_bits(port->adi, 10));
for (i = 0; i < 2; i++) {
adi_id_decode(port->adi + i, port);
adi_init_input(port->adi + i, port, i);
}
if (!port->adi[0].length && !port->adi[1].length) {
gameport_close(gameport);
kfree(port);
return;
}
msleep(ADI_INIT_DELAY);
if (adi_read(port)) {
msleep(ADI_DATA_DELAY);
adi_read(port);
}
for (i = 0; i < 2; i++)
if (port->adi[i].length > 0) {
adi_init_center(port->adi + i);
input_register_device(&port->adi[i].dev);
printk(KERN_INFO "input: %s [%s] on %s\n",
port->adi[i].name, port->adi[i].cname, gameport->phys);
}
}
static void adi_disconnect(struct gameport *gameport)
{
int i;
struct adi_port *port = gameport->private;
for (i = 0; i < 2; i++)
if (port->adi[i].length > 0)
input_unregister_device(&port->adi[i].dev);
gameport_close(gameport);
kfree(port);
}
/*
* The gameport device structure.
*/
static struct gameport_dev adi_dev = {
.connect = adi_connect,
.disconnect = adi_disconnect,
};
int __init adi_init(void)
{
gameport_register_device(&adi_dev);
return 0;
}
void __exit adi_exit(void)
{
gameport_unregister_device(&adi_dev);
}
module_init(adi_init);
module_exit(adi_exit);

View File

@@ -0,0 +1,161 @@
/*
* $Id: amijoy.c,v 1.13 2002/01/22 20:26:32 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* Driver for Amiga joysticks for Linux/m68k
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Driver for Amiga joysticks");
MODULE_LICENSE("GPL");
static int amijoy[2] = { 0, 1 };
module_param_array_named(map, amijoy, uint, NULL, 0);
MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is 0,1)");
__obsolete_setup("amijoy=");
static int amijoy_used[2] = { 0, 0 };
static struct input_dev amijoy_dev[2];
static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" };
static char *amijoy_name = "Amiga joystick";
static irqreturn_t amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
int i, data = 0, button = 0;
for (i = 0; i < 2; i++)
if (amijoy[i]) {
switch (i) {
case 0: data = ~custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break;
case 1: data = ~custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break;
}
input_regs(amijoy_dev + i, fp);
input_report_key(amijoy_dev + i, BTN_TRIGGER, button);
input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1));
data = ~(data ^ (data << 1));
input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1));
input_sync(amijoy_dev + i);
}
return IRQ_HANDLED;
}
static int amijoy_open(struct input_dev *dev)
{
int *used = dev->private;
if ((*used)++)
return 0;
if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) {
(*used)--;
printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
return -EBUSY;
}
return 0;
}
static void amijoy_close(struct input_dev *dev)
{
int *used = dev->private;
if (!--(*used))
free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt);
}
static int __init amijoy_init(void)
{
int i, j;
for (i = 0; i < 2; i++)
if (amijoy[i]) {
if (!request_mem_region(CUSTOM_PHYSADDR+10+i*2, 2,
"amijoy [Denise]")) {
if (i == 1 && amijoy[0]) {
input_unregister_device(amijoy_dev);
release_mem_region(CUSTOM_PHYSADDR+10, 2);
}
return -EBUSY;
}
amijoy_dev[i].open = amijoy_open;
amijoy_dev[i].close = amijoy_close;
amijoy_dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
amijoy_dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
amijoy_dev[i].keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
for (j = 0; j < 2; j++) {
amijoy_dev[i].absmin[ABS_X + j] = -1;
amijoy_dev[i].absmax[ABS_X + j] = 1;
}
amijoy_dev[i].name = amijoy_name;
amijoy_dev[i].phys = amijoy_phys[i];
amijoy_dev[i].id.bustype = BUS_AMIGA;
amijoy_dev[i].id.vendor = 0x0001;
amijoy_dev[i].id.product = 0x0003;
amijoy_dev[i].id.version = 0x0100;
amijoy_dev[i].private = amijoy_used + i;
input_register_device(amijoy_dev + i);
printk(KERN_INFO "input: %s at joy%ddat\n", amijoy_name, i);
}
return 0;
}
static void __exit amijoy_exit(void)
{
int i;
for (i = 0; i < 2; i++)
if (amijoy[i]) {
input_unregister_device(amijoy_dev + i);
release_mem_region(CUSTOM_PHYSADDR+10+i*2, 2);
}
}
module_init(amijoy_init);
module_exit(amijoy_exit);

View File

@@ -0,0 +1,762 @@
/*
* $Id: analog.c,v 1.68 2002/01/22 20:18:32 vojtech Exp $
*
* Copyright (c) 1996-2001 Vojtech Pavlik
*/
/*
* Analog joystick and gamepad driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/gameport.h>
#include <asm/timex.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Analog joystick and gamepad driver");
MODULE_LICENSE("GPL");
/*
* Option parsing.
*/
#define ANALOG_PORTS 16
static char *js[ANALOG_PORTS];
static int js_nargs;
static int analog_options[ANALOG_PORTS];
module_param_array_named(map, js, charp, &js_nargs, 0);
MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities");
__obsolete_setup("js=");
/*
* Times, feature definitions.
*/
#define ANALOG_RUDDER 0x00004
#define ANALOG_THROTTLE 0x00008
#define ANALOG_AXES_STD 0x0000f
#define ANALOG_BTNS_STD 0x000f0
#define ANALOG_BTNS_CHF 0x00100
#define ANALOG_HAT1_CHF 0x00200
#define ANALOG_HAT2_CHF 0x00400
#define ANALOG_HAT_FCS 0x00800
#define ANALOG_HATS_ALL 0x00e00
#define ANALOG_BTN_TL 0x01000
#define ANALOG_BTN_TR 0x02000
#define ANALOG_BTN_TL2 0x04000
#define ANALOG_BTN_TR2 0x08000
#define ANALOG_BTNS_TLR 0x03000
#define ANALOG_BTNS_TLR2 0x0c000
#define ANALOG_BTNS_GAMEPAD 0x0f000
#define ANALOG_HBTN_CHF 0x10000
#define ANALOG_ANY_CHF 0x10700
#define ANALOG_SAITEK 0x20000
#define ANALOG_EXTENSIONS 0x7ff00
#define ANALOG_GAMEPAD 0x80000
#define ANALOG_MAX_TIME 3 /* 3 ms */
#define ANALOG_LOOP_TIME 2000 /* 2 * loop */
#define ANALOG_REFRESH_TIME HZ/100 /* 10 ms */
#define ANALOG_SAITEK_DELAY 200 /* 200 us */
#define ANALOG_SAITEK_TIME 2000 /* 2000 us */
#define ANALOG_AXIS_TIME 2 /* 2 * refresh */
#define ANALOG_INIT_RETRIES 8 /* 8 times */
#define ANALOG_FUZZ_BITS 2 /* 2 bit more */
#define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */
#define ANALOG_MAX_NAME_LENGTH 128
#define ANALOG_MAX_PHYS_LENGTH 32
static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE };
static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR };
static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS };
static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE };
static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2,
BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 };
static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 };
struct analog {
struct input_dev dev;
int mask;
short *buttons;
char name[ANALOG_MAX_NAME_LENGTH];
char phys[ANALOG_MAX_PHYS_LENGTH];
};
struct analog_port {
struct gameport *gameport;
struct timer_list timer;
struct analog analog[2];
unsigned char mask;
char saitek;
char cooked;
int bads;
int reads;
int speed;
int loop;
int fuzz;
int axes[4];
int buttons;
int initial[4];
int used;
int axtime;
};
/*
* Time macros.
*/
#ifdef __i386__
#define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
#define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? CLOCK_TICK_RATE / HZ : 0)))
#define TIME_NAME (cpu_has_tsc?"TSC":"PIT")
static unsigned int get_time_pit(void)
{
extern spinlock_t i8253_lock;
unsigned long flags;
unsigned int count;
spin_lock_irqsave(&i8253_lock, flags);
outb_p(0x00, 0x43);
count = inb_p(0x40);
count |= inb_p(0x40) << 8;
spin_unlock_irqrestore(&i8253_lock, flags);
return count;
}
#elif defined(__x86_64__)
#define GET_TIME(x) rdtscl(x)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "TSC"
#elif defined(__alpha__)
#define GET_TIME(x) do { x = get_cycles(); } while (0)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "PCC"
#else
#define FAKE_TIME
static unsigned long analog_faketime = 0;
#define GET_TIME(x) do { x = analog_faketime++; } while(0)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "Unreliable"
#warning Precise timer not defined for this architecture.
#endif
/*
* analog_decode() decodes analog joystick data and reports input events.
*/
static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons)
{
struct input_dev *dev = &analog->dev;
int i, j;
if (analog->mask & ANALOG_HAT_FCS)
for (i = 0; i < 4; i++)
if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) {
buttons |= 1 << (i + 14);
break;
}
for (i = j = 0; i < 6; i++)
if (analog->mask & (0x10 << i))
input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1);
if (analog->mask & ANALOG_HBTN_CHF)
for (i = 0; i < 4; i++)
input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1);
if (analog->mask & ANALOG_BTN_TL)
input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1));
if (analog->mask & ANALOG_BTN_TR)
input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1));
if (analog->mask & ANALOG_BTN_TL2)
input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1)));
if (analog->mask & ANALOG_BTN_TR2)
input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1)));
for (i = j = 0; i < 4; i++)
if (analog->mask & (1 << i))
input_report_abs(dev, analog_axes[j++], axes[i]);
for (i = j = 0; i < 3; i++)
if (analog->mask & analog_exts[i]) {
input_report_abs(dev, analog_hats[j++],
((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1));
input_report_abs(dev, analog_hats[j++],
((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1));
}
input_sync(dev);
}
/*
* analog_cooked_read() reads analog joystick data.
*/
static int analog_cooked_read(struct analog_port *port)
{
struct gameport *gameport = port->gameport;
unsigned int time[4], start, loop, now, loopout, timeout;
unsigned char data[4], this, last;
unsigned long flags;
int i, j;
loopout = (ANALOG_LOOP_TIME * port->loop) / 1000;
timeout = ANALOG_MAX_TIME * port->speed;
local_irq_save(flags);
gameport_trigger(gameport);
GET_TIME(now);
local_irq_restore(flags);
start = now;
this = port->mask;
i = 0;
do {
loop = now;
last = this;
local_irq_disable();
this = gameport_read(gameport) & port->mask;
GET_TIME(now);
local_irq_restore(flags);
if ((last ^ this) && (DELTA(loop, now) < loopout)) {
data[i] = last ^ this;
time[i] = now;
i++;
}
} while (this && (i < 4) && (DELTA(start, now) < timeout));
this <<= 4;
for (--i; i >= 0; i--) {
this |= data[i];
for (j = 0; j < 4; j++)
if (data[i] & (1 << j))
port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
}
return -(this != port->mask);
}
static int analog_button_read(struct analog_port *port, char saitek, char chf)
{
unsigned char u;
int t = 1, i = 0;
int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME);
u = gameport_read(port->gameport);
if (!chf) {
port->buttons = (~u >> 4) & 0xf;
return 0;
}
port->buttons = 0;
while ((~u & 0xf0) && (i < 16) && t) {
port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf];
if (!saitek) return 0;
udelay(ANALOG_SAITEK_DELAY);
t = strobe;
gameport_trigger(port->gameport);
while (((u = gameport_read(port->gameport)) & port->mask) && t) t--;
i++;
}
return -(!t || (i == 16));
}
/*
* analog_timer() repeatedly polls the Analog joysticks.
*/
static void analog_timer(unsigned long data)
{
struct analog_port *port = (void *) data;
int i;
char saitek = !!(port->analog[0].mask & ANALOG_SAITEK);
char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF);
if (port->cooked) {
port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons);
if (chf)
port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0;
port->reads++;
} else {
if (!port->axtime--) {
port->bads -= analog_cooked_read(port);
port->bads -= analog_button_read(port, saitek, chf);
port->reads++;
port->axtime = ANALOG_AXIS_TIME - 1;
} else {
if (!saitek)
analog_button_read(port, saitek, chf);
}
}
for (i = 0; i < 2; i++)
if (port->analog[i].mask)
analog_decode(port->analog + i, port->axes, port->initial, port->buttons);
mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME);
}
/*
* analog_open() is a callback from the input open routine.
*/
static int analog_open(struct input_dev *dev)
{
struct analog_port *port = dev->private;
if (!port->used++)
mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME);
return 0;
}
/*
* analog_close() is a callback from the input close routine.
*/
static void analog_close(struct input_dev *dev)
{
struct analog_port *port = dev->private;
if (!--port->used)
del_timer(&port->timer);
}
/*
* analog_calibrate_timer() calibrates the timer and computes loop
* and timeout values for a joystick port.
*/
static void analog_calibrate_timer(struct analog_port *port)
{
struct gameport *gameport = port->gameport;
unsigned int i, t, tx, t1, t2, t3;
unsigned long flags;
local_irq_save(flags);
GET_TIME(t1);
#ifdef FAKE_TIME
analog_faketime += 830;
#endif
udelay(1000);
GET_TIME(t2);
GET_TIME(t3);
local_irq_restore(flags);
port->speed = DELTA(t1, t2) - DELTA(t2, t3);
tx = ~0;
for (i = 0; i < 50; i++) {
local_irq_save(flags);
GET_TIME(t1);
for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
GET_TIME(t3);
local_irq_restore(flags);
udelay(i);
t = DELTA(t1, t2) - DELTA(t2, t3);
if (t < tx) tx = t;
}
port->loop = tx / 50;
}
/*
* analog_name() constructs a name for an analog joystick.
*/
static void analog_name(struct analog *analog)
{
sprintf(analog->name, "Analog %d-axis %d-button",
hweight8(analog->mask & ANALOG_AXES_STD),
hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
if (analog->mask & ANALOG_HATS_ALL)
sprintf(analog->name, "%s %d-hat",
analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
if (analog->mask & ANALOG_HAT_FCS)
strcat(analog->name, " FCS");
if (analog->mask & ANALOG_ANY_CHF)
strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick");
}
/*
* analog_init_device()
*/
static void analog_init_device(struct analog_port *port, struct analog *analog, int index)
{
int i, j, t, v, w, x, y, z;
analog_name(analog);
sprintf(analog->phys, "%s/input%d", port->gameport->phys, index);
analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
init_input_dev(&analog->dev);
analog->dev.name = analog->name;
analog->dev.phys = analog->phys;
analog->dev.id.bustype = BUS_GAMEPORT;
analog->dev.id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
analog->dev.id.product = analog->mask >> 4;
analog->dev.id.version = 0x0100;
analog->dev.open = analog_open;
analog->dev.close = analog_close;
analog->dev.private = port;
analog->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = j = 0; i < 4; i++)
if (analog->mask & (1 << i)) {
t = analog_axes[j];
x = port->axes[i];
y = (port->axes[0] + port->axes[1]) >> 1;
z = y - port->axes[i];
z = z > 0 ? z : -z;
v = (x >> 3);
w = (x >> 3);
set_bit(t, analog->dev.absbit);
if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3)))
x = y;
if (analog->mask & ANALOG_SAITEK) {
if (i == 2) x = port->axes[i];
v = x - (x >> 2);
w = (x >> 4);
}
analog->dev.absmax[t] = (x << 1) - v;
analog->dev.absmin[t] = v;
analog->dev.absfuzz[t] = port->fuzz;
analog->dev.absflat[t] = w;
j++;
}
for (i = j = 0; i < 3; i++)
if (analog->mask & analog_exts[i])
for (x = 0; x < 2; x++) {
t = analog_hats[j++];
set_bit(t, analog->dev.absbit);
analog->dev.absmax[t] = 1;
analog->dev.absmin[t] = -1;
}
for (i = j = 0; i < 4; i++)
if (analog->mask & (0x10 << i))
set_bit(analog->buttons[j++], analog->dev.keybit);
if (analog->mask & ANALOG_BTNS_CHF)
for (i = 0; i < 2; i++)
set_bit(analog->buttons[j++], analog->dev.keybit);
if (analog->mask & ANALOG_HBTN_CHF)
for (i = 0; i < 4; i++)
set_bit(analog->buttons[j++], analog->dev.keybit);
for (i = 0; i < 4; i++)
if (analog->mask & (ANALOG_BTN_TL << i))
set_bit(analog_pads[i], analog->dev.keybit);
analog_decode(analog, port->axes, port->initial, port->buttons);
input_register_device(&analog->dev);
printk(KERN_INFO "input: %s at %s", analog->name, port->gameport->phys);
if (port->cooked)
printk(" [ADC port]\n");
else
printk(" [%s timer, %d %sHz clock, %d ns res]\n", TIME_NAME,
port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed,
port->speed > 10000 ? "M" : "k",
port->speed > 10000 ? (port->loop * 1000) / (port->speed / 1000)
: (port->loop * 1000000) / port->speed);
}
/*
* analog_init_devices() sets up device-specific values and registers the input devices.
*/
static int analog_init_masks(struct analog_port *port)
{
int i;
struct analog *analog = port->analog;
int max[4];
if (!port->mask)
return -1;
if ((port->mask & 3) != 3 && port->mask != 0xc) {
printk(KERN_WARNING "analog.c: Unknown joystick device found "
"(data=%#x, %s), probably not analog joystick.\n",
port->mask, port->gameport->phys);
return -1;
}
i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */
analog[0].mask = i & 0xfffff;
analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD)
| port->mask | ((port->mask << 8) & ANALOG_HAT_FCS)
| ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2);
analog[0].mask &= ~(ANALOG_HAT2_CHF)
| ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF);
analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2)
| ((~analog[0].mask & ANALOG_HAT_FCS) >> 8)
| ((~analog[0].mask & ANALOG_HAT_FCS) << 2)
| ((~analog[0].mask & ANALOG_HAT_FCS) << 4);
analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER)
| (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10)
& ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12));
analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000);
analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD
: (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD);
if (port->cooked) {
for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1;
if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1;
if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1;
if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1;
if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1;
if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1;
gameport_calibrate(port->gameport, port->axes, max);
}
for (i = 0; i < 4; i++)
port->initial[i] = port->axes[i];
return -!(analog[0].mask || analog[1].mask);
}
static int analog_init_port(struct gameport *gameport, struct gameport_dev *dev, struct analog_port *port)
{
int i, t, u, v;
gameport->private = port;
port->gameport = gameport;
init_timer(&port->timer);
port->timer.data = (long) port;
port->timer.function = analog_timer;
if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) {
analog_calibrate_timer(port);
gameport_trigger(gameport);
t = gameport_read(gameport);
msleep(ANALOG_MAX_TIME);
port->mask = (gameport_read(gameport) ^ t) & t & 0xf;
port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS;
for (i = 0; i < ANALOG_INIT_RETRIES; i++) {
if (!analog_cooked_read(port)) break;
msleep(ANALOG_MAX_TIME);
}
u = v = 0;
msleep(ANALOG_MAX_TIME);
t = gameport_time(gameport, ANALOG_MAX_TIME * 1000);
gameport_trigger(gameport);
while ((gameport_read(port->gameport) & port->mask) && (u < t)) u++;
udelay(ANALOG_SAITEK_DELAY);
t = gameport_time(gameport, ANALOG_SAITEK_TIME);
gameport_trigger(gameport);
while ((gameport_read(port->gameport) & port->mask) && (v < t)) v++;
if (v < (u >> 1)) { /* FIXME - more than one port */
analog_options[0] |= /* FIXME - more than one port */
ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF;
return 0;
}
gameport_close(gameport);
}
if (!gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) {
for (i = 0; i < ANALOG_INIT_RETRIES; i++)
if (!gameport_cooked_read(gameport, port->axes, &port->buttons))
break;
for (i = 0; i < 4; i++)
if (port->axes[i] != -1) port->mask |= 1 << i;
port->fuzz = gameport->fuzz;
port->cooked = 1;
return 0;
}
if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
return 0;
return -1;
}
static void analog_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct analog_port *port;
int i;
if (!(port = kmalloc(sizeof(struct analog_port), GFP_KERNEL)))
return;
memset(port, 0, sizeof(struct analog_port));
if (analog_init_port(gameport, dev, port)) {
kfree(port);
return;
}
if (analog_init_masks(port)) {
gameport_close(gameport);
kfree(port);
return;
}
for (i = 0; i < 2; i++)
if (port->analog[i].mask)
analog_init_device(port, port->analog + i, i);
}
static void analog_disconnect(struct gameport *gameport)
{
int i;
struct analog_port *port = gameport->private;
for (i = 0; i < 2; i++)
if (port->analog[i].mask)
input_unregister_device(&port->analog[i].dev);
gameport_close(gameport);
printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n",
port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0,
port->gameport->phys);
kfree(port);
}
struct analog_types {
char *name;
int value;
};
struct analog_types analog_types[] = {
{ "none", 0x00000000 },
{ "auto", 0x000000ff },
{ "2btn", 0x0000003f },
{ "y-joy", 0x0cc00033 },
{ "y-pad", 0x8cc80033 },
{ "fcs", 0x000008f7 },
{ "chf", 0x000002ff },
{ "fullchf", 0x000007ff },
{ "gamepad", 0x000830f3 },
{ "gamepad8", 0x0008f0f3 },
{ NULL, 0 }
};
static void analog_parse_options(void)
{
int i, j;
char *end;
for (i = 0; i < js_nargs; i++) {
for (j = 0; analog_types[j].name; j++)
if (!strcmp(analog_types[j].name, js[i])) {
analog_options[i] = analog_types[j].value;
break;
}
if (analog_types[j].name) continue;
analog_options[i] = simple_strtoul(js[i], &end, 0);
if (end != js[i]) continue;
analog_options[i] = 0xff;
if (!strlen(js[i])) continue;
printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]);
}
for (; i < ANALOG_PORTS; i++)
analog_options[i] = 0xff;
}
/*
* The gameport device structure.
*/
static struct gameport_dev analog_dev = {
.connect = analog_connect,
.disconnect = analog_disconnect,
};
int __init analog_init(void)
{
analog_parse_options();
gameport_register_device(&analog_dev);
return 0;
}
void __exit analog_exit(void)
{
gameport_unregister_device(&analog_dev);
}
module_init(analog_init);
module_exit(analog_exit);

View File

@@ -0,0 +1,256 @@
/*
* $Id: cobra.c,v 1.19 2002/01/22 20:26:52 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Creative Labs Blaster GamePad Cobra driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Creative Labs Blaster GamePad Cobra driver");
MODULE_LICENSE("GPL");
#define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */
#define COBRA_REFRESH_TIME HZ/50 /* 20 ms between reads */
#define COBRA_LENGTH 36
static char* cobra_name = "Creative Labs Blaster GamePad Cobra";
static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 };
struct cobra {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev[2];
int used;
int reads;
int bads;
unsigned char exists;
char phys[2][32];
};
static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data)
{
unsigned long flags;
unsigned char u, v, w;
__u64 buf[2];
int r[2], t[2];
int i, j, ret;
int strobe = gameport_time(gameport, COBRA_MAX_STROBE);
for (i = 0; i < 2; i++) {
r[i] = buf[i] = 0;
t[i] = COBRA_MAX_STROBE;
}
local_irq_save(flags);
u = gameport_read(gameport);
do {
t[0]--; t[1]--;
v = gameport_read(gameport);
for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
if (w & 0x30) {
if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) {
buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
t[i] = strobe;
u = v;
} else t[i] = 0;
}
} while (t[0] > 0 || t[1] > 0);
local_irq_restore(flags);
ret = 0;
for (i = 0; i < 2; i++) {
if (r[i] != COBRA_LENGTH) continue;
for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1));
if (j < COBRA_LENGTH) ret |= (1 << i);
data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0)
| ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
| ((buf[i] >> 11) & 0x1f00000);
}
return ret;
}
static void cobra_timer(unsigned long private)
{
struct cobra *cobra = (void *) private;
struct input_dev *dev;
unsigned int data[2];
int i, j, r;
cobra->reads++;
if ((r = cobra_read_packet(cobra->gameport, data)) != cobra->exists)
cobra->bads++;
else
for (i = 0; i < 2; i++)
if (cobra->exists & r & (1 << i)) {
dev = cobra->dev + i;
input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1));
input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1));
for (j = 0; cobra_btn[j]; j++)
input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j));
input_sync(dev);
}
mod_timer(&cobra->timer, jiffies + COBRA_REFRESH_TIME);
}
static int cobra_open(struct input_dev *dev)
{
struct cobra *cobra = dev->private;
if (!cobra->used++)
mod_timer(&cobra->timer, jiffies + COBRA_REFRESH_TIME);
return 0;
}
static void cobra_close(struct input_dev *dev)
{
struct cobra *cobra = dev->private;
if (!--cobra->used)
del_timer(&cobra->timer);
}
static void cobra_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct cobra *cobra;
unsigned int data[2];
int i, j;
if (!(cobra = kmalloc(sizeof(struct cobra), GFP_KERNEL)))
return;
memset(cobra, 0, sizeof(struct cobra));
gameport->private = cobra;
cobra->gameport = gameport;
init_timer(&cobra->timer);
cobra->timer.data = (long) cobra;
cobra->timer.function = cobra_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
cobra->exists = cobra_read_packet(gameport, data);
for (i = 0; i < 2; i++)
if ((cobra->exists >> i) & data[i] & 1) {
printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d"
" Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7);
cobra->exists &= ~(1 << i);
}
if (!cobra->exists)
goto fail2;
for (i = 0; i < 2; i++)
if ((cobra->exists >> i) & 1) {
sprintf(cobra->phys[i], "%s/input%d", gameport->phys, i);
cobra->dev[i].private = cobra;
cobra->dev[i].open = cobra_open;
cobra->dev[i].close = cobra_close;
cobra->dev[i].name = cobra_name;
cobra->dev[i].phys = cobra->phys[i];
cobra->dev[i].id.bustype = BUS_GAMEPORT;
cobra->dev[i].id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
cobra->dev[i].id.product = 0x0008;
cobra->dev[i].id.version = 0x0100;
cobra->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
cobra->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
for (j = 0; cobra_btn[j]; j++)
set_bit(cobra_btn[j], cobra->dev[i].keybit);
cobra->dev[i].absmin[ABS_X] = -1; cobra->dev[i].absmax[ABS_X] = 1;
cobra->dev[i].absmin[ABS_Y] = -1; cobra->dev[i].absmax[ABS_Y] = 1;
input_register_device(cobra->dev + i);
printk(KERN_INFO "input: %s on %s\n", cobra_name, gameport->phys);
}
return;
fail2: gameport_close(gameport);
fail1: kfree(cobra);
}
static void cobra_disconnect(struct gameport *gameport)
{
int i;
struct cobra *cobra = gameport->private;
for (i = 0; i < 2; i++)
if ((cobra->exists >> i) & 1)
input_unregister_device(cobra->dev + i);
gameport_close(gameport);
kfree(cobra);
}
static struct gameport_dev cobra_dev = {
.connect = cobra_connect,
.disconnect = cobra_disconnect,
};
int __init cobra_init(void)
{
gameport_register_device(&cobra_dev);
return 0;
}
void __exit cobra_exit(void)
{
gameport_unregister_device(&cobra_dev);
}
module_init(cobra_init);
module_exit(cobra_exit);

View File

@@ -0,0 +1,647 @@
/*
* $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* Andree Borrmann Mats Sjövall
*/
/*
* Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver");
MODULE_LICENSE("GPL");
static int db9[] __initdata = { -1, 0 };
static int db9_nargs __initdata = 0;
module_param_array_named(dev, db9, int, &db9_nargs, 0);
MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
static int db9_2[] __initdata = { -1, 0 };
static int db9_nargs_2 __initdata = 0;
module_param_array_named(dev2, db9_2, int, &db9_nargs_2, 0);
MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)");
static int db9_3[] __initdata = { -1, 0 };
static int db9_nargs_3 __initdata = 0;
module_param_array_named(dev3, db9_3, int, &db9_nargs_3, 0);
MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)");
__obsolete_setup("db9=");
__obsolete_setup("db9_2=");
__obsolete_setup("db9_3=");
#define DB9_MULTI_STICK 0x01
#define DB9_MULTI2_STICK 0x02
#define DB9_GENESIS_PAD 0x03
#define DB9_GENESIS5_PAD 0x05
#define DB9_GENESIS6_PAD 0x06
#define DB9_SATURN_PAD 0x07
#define DB9_MULTI_0802 0x08
#define DB9_MULTI_0802_2 0x09
#define DB9_CD32_PAD 0x0A
#define DB9_SATURN_DPP 0x0B
#define DB9_SATURN_DPP_2 0x0C
#define DB9_MAX_PAD 0x0D
#define DB9_UP 0x01
#define DB9_DOWN 0x02
#define DB9_LEFT 0x04
#define DB9_RIGHT 0x08
#define DB9_FIRE1 0x10
#define DB9_FIRE2 0x20
#define DB9_FIRE3 0x40
#define DB9_FIRE4 0x80
#define DB9_NORMAL 0x0a
#define DB9_NOSELECT 0x08
#define DB9_MAX_DEVICES 2
#define DB9_GENESIS6_DELAY 14
#define DB9_REFRESH_TIME HZ/100
struct db9 {
struct input_dev dev[DB9_MAX_DEVICES];
struct timer_list timer;
struct pardevice *pd;
int mode;
int used;
char phys[2][32];
};
static struct db9 *db9_base[3];
static short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB };
static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7, 9, 9 };
static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn,
db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn,
db9_cd32_btn, db9_cd32_btn };
static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick",
"Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad", "Saturn dpp", "Saturn dpp dual" };
static const int db9_max_pads[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 6, 1, 2, 1, 6, 12 };
static const int db9_num_axis[DB9_MAX_PAD] = { 0, 2, 2, 2, 0, 2, 2, 7, 2, 2, 2 ,7, 7 };
static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
static const int db9_bidirectional[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0 };
static const int db9_reverse[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 };
/*
* Saturn controllers
*/
#define DB9_SATURN_DELAY 300
static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 };
/*
* db9_saturn_write_sub() writes 2 bit data.
*/
static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
{
unsigned char c;
switch (type) {
case 1: /* DPP1 */
c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data;
parport_write_data(port, c);
break;
case 2: /* DPP2 */
c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03;
parport_write_data(port, c);
break;
case 0: /* DB9 */
c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered;
parport_write_control(port, c);
break;
}
}
/*
* gc_saturn_read_sub() reads 4 bit data.
*/
static unsigned char db9_saturn_read_sub(struct parport *port, int type)
{
unsigned char data;
if (type) {
/* DPP */
data = parport_read_status(port) ^ 0x80;
return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0)
| (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0);
} else {
/* DB9 */
data = parport_read_data(port) & 0x0f;
return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0)
| (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0);
}
}
/*
* db9_saturn_read_analog() sends clock and reads 8 bit data.
*/
static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
{
unsigned char data;
db9_saturn_write_sub(port, type, 0, powered, 0);
udelay(DB9_SATURN_DELAY);
data = db9_saturn_read_sub(port, type) << 4;
db9_saturn_write_sub(port, type, 2, powered, 0);
udelay(DB9_SATURN_DELAY);
data |= db9_saturn_read_sub(port, type);
return data;
}
/*
* db9_saturn_read_packet() reads whole saturn packet at connector
* and returns device identifier code.
*/
static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
{
int i, j;
unsigned char tmp;
db9_saturn_write_sub(port, type, 3, powered, 0);
data[0] = db9_saturn_read_sub(port, type);
switch (data[0] & 0x0f) {
case 0xf:
/* 1111 no pad */
return data[0] = 0xff;
case 0x4: case 0x4 | 0x8:
/* ?100 : digital controller */
db9_saturn_write_sub(port, type, 0, powered, 1);
data[2] = db9_saturn_read_sub(port, type) << 4;
db9_saturn_write_sub(port, type, 2, powered, 1);
data[1] = db9_saturn_read_sub(port, type) << 4;
db9_saturn_write_sub(port, type, 1, powered, 1);
data[1] |= db9_saturn_read_sub(port, type);
db9_saturn_write_sub(port, type, 3, powered, 1);
/* data[2] |= db9_saturn_read_sub(port, type); */
data[2] |= data[0];
return data[0] = 0x02;
case 0x1:
/* 0001 : analog controller or multitap */
db9_saturn_write_sub(port, type, 2, powered, 0);
udelay(DB9_SATURN_DELAY);
data[0] = db9_saturn_read_analog(port, type, powered);
if (data[0] != 0x41) {
/* read analog controller */
for (i = 0; i < (data[0] & 0x0f); i++)
data[i + 1] = db9_saturn_read_analog(port, type, powered);
db9_saturn_write_sub(port, type, 3, powered, 0);
return data[0];
} else {
/* read multitap */
if (db9_saturn_read_analog(port, type, powered) != 0x60)
return data[0] = 0xff;
for (i = 0; i < 60; i += 10) {
data[i] = db9_saturn_read_analog(port, type, powered);
if (data[i] != 0xff)
/* read each pad */
for (j = 0; j < (data[i] & 0x0f); j++)
data[i + j + 1] = db9_saturn_read_analog(port, type, powered);
}
db9_saturn_write_sub(port, type, 3, powered, 0);
return 0x41;
}
case 0x0:
/* 0000 : mouse */
db9_saturn_write_sub(port, type, 2, powered, 0);
udelay(DB9_SATURN_DELAY);
tmp = db9_saturn_read_analog(port, type, powered);
if (tmp == 0xff) {
for (i = 0; i < 3; i++)
data[i + 1] = db9_saturn_read_analog(port, type, powered);
db9_saturn_write_sub(port, type, 3, powered, 0);
return data[0] = 0xe3;
}
default:
return data[0];
}
}
/*
* db9_saturn_report() analyzes packet and reports.
*/
static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads)
{
int tmp, i, j;
tmp = (id == 0x41) ? 60 : 10;
for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) {
switch (data[j]) {
case 0x16: /* multi controller (analog 4 axis) */
input_report_abs(dev + n, db9_abs[5], data[j + 6]);
case 0x15: /* mission stick (analog 3 axis) */
input_report_abs(dev + n, db9_abs[3], data[j + 4]);
input_report_abs(dev + n, db9_abs[4], data[j + 5]);
case 0x13: /* racing controller (analog 1 axis) */
input_report_abs(dev + n, db9_abs[2], data[j + 3]);
case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
case 0x02: /* digital pad (digital 2 axis + buttons) */
input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
for (i = 0; i < 9; i++)
input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
break;
case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
for (i = 0; i < 9; i++)
input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
input_report_abs(dev + n, db9_abs[2], data[j + 3]);
input_report_abs(dev + n, db9_abs[3], data[j + 4]);
input_report_abs(dev + n, db9_abs[4], data[j + 5]);
/*
input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
*/
input_report_abs(dev + n, db9_abs[6], data[j + 7]);
input_report_abs(dev + n, db9_abs[7], data[j + 8]);
input_report_abs(dev + n, db9_abs[5], data[j + 9]);
break;
case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
input_report_key(dev + n, BTN_A, data[j + 3] & 0x80);
input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f);
break;
case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80);
input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
break;
case 0xff:
default: /* no pad */
input_report_abs(dev + n, db9_abs[0], 0);
input_report_abs(dev + n, db9_abs[1], 0);
for (i = 0; i < 9; i++)
input_report_key(dev + n, db9_cd32_btn[i], 0);
break;
}
}
return n;
}
static int db9_saturn(int mode, struct parport *port, struct input_dev *dev)
{
unsigned char id, data[60];
int type, n, max_pads;
int tmp, i;
switch (mode) {
case DB9_SATURN_PAD:
type = 0;
n = 1;
break;
case DB9_SATURN_DPP:
type = 1;
n = 1;
break;
case DB9_SATURN_DPP_2:
type = 1;
n = 2;
break;
default:
return -1;
}
max_pads = min(db9_max_pads[mode], DB9_MAX_DEVICES);
for (tmp = 0, i = 0; i < n; i++) {
id = db9_saturn_read_packet(port, data, type + i, 1);
tmp = db9_saturn_report(id, data, dev, tmp, max_pads);
}
return 0;
}
static void db9_timer(unsigned long private)
{
struct db9 *db9 = (void *) private;
struct parport *port = db9->pd->port;
struct input_dev *dev = db9->dev;
int data, i;
switch(db9->mode) {
case DB9_MULTI_0802_2:
data = parport_read_data(port) >> 3;
input_report_abs(dev + 1, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev + 1, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev + 1, BTN_TRIGGER, ~data & DB9_FIRE1);
case DB9_MULTI_0802:
data = parport_read_status(port) >> 3;
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1);
break;
case DB9_MULTI_STICK:
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
break;
case DB9_MULTI2_STICK:
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
input_report_key(dev, BTN_THUMB, ~data & DB9_FIRE2);
break;
case DB9_GENESIS_PAD:
parport_write_control(port, DB9_NOSELECT);
data = parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NORMAL);
data=parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
break;
case DB9_GENESIS5_PAD:
parport_write_control(port, DB9_NOSELECT);
data=parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NORMAL);
data=parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
input_report_key(dev, BTN_X, ~data & DB9_FIRE2);
input_report_key(dev, BTN_Y, ~data & DB9_LEFT);
input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
break;
case DB9_GENESIS6_PAD:
parport_write_control(port, DB9_NOSELECT); /* 1 */
udelay(DB9_GENESIS6_DELAY);
data=parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
data=parport_read_data(port);
input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
parport_write_control(port, DB9_NOSELECT); /* 2 */
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NOSELECT); /* 3 */
udelay(DB9_GENESIS6_DELAY);
data=parport_read_data(port);
input_report_key(dev, BTN_X, ~data & DB9_LEFT);
input_report_key(dev, BTN_Y, ~data & DB9_DOWN);
input_report_key(dev, BTN_Z, ~data & DB9_UP);
input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT);
parport_write_control(port, DB9_NORMAL);
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NOSELECT); /* 4 */
udelay(DB9_GENESIS6_DELAY);
parport_write_control(port, DB9_NORMAL);
break;
case DB9_SATURN_PAD:
case DB9_SATURN_DPP:
case DB9_SATURN_DPP_2:
db9_saturn(db9->mode, port, dev);
break;
case DB9_CD32_PAD:
data=parport_read_data(port);
input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
parport_write_control(port, 0x0a);
for (i = 0; i < 7; i++) {
data = parport_read_data(port);
parport_write_control(port, 0x02);
parport_write_control(port, 0x0a);
input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2);
}
parport_write_control(port, 0x00);
break;
}
input_sync(dev);
mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
}
static int db9_open(struct input_dev *dev)
{
struct db9 *db9 = dev->private;
struct parport *port = db9->pd->port;
if (!db9->used++) {
parport_claim(db9->pd);
parport_write_data(port, 0xff);
if (db9_reverse[db9->mode]) {
parport_data_reverse(port);
parport_write_control(port, DB9_NORMAL);
}
mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
}
return 0;
}
static void db9_close(struct input_dev *dev)
{
struct db9 *db9 = dev->private;
struct parport *port = db9->pd->port;
if (!--db9->used) {
del_timer(&db9->timer);
parport_write_control(port, 0x00);
parport_data_forward(port);
parport_release(db9->pd);
}
}
static struct db9 __init *db9_probe(int *config, int nargs)
{
struct db9 *db9;
struct parport *pp;
int i, j;
if (config[0] < 0)
return NULL;
if (nargs < 2) {
printk(KERN_ERR "db9.c: Device type must be specified.\n");
return NULL;
}
if (config[1] < 1 || config[1] >= DB9_MAX_PAD || !db9_buttons[config[1]]) {
printk(KERN_ERR "db9.c: bad config\n");
return NULL;
}
pp = parport_find_number(config[0]);
if (!pp) {
printk(KERN_ERR "db9.c: no such parport\n");
return NULL;
}
if (db9_bidirectional[config[1]]) {
if (!(pp->modes & PARPORT_MODE_TRISTATE)) {
printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
parport_put_port(pp);
return NULL;
}
}
if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) {
parport_put_port(pp);
return NULL;
}
memset(db9, 0, sizeof(struct db9));
db9->mode = config[1];
init_timer(&db9->timer);
db9->timer.data = (long) db9;
db9->timer.function = db9_timer;
db9->pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
parport_put_port(pp);
if (!db9->pd) {
printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n");
kfree(db9);
return NULL;
}
for (i = 0; i < (min(db9_max_pads[db9->mode], DB9_MAX_DEVICES)); i++) {
sprintf(db9->phys[i], "%s/input%d", db9->pd->port->name, i);
db9->dev[i].private = db9;
db9->dev[i].open = db9_open;
db9->dev[i].close = db9_close;
db9->dev[i].name = db9_name[db9->mode];
db9->dev[i].phys = db9->phys[i];
db9->dev[i].id.bustype = BUS_PARPORT;
db9->dev[i].id.vendor = 0x0002;
db9->dev[i].id.product = config[1];
db9->dev[i].id.version = 0x0100;
db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (j = 0; j < db9_buttons[db9->mode]; j++)
set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit);
for (j = 0; j < db9_num_axis[db9->mode]; j++) {
set_bit(db9_abs[j], db9->dev[i].absbit);
if (j < 2) {
db9->dev[i].absmin[db9_abs[j]] = -1;
db9->dev[i].absmax[db9_abs[j]] = 1;
} else {
db9->dev[i].absmin[db9_abs[j]] = 1;
db9->dev[i].absmax[db9_abs[j]] = 255;
db9->dev[i].absflat[db9_abs[j]] = 0;
}
}
input_register_device(db9->dev + i);
printk(KERN_INFO "input: %s on %s\n", db9->dev[i].name, db9->pd->port->name);
}
return db9;
}
int __init db9_init(void)
{
db9_base[0] = db9_probe(db9, db9_nargs);
db9_base[1] = db9_probe(db9_2, db9_nargs_2);
db9_base[2] = db9_probe(db9_3, db9_nargs_3);
if (db9_base[0] || db9_base[1] || db9_base[2])
return 0;
return -ENODEV;
}
void __exit db9_exit(void)
{
int i, j;
for (i = 0; i < 3; i++)
if (db9_base[i]) {
for (j = 0; j < min(db9_max_pads[db9_base[i]->mode], DB9_MAX_DEVICES); j++)
input_unregister_device(db9_base[i]->dev + j);
parport_unregister_device(db9_base[i]->pd);
}
}
module_init(db9_init);
module_exit(db9_exit);

View File

@@ -0,0 +1,694 @@
/*
* NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
*
* Copyright (c) 1999-2004 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2004 Peter Nelson <rufus-kernel@hackish.org>
*
* Based on the work of:
* Andree Borrmann John Dahlstrom
* David Kuder Nathan Hand
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
MODULE_LICENSE("GPL");
static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 };
static int gc_nargs __initdata = 0;
module_param_array_named(map, gc, int, &gc_nargs, 0);
MODULE_PARM_DESC(map, "Describers first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 };
static int gc_nargs_2 __initdata = 0;
module_param_array_named(map2, gc_2, int, &gc_nargs_2, 0);
MODULE_PARM_DESC(map2, "Describers second set of devices");
static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 };
static int gc_nargs_3 __initdata = 0;
module_param_array_named(map3, gc_3, int, &gc_nargs_3, 0);
MODULE_PARM_DESC(map3, "Describers third set of devices");
__obsolete_setup("gc=");
__obsolete_setup("gc_2=");
__obsolete_setup("gc_3=");
/* see also gs_psx_delay parameter in PSX support section */
#define GC_SNES 1
#define GC_NES 2
#define GC_NES4 3
#define GC_MULTI 4
#define GC_MULTI2 5
#define GC_N64 6
#define GC_PSX 7
#define GC_DDR 8
#define GC_MAX 8
#define GC_REFRESH_TIME HZ/100
struct gc {
struct pardevice *pd;
struct input_dev dev[5];
struct timer_list timer;
unsigned char pads[GC_MAX + 1];
int used;
char phys[5][32];
};
static struct gc *gc_base[3];
static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
"Multisystem 2-button joystick", "N64 controller", "PSX controller"
"PSX DDR controller" };
/*
* N64 support.
*/
static unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 };
static short gc_n64_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START };
#define GC_N64_LENGTH 32 /* N64 bit length, not including stop bit */
#define GC_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */
#define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */
#define GC_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */
#define GC_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */
/* GC_N64_DWS > 24 is known to fail */
#define GC_N64_POWER_W 0xe2 /* power during write (transmit request) */
#define GC_N64_POWER_R 0xfd /* power during read */
#define GC_N64_OUT 0x1d /* output bits to the 4 pads */
/* Reading the main axes of any N64 pad is known to fail if the corresponding bit */
/* in GC_N64_OUT is pulled low on the output port (by any routine) for more */
/* than 123 us */
#define GC_N64_CLOCK 0x02 /* clock bits for read */
/*
* gc_n64_read_packet() reads an N64 packet.
* Each pad uses one bit per byte. So all pads connected to this port are read in parallel.
*/
static void gc_n64_read_packet(struct gc *gc, unsigned char *data)
{
int i;
unsigned long flags;
/*
* Request the pad to transmit data
*/
local_irq_save(flags);
for (i = 0; i < GC_N64_REQUEST_LENGTH; i++) {
parport_write_data(gc->pd->port, GC_N64_POWER_W | ((GC_N64_REQUEST >> i) & 1 ? GC_N64_OUT : 0));
udelay(GC_N64_DWS);
}
local_irq_restore(flags);
/*
* Wait for the pad response to be loaded into the 33-bit register of the adapter
*/
udelay(GC_N64_DELAY);
/*
* Grab data (ignoring the last bit, which is a stop bit)
*/
for (i = 0; i < GC_N64_LENGTH; i++) {
parport_write_data(gc->pd->port, GC_N64_POWER_R);
data[i] = parport_read_status(gc->pd->port);
parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK);
}
/*
* We must wait 200 ms here for the controller to reinitialize before the next read request.
* No worries as long as gc_read is polled less frequently than this.
*/
}
/*
* NES/SNES support.
*/
#define GC_NES_DELAY 6 /* Delay between bits - 6us */
#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */
#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */
#define GC_NES_POWER 0xfc
#define GC_NES_CLOCK 0x01
#define GC_NES_LATCH 0x02
static unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 };
static unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 };
static short gc_snes_btn[] = { BTN_A, BTN_B, BTN_SELECT, BTN_START, BTN_X, BTN_Y, BTN_TL, BTN_TR };
/*
* gc_nes_read_packet() reads a NES/SNES packet.
* Each pad uses one bit per byte. So all pads connected to
* this port are read in parallel.
*/
static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data)
{
int i;
parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH);
udelay(GC_NES_DELAY * 2);
parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
for (i = 0; i < length; i++) {
udelay(GC_NES_DELAY);
parport_write_data(gc->pd->port, GC_NES_POWER);
data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
udelay(GC_NES_DELAY);
parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
}
}
/*
* Multisystem joystick support
*/
#define GC_MULTI_LENGTH 5 /* Multi system joystick packet length is 5 */
#define GC_MULTI2_LENGTH 6 /* One more bit for one more button */
/*
* gc_multi_read_packet() reads a Multisystem joystick packet.
*/
static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data)
{
int i;
for (i = 0; i < length; i++) {
parport_write_data(gc->pd->port, ~(1 << i));
data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
}
}
/*
* PSX support
*
* See documentation at:
* http://www.dim.com/~mackys/psxmemcard/ps-eng2.txt
* http://www.gamesx.com/controldata/psxcont/psxcont.htm
* ftp://milano.usal.es/pablo/
*
*/
#define GC_PSX_DELAY 25 /* 25 usec */
#define GC_PSX_LENGTH 8 /* talk to the controller in bytes */
#define GC_PSX_MOUSE 1 /* Mouse */
#define GC_PSX_NEGCON 2 /* NegCon */
#define GC_PSX_NORMAL 4 /* Digital / Analog or Rumble in Digital mode */
#define GC_PSX_ANALOG 5 /* Analog in Analog mode / Rumble in Green mode */
#define GC_PSX_RUMBLE 7 /* Rumble in Red mode */
#define GC_PSX_CLOCK 0x04 /* Pin 4 */
#define GC_PSX_COMMAND 0x01 /* Pin 2 */
#define GC_PSX_POWER 0xf8 /* Pins 5-9 */
#define GC_PSX_SELECT 0x02 /* Pin 3 */
#define GC_PSX_ID(x) ((x) >> 4) /* High nibble is device type */
#define GC_PSX_LEN(x) ((x) & 0xf) /* Low nibble is length in words */
static int gc_psx_delay = GC_PSX_DELAY;
module_param_named(psx_delay, gc_psx_delay, uint, 0);
MODULE_PARM_DESC(psx_delay, "Delay when accessing Sony PSX controller (usecs)");
__obsolete_setup("gc_psx_delay=");
static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y };
static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y,
BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR };
static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 };
/*
* gc_psx_command() writes 8bit command and reads 8bit data from
* the psx pad.
*/
static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_PSX_LENGTH])
{
int i, j, cmd, read;
for (i = 0; i < 5; i++)
data[i] = 0;
for (i = 0; i < 8; i++, b >>= 1) {
cmd = (b & 1) ? GC_PSX_COMMAND : 0;
parport_write_data(gc->pd->port, cmd | GC_PSX_POWER);
udelay(gc_psx_delay);
read = parport_read_status(gc->pd->port) ^ 0x80;
for (j = 0; j < 5; j++)
data[j] |= (read & gc_status_bit[j] & gc->pads[GC_PSX]) ? (1 << i) : 0;
parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER);
udelay(gc_psx_delay);
}
}
/*
* gc_psx_read_packet() reads a whole psx packet and returns
* device identifier code.
*/
static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGTH], unsigned char id[5])
{
int i, j, max_len = 0;
unsigned long flags;
unsigned char data2[5];
parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */
udelay(gc_psx_delay);
parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER); /* Deselect, begin command */
udelay(gc_psx_delay);
local_irq_save(flags);
gc_psx_command(gc, 0x01, data2); /* Access pad */
gc_psx_command(gc, 0x42, id); /* Get device ids */
gc_psx_command(gc, 0, data2); /* Dump status */
for (i =0; i < 5; i++) /* Find the longest pad */
if((gc_status_bit[i] & gc->pads[GC_PSX]) && (GC_PSX_LEN(id[i]) > max_len))
max_len = GC_PSX_LEN(id[i]);
for (i = 0; i < max_len * 2; i++) { /* Read in all the data */
gc_psx_command(gc, 0, data2);
for (j = 0; j < 5; j++)
data[j][i] = data2[j];
}
local_irq_restore(flags);
parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
for(i = 0; i < 5; i++) /* Set id's to the real value */
id[i] = GC_PSX_ID(id[i]);
}
/*
* gc_timer() reads and analyzes console pads data.
*/
#define GC_MAX_LENGTH GC_N64_LENGTH
static void gc_timer(unsigned long private)
{
struct gc *gc = (void *) private;
struct input_dev *dev = gc->dev;
unsigned char data[GC_MAX_LENGTH];
unsigned char data_psx[5][GC_PSX_LENGTH];
int i, j, s;
/*
* N64 pads - must be read first, any read confuses them for 200 us
*/
if (gc->pads[GC_N64]) {
gc_n64_read_packet(gc, data);
for (i = 0; i < 5; i++) {
s = gc_status_bit[i];
if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) {
signed char axes[2];
axes[0] = axes[1] = 0;
for (j = 0; j < 8; j++) {
if (data[23 - j] & s) axes[0] |= 1 << j;
if (data[31 - j] & s) axes[1] |= 1 << j;
}
input_report_abs(dev + i, ABS_X, axes[0]);
input_report_abs(dev + i, ABS_Y, -axes[1]);
input_report_abs(dev + i, ABS_HAT0X, !(s & data[6]) - !(s & data[7]));
input_report_abs(dev + i, ABS_HAT0Y, !(s & data[4]) - !(s & data[5]));
for (j = 0; j < 10; j++)
input_report_key(dev + i, gc_n64_btn[j], s & data[gc_n64_bytes[j]]);
input_sync(dev + i);
}
}
}
/*
* NES and SNES pads
*/
if (gc->pads[GC_NES] || gc->pads[GC_SNES]) {
gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data);
for (i = 0; i < 5; i++) {
s = gc_status_bit[i];
if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) {
input_report_abs(dev + i, ABS_X, !(s & data[6]) - !(s & data[7]));
input_report_abs(dev + i, ABS_Y, !(s & data[4]) - !(s & data[5]));
}
if (s & gc->pads[GC_NES])
for (j = 0; j < 4; j++)
input_report_key(dev + i, gc_snes_btn[j], s & data[gc_nes_bytes[j]]);
if (s & gc->pads[GC_SNES])
for (j = 0; j < 8; j++)
input_report_key(dev + i, gc_snes_btn[j], s & data[gc_snes_bytes[j]]);
input_sync(dev + i);
}
}
/*
* Multi and Multi2 joysticks
*/
if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) {
gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data);
for (i = 0; i < 5; i++) {
s = gc_status_bit[i];
if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) {
input_report_abs(dev + i, ABS_X, !(s & data[2]) - !(s & data[3]));
input_report_abs(dev + i, ABS_Y, !(s & data[0]) - !(s & data[1]));
input_report_key(dev + i, BTN_TRIGGER, s & data[4]);
}
if (s & gc->pads[GC_MULTI2])
input_report_key(dev + i, BTN_THUMB, s & data[5]);
input_sync(dev + i);
}
}
/*
* PSX controllers
*/
if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) {
gc_psx_read_packet(gc, data_psx, data);
for (i = 0; i < 5; i++) {
switch (data[i]) {
case GC_PSX_RUMBLE:
input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04);
input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02);
case GC_PSX_NEGCON:
case GC_PSX_ANALOG:
if(gc->pads[GC_DDR] & gc_status_bit[i]) {
for(j = 0; j < 4; j++)
input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
} else {
for (j = 0; j < 4; j++)
input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]);
input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
}
for (j = 0; j < 8; j++)
input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08);
input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
input_sync(dev + i);
break;
case GC_PSX_NORMAL:
if(gc->pads[GC_DDR] & gc_status_bit[i]) {
for(j = 0; j < 4; j++)
input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
} else {
input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
/* for some reason if the extra axes are left unset they drift */
/* for (j = 0; j < 4; j++)
input_report_abs(dev + i, gc_psx_abs[j+2], 128);
* This needs to be debugged properly,
* maybe fuzz processing needs to be done in input_sync()
* --vojtech
*/
}
for (j = 0; j < 8; j++)
input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08);
input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
input_sync(dev + i);
break;
case 0: /* not a pad, ignore */
break;
}
}
}
mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
}
static int gc_open(struct input_dev *dev)
{
struct gc *gc = dev->private;
if (!gc->used++) {
parport_claim(gc->pd);
parport_write_control(gc->pd->port, 0x04);
mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
}
return 0;
}
static void gc_close(struct input_dev *dev)
{
struct gc *gc = dev->private;
if (!--gc->used) {
del_timer(&gc->timer);
parport_write_control(gc->pd->port, 0x00);
parport_release(gc->pd);
}
}
static struct gc __init *gc_probe(int *config, int nargs)
{
struct gc *gc;
struct parport *pp;
int i, j;
if (config[0] < 0)
return NULL;
if (nargs < 2) {
printk(KERN_ERR "gamecon.c: at least one device must be specified\n");
return NULL;
}
pp = parport_find_number(config[0]);
if (!pp) {
printk(KERN_ERR "gamecon.c: no such parport\n");
return NULL;
}
if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) {
parport_put_port(pp);
return NULL;
}
memset(gc, 0, sizeof(struct gc));
gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
parport_put_port(pp);
if (!gc->pd) {
printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n");
kfree(gc);
return NULL;
}
parport_claim(gc->pd);
init_timer(&gc->timer);
gc->timer.data = (long) gc;
gc->timer.function = gc_timer;
for (i = 0; i < nargs - 1; i++) {
if (!config[i + 1])
continue;
if (config[i + 1] < 1 || config[i + 1] > GC_MAX) {
printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", config[i + 1]);
continue;
}
gc->dev[i].private = gc;
gc->dev[i].open = gc_open;
gc->dev[i].close = gc_close;
gc->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (j = 0; j < 2; j++) {
set_bit(ABS_X + j, gc->dev[i].absbit);
gc->dev[i].absmin[ABS_X + j] = -1;
gc->dev[i].absmax[ABS_X + j] = 1;
}
gc->pads[0] |= gc_status_bit[i];
gc->pads[config[i + 1]] |= gc_status_bit[i];
switch(config[i + 1]) {
case GC_N64:
for (j = 0; j < 10; j++)
set_bit(gc_n64_btn[j], gc->dev[i].keybit);
for (j = 0; j < 2; j++) {
set_bit(ABS_X + j, gc->dev[i].absbit);
gc->dev[i].absmin[ABS_X + j] = -127;
gc->dev[i].absmax[ABS_X + j] = 126;
gc->dev[i].absflat[ABS_X + j] = 2;
set_bit(ABS_HAT0X + j, gc->dev[i].absbit);
gc->dev[i].absmin[ABS_HAT0X + j] = -1;
gc->dev[i].absmax[ABS_HAT0X + j] = 1;
}
break;
case GC_SNES:
for (j = 4; j < 8; j++)
set_bit(gc_snes_btn[j], gc->dev[i].keybit);
case GC_NES:
for (j = 0; j < 4; j++)
set_bit(gc_snes_btn[j], gc->dev[i].keybit);
break;
case GC_MULTI2:
set_bit(BTN_THUMB, gc->dev[i].keybit);
case GC_MULTI:
set_bit(BTN_TRIGGER, gc->dev[i].keybit);
break;
case GC_PSX:
case GC_DDR:
if(config[i + 1] == GC_DDR) {
for (j = 0; j < 4; j++)
set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit);
} else {
for (j = 0; j < 6; j++) {
set_bit(gc_psx_abs[j], gc->dev[i].absbit);
gc->dev[i].absmin[gc_psx_abs[j]] = 4;
gc->dev[i].absmax[gc_psx_abs[j]] = 252;
gc->dev[i].absflat[gc_psx_abs[j]] = 2;
}
}
for (j = 0; j < 12; j++)
set_bit(gc_psx_btn[j], gc->dev[i].keybit);
break;
}
sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i);
gc->dev[i].name = gc_names[config[i + 1]];
gc->dev[i].phys = gc->phys[i];
gc->dev[i].id.bustype = BUS_PARPORT;
gc->dev[i].id.vendor = 0x0001;
gc->dev[i].id.product = config[i + 1];
gc->dev[i].id.version = 0x0100;
}
parport_release(gc->pd);
if (!gc->pads[0]) {
parport_unregister_device(gc->pd);
kfree(gc);
return NULL;
}
for (i = 0; i < 5; i++)
if (gc->pads[0] & gc_status_bit[i]) {
input_register_device(gc->dev + i);
printk(KERN_INFO "input: %s on %s\n", gc->dev[i].name, gc->pd->port->name);
}
return gc;
}
int __init gc_init(void)
{
gc_base[0] = gc_probe(gc, gc_nargs);
gc_base[1] = gc_probe(gc_2, gc_nargs_2);
gc_base[2] = gc_probe(gc_3, gc_nargs_3);
if (gc_base[0] || gc_base[1] || gc_base[2])
return 0;
return -ENODEV;
}
void __exit gc_exit(void)
{
int i, j;
for (i = 0; i < 3; i++)
if (gc_base[i]) {
for (j = 0; j < 5; j++)
if (gc_base[i]->pads[0] & gc_status_bit[j])
input_unregister_device(gc_base[i]->dev + j);
parport_unregister_device(gc_base[i]->pd);
}
}
module_init(gc_init);
module_exit(gc_exit);

View File

@@ -0,0 +1,366 @@
/*
* $Id: gf2k.c,v 1.19 2002/01/22 20:27:43 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* Genius Flight 2000 joystick driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/gameport.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Genius Flight 2000 joystick driver");
MODULE_LICENSE("GPL");
#define GF2K_START 400 /* The time we wait for the first bit [400 us] */
#define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */
#define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */
#define GF2K_LENGTH 80 /* Max number of triplets in a packet */
#define GF2K_REFRESH HZ/50 /* Time between joystick polls [20 ms] */
/*
* Genius joystick ids ...
*/
#define GF2K_ID_G09 1
#define GF2K_ID_F30D 2
#define GF2K_ID_F30 3
#define GF2K_ID_F31D 4
#define GF2K_ID_F305 5
#define GF2K_ID_F23P 6
#define GF2K_ID_F31 7
#define GF2K_ID_MAX 7
static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 };
static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D",
"Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"};
static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 };
static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 };
static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 };
static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 };
static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 };
static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE };
static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 };
static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT };
static short gf2k_seq_reset[] = { 240, 340, 0 };
static short gf2k_seq_digital[] = { 590, 320, 860, 0 };
struct gf2k {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev;
int reads;
int bads;
int used;
unsigned char id;
unsigned char length;
char phys[32];
};
/*
* gf2k_read_packet() reads a Genius Flight2000 packet.
*/
static int gf2k_read_packet(struct gameport *gameport, int length, char *data)
{
unsigned char u, v;
int i;
unsigned int t, p;
unsigned long flags;
t = gameport_time(gameport, GF2K_START);
p = gameport_time(gameport, GF2K_STROBE);
i = 0;
local_irq_save(flags);
gameport_trigger(gameport);
v = gameport_read(gameport);
while (t > 0 && i < length) {
t--; u = v;
v = gameport_read(gameport);
if (v & ~u & 0x10) {
data[i++] = v >> 5;
t = p;
}
}
local_irq_restore(flags);
return i;
}
/*
* gf2k_trigger_seq() initializes a Genius Flight2000 joystick
* into digital mode.
*/
static void gf2k_trigger_seq(struct gameport *gameport, short *seq)
{
unsigned long flags;
int i, t;
local_irq_save(flags);
i = 0;
do {
gameport_trigger(gameport);
t = gameport_time(gameport, GF2K_TIMEOUT * 1000);
while ((gameport_read(gameport) & 1) && t) t--;
udelay(seq[i]);
} while (seq[++i]);
gameport_trigger(gameport);
local_irq_restore(flags);
}
/*
* js_sw_get_bits() composes bits from the triplet buffer into a __u64.
* Parameter 'pos' is bit number inside packet where to start at, 'num' is number
* of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
* is number of bits per triplet.
*/
#define GB(p,n,s) gf2k_get_bits(data, p, n, s)
static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift)
{
__u64 data = 0;
int i;
for (i = 0; i < num / 3 + 2; i++)
data |= buf[pos / 3 + i] << (i * 3);
data >>= pos % 3;
data &= (1 << num) - 1;
data <<= shift;
return data;
}
static void gf2k_read(struct gf2k *gf2k, unsigned char *data)
{
struct input_dev *dev = &gf2k->dev;
int i, t;
for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++)
input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9));
for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++)
input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9));
t = GB(40,4,0);
for (i = 0; i < gf2k_hats[gf2k->id]; i++)
input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]);
t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10);
for (i = 0; i < gf2k_joys[gf2k->id]; i++)
input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1);
for (i = 0; i < gf2k_pads[gf2k->id]; i++)
input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1);
input_sync(dev);
}
/*
* gf2k_timer() reads and analyzes Genius joystick data.
*/
static void gf2k_timer(unsigned long private)
{
struct gf2k *gf2k = (void *) private;
unsigned char data[GF2K_LENGTH];
gf2k->reads++;
if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id]) {
gf2k->bads++;
} else gf2k_read(gf2k, data);
mod_timer(&gf2k->timer, jiffies + GF2K_REFRESH);
}
static int gf2k_open(struct input_dev *dev)
{
struct gf2k *gf2k = dev->private;
if (!gf2k->used++)
mod_timer(&gf2k->timer, jiffies + GF2K_REFRESH);
return 0;
}
static void gf2k_close(struct input_dev *dev)
{
struct gf2k *gf2k = dev->private;
if (!--gf2k->used)
del_timer(&gf2k->timer);
}
/*
* gf2k_connect() probes for Genius id joysticks.
*/
static void gf2k_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct gf2k *gf2k;
unsigned char data[GF2K_LENGTH];
int i;
if (!(gf2k = kmalloc(sizeof(struct gf2k), GFP_KERNEL)))
return;
memset(gf2k, 0, sizeof(struct gf2k));
gameport->private = gf2k;
gf2k->gameport = gameport;
init_timer(&gf2k->timer);
gf2k->timer.data = (long) gf2k;
gf2k->timer.function = gf2k_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
gf2k_trigger_seq(gameport, gf2k_seq_reset);
msleep(GF2K_TIMEOUT);
gf2k_trigger_seq(gameport, gf2k_seq_digital);
msleep(GF2K_TIMEOUT);
if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12)
goto fail2;
if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5)))
goto fail2;
#ifdef RESET_WORKS
if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) ||
(gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5))))
goto fail2;
#else
gf2k->id = 6;
#endif
if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) {
printk(KERN_WARNING "gf2k.c: Not yet supported joystick on %s. [id: %d type:%s]\n",
gameport->phys, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]);
goto fail2;
}
sprintf(gf2k->phys, "%s/input0", gameport->phys);
gf2k->length = gf2k_lens[gf2k->id];
init_input_dev(&gf2k->dev);
gf2k->dev.private = gf2k;
gf2k->dev.open = gf2k_open;
gf2k->dev.close = gf2k_close;
gf2k->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
gf2k->dev.name = gf2k_names[gf2k->id];
gf2k->dev.phys = gf2k->phys;
gf2k->dev.id.bustype = BUS_GAMEPORT;
gf2k->dev.id.vendor = GAMEPORT_ID_VENDOR_GENIUS;
gf2k->dev.id.product = gf2k->id;
gf2k->dev.id.version = 0x0100;
for (i = 0; i < gf2k_axes[gf2k->id]; i++)
set_bit(gf2k_abs[i], gf2k->dev.absbit);
for (i = 0; i < gf2k_hats[gf2k->id]; i++) {
set_bit(ABS_HAT0X + i, gf2k->dev.absbit);
gf2k->dev.absmin[ABS_HAT0X + i] = -1;
gf2k->dev.absmax[ABS_HAT0X + i] = 1;
}
for (i = 0; i < gf2k_joys[gf2k->id]; i++)
set_bit(gf2k_btn_joy[i], gf2k->dev.keybit);
for (i = 0; i < gf2k_pads[gf2k->id]; i++)
set_bit(gf2k_btn_pad[i], gf2k->dev.keybit);
gf2k_read_packet(gameport, gf2k->length, data);
gf2k_read(gf2k, data);
for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 :
gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32;
gf2k->dev.absmin[gf2k_abs[i]] = 32;
gf2k->dev.absfuzz[gf2k_abs[i]] = 8;
gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0;
}
input_register_device(&gf2k->dev);
printk(KERN_INFO "input: %s on %s\n", gf2k_names[gf2k->id], gameport->phys);
return;
fail2: gameport_close(gameport);
fail1: kfree(gf2k);
}
static void gf2k_disconnect(struct gameport *gameport)
{
struct gf2k *gf2k = gameport->private;
input_unregister_device(&gf2k->dev);
gameport_close(gameport);
kfree(gf2k);
}
static struct gameport_dev gf2k_dev = {
.connect = gf2k_connect,
.disconnect = gf2k_disconnect,
};
int __init gf2k_init(void)
{
gameport_register_device(&gf2k_dev);
return 0;
}
void __exit gf2k_exit(void)
{
gameport_unregister_device(&gf2k_dev);
}
module_init(gf2k_init);
module_exit(gf2k_exit);

View File

@@ -0,0 +1,429 @@
/*
* $Id: grip.c,v 1.21 2002/01/22 20:27:57 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Gravis GrIP protocol joystick driver");
MODULE_LICENSE("GPL");
#define GRIP_MODE_GPP 1
#define GRIP_MODE_BD 2
#define GRIP_MODE_XT 3
#define GRIP_MODE_DC 4
#define GRIP_LENGTH_GPP 24
#define GRIP_STROBE_GPP 200 /* 200 us */
#define GRIP_LENGTH_XT 4
#define GRIP_STROBE_XT 64 /* 64 us */
#define GRIP_MAX_CHUNKS_XT 10
#define GRIP_MAX_BITS_XT 30
#define GRIP_REFRESH_TIME HZ/50 /* 20 ms */
struct grip {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev[2];
unsigned char mode[2];
int used;
int reads;
int bads;
char phys[2][32];
};
static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 };
static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 };
static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 };
static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 };
static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 };
static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 };
static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital",
"Gravis Xterminator Digital", "Gravis Xterminator DualControl" };
static int *grip_abs[] = { NULL, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc };
static int *grip_btn[] = { NULL, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc };
static char grip_anx[] = { 0, 0, 3, 5, 5 };
static char grip_cen[] = { 0, 0, 2, 2, 4 };
/*
* grip_gpp_read_packet() reads a Gravis GamePad Pro packet.
*/
static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data)
{
unsigned long flags;
unsigned char u, v;
unsigned int t;
int i;
int strobe = gameport_time(gameport, GRIP_STROBE_GPP);
data[0] = 0;
t = strobe;
i = 0;
local_irq_save(flags);
v = gameport_read(gameport) >> shift;
do {
t--;
u = v; v = (gameport_read(gameport) >> shift) & 3;
if (~v & u & 1) {
data[0] |= (v >> 1) << i++;
t = strobe;
}
} while (i < GRIP_LENGTH_GPP && t > 0);
local_irq_restore(flags);
if (i < GRIP_LENGTH_GPP) return -1;
for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++)
data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1);
return -(i == GRIP_LENGTH_GPP);
}
/*
* grip_xt_read_packet() reads a Gravis Xterminator packet.
*/
static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data)
{
unsigned int i, j, buf, crc;
unsigned char u, v, w;
unsigned long flags;
unsigned int t;
char status;
int strobe = gameport_time(gameport, GRIP_STROBE_XT);
data[0] = data[1] = data[2] = data[3] = 0;
status = buf = i = j = 0;
t = strobe;
local_irq_save(flags);
v = w = (gameport_read(gameport) >> shift) & 3;
do {
t--;
u = (gameport_read(gameport) >> shift) & 3;
if (u ^ v) {
if ((u ^ v) & 1) {
buf = (buf << 1) | (u >> 1);
t = strobe;
i++;
} else
if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) {
if (i == 20) {
crc = buf ^ (buf >> 7) ^ (buf >> 14);
if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) {
data[buf >> 18] = buf >> 4;
status |= 1 << (buf >> 18);
}
j++;
}
t = strobe;
buf = 0;
i = 0;
}
w = v;
v = u;
}
} while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0);
local_irq_restore(flags);
return -(status != 0xf);
}
/*
* grip_timer() repeatedly polls the joysticks and generates events.
*/
static void grip_timer(unsigned long private)
{
struct grip *grip = (void*) private;
unsigned int data[GRIP_LENGTH_XT];
struct input_dev *dev;
int i, j;
for (i = 0; i < 2; i++) {
dev = grip->dev + i;
grip->reads++;
switch (grip->mode[i]) {
case GRIP_MODE_GPP:
if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) {
grip->bads++;
break;
}
input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1));
input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1));
for (j = 0; j < 12; j++)
if (grip_btn_gpp[j])
input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1);
break;
case GRIP_MODE_BD:
if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
grip->bads++;
break;
}
input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f);
input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f));
input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1));
input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
for (j = 0; j < 5; j++)
input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1);
break;
case GRIP_MODE_XT:
if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
grip->bads++;
break;
}
input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f);
input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f));
input_report_abs(dev, ABS_BRAKE, (data[1] >> 2) & 0x3f);
input_report_abs(dev, ABS_GAS, (data[1] >> 8) & 0x3f);
input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1));
input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1));
input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1));
for (j = 0; j < 11; j++)
input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1);
break;
case GRIP_MODE_DC:
if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
grip->bads++;
break;
}
input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f);
input_report_abs(dev, ABS_Y, (data[0] >> 8) & 0x3f);
input_report_abs(dev, ABS_RX, (data[1] >> 2) & 0x3f);
input_report_abs(dev, ABS_RY, (data[1] >> 8) & 0x3f);
input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1));
input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
for (j = 0; j < 9; j++)
input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1);
break;
}
input_sync(dev);
}
mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME);
}
static int grip_open(struct input_dev *dev)
{
struct grip *grip = dev->private;
if (!grip->used++)
mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME);
return 0;
}
static void grip_close(struct input_dev *dev)
{
struct grip *grip = dev->private;
if (!--grip->used)
del_timer(&grip->timer);
}
static void grip_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct grip *grip;
unsigned int data[GRIP_LENGTH_XT];
int i, j, t;
if (!(grip = kmalloc(sizeof(struct grip), GFP_KERNEL)))
return;
memset(grip, 0, sizeof(struct grip));
gameport->private = grip;
grip->gameport = gameport;
init_timer(&grip->timer);
grip->timer.data = (long) grip;
grip->timer.function = grip_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
for (i = 0; i < 2; i++) {
if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) {
grip->mode[i] = GRIP_MODE_GPP;
continue;
}
if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) {
if (!(data[3] & 7)) {
grip->mode[i] = GRIP_MODE_BD;
continue;
}
if (!(data[2] & 0xf0)) {
grip->mode[i] = GRIP_MODE_XT;
continue;
}
grip->mode[i] = GRIP_MODE_DC;
continue;
}
}
if (!grip->mode[0] && !grip->mode[1])
goto fail2;
for (i = 0; i < 2; i++)
if (grip->mode[i]) {
sprintf(grip->phys[i], "%s/input%d", gameport->phys, i);
grip->dev[i].private = grip;
grip->dev[i].open = grip_open;
grip->dev[i].close = grip_close;
grip->dev[i].name = grip_name[grip->mode[i]];
grip->dev[i].phys = grip->phys[i];
grip->dev[i].id.bustype = BUS_GAMEPORT;
grip->dev[i].id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
grip->dev[i].id.product = grip->mode[i];
grip->dev[i].id.version = 0x0100;
grip->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) {
set_bit(t, grip->dev[i].absbit);
if (j < grip_cen[grip->mode[i]]) {
grip->dev[i].absmin[t] = 14;
grip->dev[i].absmax[t] = 52;
grip->dev[i].absfuzz[t] = 1;
grip->dev[i].absflat[t] = 2;
continue;
}
if (j < grip_anx[grip->mode[i]]) {
grip->dev[i].absmin[t] = 3;
grip->dev[i].absmax[t] = 57;
grip->dev[i].absfuzz[t] = 1;
continue;
}
grip->dev[i].absmin[t] = -1;
grip->dev[i].absmax[t] = 1;
}
for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++)
if (t > 0)
set_bit(t, grip->dev[i].keybit);
input_register_device(grip->dev + i);
printk(KERN_INFO "input: %s on %s\n",
grip_name[grip->mode[i]], gameport->phys);
}
return;
fail2: gameport_close(gameport);
fail1: kfree(grip);
}
static void grip_disconnect(struct gameport *gameport)
{
int i;
struct grip *grip = gameport->private;
for (i = 0; i < 2; i++)
if (grip->mode[i])
input_unregister_device(grip->dev + i);
gameport_close(gameport);
kfree(grip);
}
static struct gameport_dev grip_dev = {
.connect = grip_connect,
.disconnect = grip_disconnect,
};
int __init grip_init(void)
{
gameport_register_device(&grip_dev);
return 0;
}
void __exit grip_exit(void)
{
gameport_unregister_device(&grip_dev);
}
module_init(grip_init);
module_exit(grip_exit);

View File

@@ -0,0 +1,674 @@
/*
* $Id: grip_mp.c,v 1.9 2002/07/20 19:28:45 bonnland Exp $
*
* Driver for the Gravis Grip Multiport, a gamepad "hub" that
* connects up to four 9-pin digital gamepads/joysticks.
* Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5.
*
* Thanks to Chris Gassib for helpful advice.
*
* Copyright (c) 2002 Brian Bonnlander, Bill Soudan
* Copyright (c) 1998-2000 Vojtech Pavlik
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
MODULE_AUTHOR("Brian Bonnlander");
MODULE_DESCRIPTION("Gravis Grip Multiport driver");
MODULE_LICENSE("GPL");
#ifdef GRIP_DEBUG
#define dbg(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
#else
#define dbg(format, arg...) do {} while (0)
#endif
/*
* Grip multiport state
*/
struct grip_mp {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev[4];
int mode[4];
int registered[4];
int used;
int reads;
int bads;
/* individual gamepad states */
int buttons[4];
int xaxes[4];
int yaxes[4];
int dirty[4]; /* has the state been updated? */
};
/*
* Multiport packet interpretation
*/
#define PACKET_FULL 0x80000000 /* packet is full */
#define PACKET_IO_FAST 0x40000000 /* 3 bits per gameport read */
#define PACKET_IO_SLOW 0x20000000 /* 1 bit per gameport read */
#define PACKET_MP_MORE 0x04000000 /* multiport wants to send more */
#define PACKET_MP_DONE 0x02000000 /* multiport done sending */
/*
* Packet status code interpretation
*/
#define IO_GOT_PACKET 0x0100 /* Got a packet */
#define IO_MODE_FAST 0x0200 /* Used 3 data bits per gameport read */
#define IO_SLOT_CHANGE 0x0800 /* Multiport physical slot status changed */
#define IO_DONE 0x1000 /* Multiport is done sending packets */
#define IO_RETRY 0x4000 /* Try again later to get packet */
#define IO_RESET 0x8000 /* Force multiport to resend all packets */
/*
* Gamepad configuration data. Other 9-pin digital joystick devices
* may work with the multiport, so this may not be an exhaustive list!
* Commodore 64 joystick remains untested.
*/
#define GRIP_INIT_DELAY 2000 /* 2 ms */
#define GRIP_REFRESH_TIME HZ/50 /* 20 ms */
#define GRIP_MODE_NONE 0
#define GRIP_MODE_RESET 1
#define GRIP_MODE_GP 2
#define GRIP_MODE_C64 3
static int grip_btn_gp[] = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 };
static int grip_btn_c64[] = { BTN_JOYSTICK, -1 };
static int grip_abs_gp[] = { ABS_X, ABS_Y, -1 };
static int grip_abs_c64[] = { ABS_X, ABS_Y, -1 };
static int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 };
static int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 };
static char *grip_name[] = { NULL, NULL, "Gravis Grip Pad", "Commodore 64 Joystick" };
static const int init_seq[] = {
1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 };
/* Maps multiport directional values to X,Y axis values (each axis encoded in 3 bits) */
static int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 };
static void register_slot(int i, struct grip_mp *grip);
/*
* Returns whether an odd or even number of bits are on in pkt.
*/
static int bit_parity(u32 pkt)
{
int x = pkt ^ (pkt >> 16);
x ^= x >> 8;
x ^= x >> 4;
x ^= x >> 2;
x ^= x >> 1;
return x & 1;
}
/*
* Poll gameport; return true if all bits set in 'onbits' are on and
* all bits set in 'offbits' are off.
*/
static inline int poll_until(u8 onbits, u8 offbits, int u_sec, struct gameport* gp, u8 *data)
{
int i, nloops;
nloops = gameport_time(gp, u_sec);
for (i = 0; i < nloops; i++) {
*data = gameport_read(gp);
if ((*data & onbits) == onbits &&
(~(*data) & offbits) == offbits)
return 1;
}
dbg("gameport timed out after %d microseconds.\n", u_sec);
return 0;
}
/*
* Gets a 28-bit packet from the multiport.
*
* After getting a packet successfully, commands encoded by sendcode may
* be sent to the multiport.
*
* The multiport clock value is reflected in gameport bit B4.
*
* Returns a packet status code indicating whether packet is valid, the transfer
* mode, and any error conditions.
*
* sendflags: current I/O status
* sendcode: data to send to the multiport if sendflags is nonzero
*/
static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
{
u8 raw_data; /* raw data from gameport */
u8 data_mask; /* packet data bits from raw_data */
u32 pkt; /* packet temporary storage */
int bits_per_read; /* num packet bits per gameport read */
int portvals = 0; /* used for port value sanity check */
int i;
/* Gameport bits B0, B4, B5 should first be off, then B4 should come on. */
*packet = 0;
raw_data = gameport_read(gameport);
if (raw_data & 1)
return IO_RETRY;
for (i = 0; i < 64; i++) {
raw_data = gameport_read(gameport);
portvals |= 1 << ((raw_data >> 4) & 3); /* Demux B4, B5 */
}
if (portvals == 1) { /* B4, B5 off */
raw_data = gameport_read(gameport);
portvals = raw_data & 0xf0;
if (raw_data & 0x31)
return IO_RESET;
gameport_trigger(gameport);
if (!poll_until(0x10, 0, 308, gameport, &raw_data))
return IO_RESET;
} else
return IO_RETRY;
/* Determine packet transfer mode and prepare for packet construction. */
if (raw_data & 0x20) { /* 3 data bits/read */
portvals |= raw_data >> 4; /* Compare B4-B7 before & after trigger */
if (portvals != 0xb)
return 0;
data_mask = 7;
bits_per_read = 3;
pkt = (PACKET_FULL | PACKET_IO_FAST) >> 28;
} else { /* 1 data bit/read */
data_mask = 1;
bits_per_read = 1;
pkt = (PACKET_FULL | PACKET_IO_SLOW) >> 28;
}
/* Construct a packet. Final data bits must be zero. */
while (1) {
if (!poll_until(0, 0x10, 77, gameport, &raw_data))
return IO_RESET;
raw_data = (raw_data >> 5) & data_mask;
if (pkt & PACKET_FULL)
break;
pkt = (pkt << bits_per_read) | raw_data;
if (!poll_until(0x10, 0, 77, gameport, &raw_data))
return IO_RESET;
}
if (raw_data)
return IO_RESET;
/* If 3 bits/read used, drop from 30 bits to 28. */
if (bits_per_read == 3) {
pkt = (pkt & 0xffff0000) | ((pkt << 1) & 0xffff);
pkt = (pkt >> 2) | 0xf0000000;
}
if (bit_parity(pkt) == 1)
return IO_RESET;
/* Acknowledge packet receipt */
if (!poll_until(0x30, 0, 77, gameport, &raw_data))
return IO_RESET;
raw_data = gameport_read(gameport);
if (raw_data & 1)
return IO_RESET;
gameport_trigger(gameport);
if (!poll_until(0, 0x20, 77, gameport, &raw_data))
return IO_RESET;
/* Return if we just wanted the packet or multiport wants to send more */
*packet = pkt;
if ((sendflags == 0) || ((sendflags & IO_RETRY) && !(pkt & PACKET_MP_DONE)))
return IO_GOT_PACKET;
if (pkt & PACKET_MP_MORE)
return IO_GOT_PACKET | IO_RETRY;
/* Multiport is done sending packets and is ready to receive data */
if (!poll_until(0x20, 0, 77, gameport, &raw_data))
return IO_GOT_PACKET | IO_RESET;
raw_data = gameport_read(gameport);
if (raw_data & 1)
return IO_GOT_PACKET | IO_RESET;
/* Trigger gameport based on bits in sendcode */
gameport_trigger(gameport);
do {
if (!poll_until(0x20, 0x10, 116, gameport, &raw_data))
return IO_GOT_PACKET | IO_RESET;
if (!poll_until(0x30, 0, 193, gameport, &raw_data))
return IO_GOT_PACKET | IO_RESET;
if (raw_data & 1)
return IO_GOT_PACKET | IO_RESET;
if (sendcode & 1)
gameport_trigger(gameport);
sendcode >>= 1;
} while (sendcode);
return IO_GOT_PACKET | IO_MODE_FAST;
}
/*
* Disables and restores interrupts for mp_io(), which does the actual I/O.
*/
static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
{
int status;
unsigned long flags;
local_irq_save(flags);
status = mp_io(gameport, sendflags, sendcode, packet);
local_irq_restore(flags);
return status;
}
/*
* Puts multiport into digital mode. Multiport LED turns green.
*
* Returns true if a valid digital packet was received, false otherwise.
*/
static int dig_mode_start(struct gameport *gameport, u32 *packet)
{
int i, seq_len = sizeof(init_seq)/sizeof(int);
int flags, tries = 0, bads = 0;
for (i = 0; i < seq_len; i++) { /* Send magic sequence */
if (init_seq[i])
gameport_trigger(gameport);
udelay(GRIP_INIT_DELAY);
}
for (i = 0; i < 16; i++) /* Wait for multiport to settle */
udelay(GRIP_INIT_DELAY);
while (tries < 64 && bads < 8) { /* Reset multiport and try getting a packet */
flags = multiport_io(gameport, IO_RESET, 0x27, packet);
if (flags & IO_MODE_FAST)
return 1;
if (flags & IO_RETRY)
tries++;
else
bads++;
}
return 0;
}
/*
* Packet structure: B0-B15 => gamepad state
* B16-B20 => gamepad device type
* B21-B24 => multiport slot index (1-4)
*
* Known device types: 0x1f (grip pad), 0x0 (no device). Others may exist.
*
* Returns the packet status.
*/
static int get_and_decode_packet(struct grip_mp *grip, int flags)
{
u32 packet;
int joytype = 0;
int slot = 0;
/* Get a packet and check for validity */
flags &= IO_RESET | IO_RETRY;
flags = multiport_io(grip->gameport, flags, 0, &packet);
grip->reads++;
if (packet & PACKET_MP_DONE)
flags |= IO_DONE;
if (flags && !(flags & IO_GOT_PACKET)) {
grip->bads++;
return flags;
}
/* Ignore non-gamepad packets, e.g. multiport hardware version */
slot = ((packet >> 21) & 0xf) - 1;
if ((slot < 0) || (slot > 3))
return flags;
/*
* Handle "reset" packets, which occur at startup, and when gamepads
* are removed or plugged in. May contain configuration of a new gamepad.
*/
joytype = (packet >> 16) & 0x1f;
if (!joytype) {
if (grip->registered[slot]) {
printk(KERN_INFO "grip_mp: removing %s, slot %d\n",
grip_name[grip->mode[slot]], slot);
input_unregister_device(grip->dev + slot);
grip->registered[slot] = 0;
}
dbg("Reset: grip multiport slot %d\n", slot);
grip->mode[slot] = GRIP_MODE_RESET;
flags |= IO_SLOT_CHANGE;
return flags;
}
/* Interpret a grip pad packet */
if (joytype == 0x1f) {
int dir = (packet >> 8) & 0xf; /* eight way directional value */
grip->buttons[slot] = (~packet) & 0xff;
grip->yaxes[slot] = ((axis_map[dir] >> 2) & 3) - 1;
grip->xaxes[slot] = (axis_map[dir] & 3) - 1;
grip->dirty[slot] = 1;
if (grip->mode[slot] == GRIP_MODE_RESET)
flags |= IO_SLOT_CHANGE;
grip->mode[slot] = GRIP_MODE_GP;
if (!grip->registered[slot]) {
dbg("New Grip pad in multiport slot %d.\n", slot);
register_slot(slot, grip);
}
return flags;
}
/* Handle non-grip device codes. For now, just print diagnostics. */
{
static int strange_code = 0;
if (strange_code != joytype) {
printk(KERN_INFO "Possible non-grip pad/joystick detected.\n");
printk(KERN_INFO "Got joy type 0x%x and packet 0x%x.\n", joytype, packet);
strange_code = joytype;
}
}
return flags;
}
/*
* Returns true if all multiport slot states appear valid.
*/
static int slots_valid(struct grip_mp *grip)
{
int flags, slot, invalid = 0, active = 0;
flags = get_and_decode_packet(grip, 0);
if (!(flags & IO_GOT_PACKET))
return 0;
for (slot = 0; slot < 4; slot++) {
if (grip->mode[slot] == GRIP_MODE_RESET)
invalid = 1;
if (grip->mode[slot] != GRIP_MODE_NONE)
active = 1;
}
/* Return true if no active slot but multiport sent all its data */
if (!active)
return (flags & IO_DONE) ? 1 : 0;
/* Return false if invalid device code received */
return invalid ? 0 : 1;
}
/*
* Returns whether the multiport was placed into digital mode and
* able to communicate its state successfully.
*/
static int multiport_init(struct grip_mp *grip)
{
int dig_mode, initialized = 0, tries = 0;
u32 packet;
dig_mode = dig_mode_start(grip->gameport, &packet);
while (!dig_mode && tries < 4) {
dig_mode = dig_mode_start(grip->gameport, &packet);
tries++;
}
if (dig_mode)
dbg("multiport_init(): digital mode achieved.\n");
else {
dbg("multiport_init(): unable to achieve digital mode.\n");
return 0;
}
/* Get packets, store multiport state, and check state's validity */
for (tries = 0; tries < 4096; tries++) {
if ( slots_valid(grip) ) {
initialized = 1;
break;
}
}
dbg("multiport_init(): initialized == %d\n", initialized);
return initialized;
}
/*
* Reports joystick state to the linux input layer.
*/
static void report_slot(struct grip_mp *grip, int slot)
{
struct input_dev *dev = &(grip->dev[slot]);
int i, buttons = grip->buttons[slot];
/* Store button states with linux input driver */
for (i = 0; i < 8; i++)
input_report_key(dev, grip_btn_gp[i], (buttons >> i) & 1);
/* Store axis states with linux driver */
input_report_abs(dev, ABS_X, grip->xaxes[slot]);
input_report_abs(dev, ABS_Y, grip->yaxes[slot]);
/* Tell the receiver of the events to process them */
input_sync(dev);
grip->dirty[slot] = 0;
}
/*
* Get the multiport state.
*/
static void get_and_report_mp_state(struct grip_mp *grip)
{
int i, npkts, flags;
for (npkts = 0; npkts < 4; npkts++) {
flags = IO_RETRY;
for (i = 0; i < 32; i++) {
flags = get_and_decode_packet(grip, flags);
if ((flags & IO_GOT_PACKET) || !(flags & IO_RETRY))
break;
}
if (flags & IO_DONE)
break;
}
for (i = 0; i < 4; i++)
if (grip->dirty[i])
report_slot(grip, i);
}
/*
* Called when a joystick device file is opened
*/
static int grip_open(struct input_dev *dev)
{
struct grip_mp *grip = dev->private;
if (!grip->used++)
mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME);
return 0;
}
/*
* Called when a joystick device file is closed
*/
static void grip_close(struct input_dev *dev)
{
struct grip_mp *grip = dev->private;
if (!--grip->used)
del_timer(&grip->timer);
}
/*
* Tell the linux input layer about a newly plugged-in gamepad.
*/
static void register_slot(int slot, struct grip_mp *grip)
{
int j, t;
grip->dev[slot].private = grip;
grip->dev[slot].open = grip_open;
grip->dev[slot].close = grip_close;
grip->dev[slot].name = grip_name[grip->mode[slot]];
grip->dev[slot].id.bustype = BUS_GAMEPORT;
grip->dev[slot].id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
grip->dev[slot].id.product = 0x0100 + grip->mode[slot];
grip->dev[slot].id.version = 0x0100;
grip->dev[slot].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (j = 0; (t = grip_abs[grip->mode[slot]][j]) >= 0; j++) {
set_bit(t, grip->dev[slot].absbit);
grip->dev[slot].absmin[t] = -1;
grip->dev[slot].absmax[t] = 1;
}
for (j = 0; (t = grip_btn[grip->mode[slot]][j]) >= 0; j++)
if (t > 0)
set_bit(t, grip->dev[slot].keybit);
input_register_device(grip->dev + slot);
grip->registered[slot] = 1;
if (grip->dirty[slot]) /* report initial state, if any */
report_slot(grip, slot);
printk(KERN_INFO "grip_mp: added %s, slot %d\n",
grip_name[grip->mode[slot]], slot);
}
/*
* Repeatedly polls the multiport and generates events.
*/
static void grip_timer(unsigned long private)
{
struct grip_mp *grip = (void*) private;
get_and_report_mp_state(grip);
mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME);
}
static void grip_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct grip_mp *grip;
if (!(grip = kmalloc(sizeof(struct grip_mp), GFP_KERNEL)))
return;
memset(grip, 0, sizeof(struct grip_mp));
gameport->private = grip;
grip->gameport = gameport;
init_timer(&grip->timer);
grip->timer.data = (long) grip;
grip->timer.function = grip_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
if (!multiport_init(grip))
goto fail2;
if (!grip->mode[0] && !grip->mode[1] && /* nothing plugged in */
!grip->mode[2] && !grip->mode[3])
goto fail2;
return;
fail2: gameport_close(gameport);
fail1: kfree(grip);
}
static void grip_disconnect(struct gameport *gameport)
{
int i;
struct grip_mp *grip = gameport->private;
for (i = 0; i < 4; i++)
if (grip->registered[i])
input_unregister_device(grip->dev + i);
gameport_close(gameport);
kfree(grip);
}
static struct gameport_dev grip_dev = {
.connect = grip_connect,
.disconnect = grip_disconnect,
};
static int grip_init(void)
{
gameport_register_device(&grip_dev);
return 0;
}
static void grip_exit(void)
{
gameport_unregister_device(&grip_dev);
}
module_init(grip_init);
module_exit(grip_exit);

View File

@@ -0,0 +1,286 @@
/*
* $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $
*
* Copyright (c) 2001 Vojtech Pavlik
*/
/*
* Guillemot Digital Interface Protocol driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Guillemot Digital joystick driver");
MODULE_LICENSE("GPL");
#define GUILLEMOT_MAX_START 600 /* 600 us */
#define GUILLEMOT_MAX_STROBE 60 /* 60 us */
#define GUILLEMOT_MAX_LENGTH 17 /* 17 bytes */
#define GUILLEMOT_REFRESH_TIME HZ/50 /* 20 ms */
static short guillemot_abs_pad[] =
{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, -1 };
static short guillemot_btn_pad[] =
{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_MODE, BTN_SELECT, -1 };
static struct {
int x;
int y;
} guillemot_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
struct guillemot_type {
unsigned char id;
short *abs;
short *btn;
int hat;
char *name;
};
struct guillemot {
struct gameport *gameport;
struct input_dev dev;
struct timer_list timer;
int used;
int bads;
int reads;
struct guillemot_type *type;
unsigned char length;
char phys[32];
};
static struct guillemot_type guillemot_type[] = {
{ 0x00, guillemot_abs_pad, guillemot_btn_pad, 1, "Guillemot Pad" },
{ 0 }};
/*
* guillemot_read_packet() reads Guillemot joystick data.
*/
static int guillemot_read_packet(struct gameport *gameport, u8 *data)
{
unsigned long flags;
unsigned char u, v;
unsigned int t, s;
int i;
for (i = 0; i < GUILLEMOT_MAX_LENGTH; i++)
data[i] = 0;
i = 0;
t = gameport_time(gameport, GUILLEMOT_MAX_START);
s = gameport_time(gameport, GUILLEMOT_MAX_STROBE);
local_irq_save(flags);
gameport_trigger(gameport);
v = gameport_read(gameport);
while (t > 0 && i < GUILLEMOT_MAX_LENGTH * 8) {
t--;
u = v; v = gameport_read(gameport);
if (v & ~u & 0x10) {
data[i >> 3] |= ((v >> 5) & 1) << (i & 7);
i++;
t = s;
}
}
local_irq_restore(flags);
return i;
}
/*
* guillemot_timer() reads and analyzes Guillemot joystick data.
*/
static void guillemot_timer(unsigned long private)
{
struct guillemot *guillemot = (struct guillemot *) private;
struct input_dev *dev = &guillemot->dev;
u8 data[GUILLEMOT_MAX_LENGTH];
int i;
guillemot->reads++;
if (guillemot_read_packet(guillemot->gameport, data) != GUILLEMOT_MAX_LENGTH * 8 ||
data[0] != 0x55 || data[16] != 0xaa) {
guillemot->bads++;
} else {
for (i = 0; i < 6 && guillemot->type->abs[i] >= 0; i++)
input_report_abs(dev, guillemot->type->abs[i], data[i + 5]);
if (guillemot->type->hat) {
input_report_abs(dev, ABS_HAT0X, guillemot_hat_to_axis[data[4] >> 4].x);
input_report_abs(dev, ABS_HAT0Y, guillemot_hat_to_axis[data[4] >> 4].y);
}
for (i = 0; i < 16 && guillemot->type->btn[i] >= 0; i++)
input_report_key(dev, guillemot->type->btn[i], (data[2 + (i >> 3)] >> (i & 7)) & 1);
}
input_sync(dev);
mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME);
}
/*
* guillemot_open() is a callback from the input open routine.
*/
static int guillemot_open(struct input_dev *dev)
{
struct guillemot *guillemot = dev->private;
if (!guillemot->used++)
mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME);
return 0;
}
/*
* guillemot_close() is a callback from the input close routine.
*/
static void guillemot_close(struct input_dev *dev)
{
struct guillemot *guillemot = dev->private;
if (!--guillemot->used)
del_timer(&guillemot->timer);
}
/*
* guillemot_connect() probes for Guillemot joysticks.
*/
static void guillemot_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct guillemot *guillemot;
u8 data[GUILLEMOT_MAX_LENGTH];
int i, t;
if (!(guillemot = kmalloc(sizeof(struct guillemot), GFP_KERNEL)))
return;
memset(guillemot, 0, sizeof(struct guillemot));
gameport->private = guillemot;
guillemot->gameport = gameport;
init_timer(&guillemot->timer);
guillemot->timer.data = (long) guillemot;
guillemot->timer.function = guillemot_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
i = guillemot_read_packet(gameport, data);
if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa)
goto fail2;
for (i = 0; guillemot_type[i].name; i++)
if (guillemot_type[i].id == data[11])
break;
if (!guillemot_type[i].name) {
printk(KERN_WARNING "guillemot.c: Unknown joystick on %s. [ %02x%02x:%04x, ver %d.%02d ]\n",
gameport->phys, data[12], data[13], data[11], data[14], data[15]);
goto fail2;
}
sprintf(guillemot->phys, "%s/input0", gameport->phys);
guillemot->type = guillemot_type + i;
guillemot->dev.private = guillemot;
guillemot->dev.open = guillemot_open;
guillemot->dev.close = guillemot_close;
guillemot->dev.name = guillemot_type[i].name;
guillemot->dev.phys = guillemot->phys;
guillemot->dev.id.bustype = BUS_GAMEPORT;
guillemot->dev.id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
guillemot->dev.id.product = guillemot_type[i].id;
guillemot->dev.id.version = (int)data[14] << 8 | data[15];
guillemot->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++) {
set_bit(t, guillemot->dev.absbit);
guillemot->dev.absmin[t] = 0;
guillemot->dev.absmax[t] = 255;
}
if (guillemot->type->hat)
for (i = 0; i < 2; i++) {
t = ABS_HAT0X + i;
set_bit(t, guillemot->dev.absbit);
guillemot->dev.absmin[t] = -1;
guillemot->dev.absmax[t] = 1;
}
for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
set_bit(t, guillemot->dev.keybit);
input_register_device(&guillemot->dev);
printk(KERN_INFO "input: %s ver %d.%02d on %s\n",
guillemot->type->name, data[14], data[15], gameport->phys);
return;
fail2: gameport_close(gameport);
fail1: kfree(guillemot);
}
static void guillemot_disconnect(struct gameport *gameport)
{
struct guillemot *guillemot = gameport->private;
printk(KERN_INFO "guillemot.c: Failed %d reads out of %d on %s\n", guillemot->reads, guillemot->bads, guillemot->phys);
input_unregister_device(&guillemot->dev);
gameport_close(gameport);
kfree(guillemot);
}
static struct gameport_dev guillemot_dev = {
.connect = guillemot_connect,
.disconnect = guillemot_disconnect,
};
int __init guillemot_init(void)
{
gameport_register_device(&guillemot_dev);
return 0;
}
void __exit guillemot_exit(void)
{
gameport_unregister_device(&guillemot_dev);
}
module_init(guillemot_init);
module_exit(guillemot_exit);

View File

@@ -0,0 +1,31 @@
#
# I-Force driver configuration
#
config JOYSTICK_IFORCE
tristate "I-Force devices"
depends on INPUT && INPUT_JOYSTICK
help
Say Y here if you have an I-Force joystick or steering wheel
You also must choose at least one of the two options below.
To compile this driver as a module, choose M here: the
module will be called iforce.
config JOYSTICK_IFORCE_USB
bool "I-Force USB joysticks and wheels"
depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB
help
Say Y here if you have an I-Force joystick or steering wheel
connected to your USB port.
config JOYSTICK_IFORCE_232
bool "I-Force Serial joysticks and wheels"
depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO
help
Say Y here if you have an I-Force joystick or steering wheel
connected to your serial (COM) port.
You will need an additional utility called inputattach, see
Documentation/input/joystick.txt and ff.txt.

View File

@@ -0,0 +1,20 @@
#
# Makefile for the I-Force driver
#
# By Johann Deneux <deneux@ifrance.com>
#
# Goal definition
iforce-objs := iforce-ff.o iforce-main.o iforce-packets.o
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
iforce-objs += iforce-serio.o
endif
ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
iforce-objs += iforce-usb.o
endif
EXTRA_CFLAGS = -Werror-implicit-function-declaration

View File

@@ -0,0 +1,543 @@
/*
* $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
/*
* Set the magnitude of a constant force effect
* Return error code
*
* Note: caller must ensure exclusive access to device
*/
static int make_magnitude_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc, __s16 level)
{
unsigned char data[3];
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = HIFIX80(level);
iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);
return 0;
}
/*
* Upload the component of an effect dealing with the period, phase and magnitude
*/
static int make_period_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc,
__s16 magnitude, __s16 offset, u16 period, u16 phase)
{
unsigned char data[7];
period = TIME_SCALE(period);
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = HIFIX80(magnitude);
data[3] = HIFIX80(offset);
data[4] = HI(phase);
data[5] = LO(period);
data[6] = HI(period);
iforce_send_packet(iforce, FF_CMD_PERIOD, data);
return 0;
}
/*
* Uploads the part of an effect setting the envelope of the force
*/
static int make_envelope_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc,
u16 attack_duration, __s16 initial_level,
u16 fade_duration, __s16 final_level)
{
unsigned char data[8];
attack_duration = TIME_SCALE(attack_duration);
fade_duration = TIME_SCALE(fade_duration);
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = LO(attack_duration);
data[3] = HI(attack_duration);
data[4] = HI(initial_level);
data[5] = LO(fade_duration);
data[6] = HI(fade_duration);
data[7] = HI(final_level);
iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
return 0;
}
/*
* Component of spring, friction, inertia... effects
*/
static int make_condition_modifier(struct iforce* iforce,
struct resource* mod_chunk, int no_alloc,
__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
{
unsigned char data[10];
if (!no_alloc) {
down(&iforce->mem_mutex);
if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
up(&iforce->mem_mutex);
return -ENOMEM;
}
up(&iforce->mem_mutex);
}
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */
center = (500*center)>>15;
data[4] = LO(center);
data[5] = HI(center);
db = (1000*db)>>16;
data[6] = LO(db);
data[7] = HI(db);
data[8] = (100*rsat)>>16;
data[9] = (100*lsat)>>16;
iforce_send_packet(iforce, FF_CMD_CONDITION, data);
iforce_dump_packet("condition", FF_CMD_CONDITION, data);
return 0;
}
static unsigned char find_button(struct iforce *iforce, signed short button)
{
int i;
for (i = 1; iforce->type->btn[i] >= 0; i++)
if (iforce->type->btn[i] == button)
return i + 1;
return 0;
}
/*
* Analyse the changes in an effect, and tell if we need to send an condition
* parameter packet
*/
static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
{
int id = new->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
int ret=0;
int i;
if (new->type != FF_SPRING && new->type != FF_FRICTION) {
printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n");
return FALSE;
}
for(i=0; i<2; i++) {
ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
|| old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
|| old->u.condition[i].deadband != new->u.condition[i].deadband
|| old->u.condition[i].center != new->u.condition[i].center;
}
return ret;
}
/*
* Analyse the changes in an effect, and tell if we need to send a magnitude
* parameter packet
*/
static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect)
{
int id = effect->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (effect->type != FF_CONSTANT) {
printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
return FALSE;
}
return (old->u.constant.level != effect->u.constant.level);
}
/*
* Analyse the changes in an effect, and tell if we need to send an envelope
* parameter packet
*/
static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect)
{
int id = effect->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
switch (effect->type) {
case FF_CONSTANT:
if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
return TRUE;
break;
case FF_PERIODIC:
if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
return TRUE;
break;
default:
printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
}
return FALSE;
}
/*
* Analyse the changes in an effect, and tell if we need to send a periodic
* parameter effect
*/
static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
{
int id = new->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (new->type != FF_PERIODIC) {
printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n");
return FALSE;
}
return (old->u.periodic.period != new->u.periodic.period
|| old->u.periodic.magnitude != new->u.periodic.magnitude
|| old->u.periodic.offset != new->u.periodic.offset
|| old->u.periodic.phase != new->u.periodic.phase);
}
/*
* Analyse the changes in an effect, and tell if we need to send an effect
* packet
*/
static int need_core(struct iforce* iforce, struct ff_effect* new)
{
int id = new->id;
struct ff_effect* old = &iforce->core_effects[id].effect;
if (old->direction != new->direction
|| old->trigger.button != new->trigger.button
|| old->trigger.interval != new->trigger.interval
|| old->replay.length != new->replay.length
|| old->replay.delay != new->replay.delay)
return TRUE;
return FALSE;
}
/*
* Send the part common to all effects to the device
*/
static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
u16 interval, u16 direction)
{
unsigned char data[14];
duration = TIME_SCALE(duration);
delay = TIME_SCALE(delay);
interval = TIME_SCALE(interval);
data[0] = LO(id);
data[1] = effect_type;
data[2] = LO(axes) | find_button(iforce, button);
data[3] = LO(duration);
data[4] = HI(duration);
data[5] = HI(direction);
data[6] = LO(interval);
data[7] = HI(interval);
data[8] = LO(mod_id1);
data[9] = HI(mod_id1);
data[10] = LO(mod_id2);
data[11] = HI(mod_id2);
data[12] = LO(delay);
data[13] = HI(delay);
/* Stop effect */
/* iforce_control_playback(iforce, id, 0);*/
iforce_send_packet(iforce, FF_CMD_EFFECT, data);
/* If needed, restart effect */
if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
iforce_control_playback(iforce, id, 1);
}
return 0;
}
/*
* Upload a periodic effect to the device
* See also iforce_upload_constant.
*/
int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update)
{
u8 wave_code;
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
int param1_err = 1;
int param2_err = 1;
int core_err = 0;
if (!is_update || need_period_modifier(iforce, effect)) {
param1_err = make_period_modifier(iforce, mod1_chunk,
is_update,
effect->u.periodic.magnitude, effect->u.periodic.offset,
effect->u.periodic.period, effect->u.periodic.phase);
if (param1_err) return param1_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
if (!is_update || need_envelope_modifier(iforce, effect)) {
param2_err = make_envelope_modifier(iforce, mod2_chunk,
is_update,
effect->u.periodic.envelope.attack_length,
effect->u.periodic.envelope.attack_level,
effect->u.periodic.envelope.fade_length,
effect->u.periodic.envelope.fade_level);
if (param2_err) return param2_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
switch (effect->u.periodic.waveform) {
case FF_SQUARE: wave_code = 0x20; break;
case FF_TRIANGLE: wave_code = 0x21; break;
case FF_SINE: wave_code = 0x22; break;
case FF_SAW_UP: wave_code = 0x23; break;
case FF_SAW_DOWN: wave_code = 0x24; break;
default: wave_code = 0x20; break;
}
if (!is_update || need_core(iforce, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start,
mod2_chunk->start,
wave_code,
0x20,
effect->replay.length,
effect->replay.delay,
effect->trigger.button,
effect->trigger.interval,
effect->direction);
}
/* If one of the parameter creation failed, we already returned an
* error code.
* If the core creation failed, we return its error code.
* Else: if one parameter at least was created, we return 0
* else we return 1;
*/
return core_err < 0 ? core_err : (param1_err && param2_err);
}
/*
* Upload a constant force effect
* Return value:
* <0 Error code
* 0 Ok, effect created or updated
* 1 effect did not change since last upload, and no packet was therefore sent
*/
int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update)
{
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
int param1_err = 1;
int param2_err = 1;
int core_err = 0;
if (!is_update || need_magnitude_modifier(iforce, effect)) {
param1_err = make_magnitude_modifier(iforce, mod1_chunk,
is_update,
effect->u.constant.level);
if (param1_err) return param1_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
if (!is_update || need_envelope_modifier(iforce, effect)) {
param2_err = make_envelope_modifier(iforce, mod2_chunk,
is_update,
effect->u.constant.envelope.attack_length,
effect->u.constant.envelope.attack_level,
effect->u.constant.envelope.fade_length,
effect->u.constant.envelope.fade_level);
if (param2_err) return param2_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
if (!is_update || need_core(iforce, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start,
mod2_chunk->start,
0x00,
0x20,
effect->replay.length,
effect->replay.delay,
effect->trigger.button,
effect->trigger.interval,
effect->direction);
}
/* If one of the parameter creation failed, we already returned an
* error code.
* If the core creation failed, we return its error code.
* Else: if one parameter at least was created, we return 0
* else we return 1;
*/
return core_err < 0 ? core_err : (param1_err && param2_err);
}
/*
* Upload an condition effect. Those are for example friction, inertia, springs...
*/
int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update)
{
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
struct resource* mod1_chunk = &(core_effect->mod1_chunk);
struct resource* mod2_chunk = &(core_effect->mod2_chunk);
u8 type;
int param_err = 1;
int core_err = 0;
switch (effect->type) {
case FF_SPRING: type = 0x40; break;
case FF_DAMPER: type = 0x41; break;
default: return -1;
}
if (!is_update || need_condition_modifier(iforce, effect)) {
param_err = make_condition_modifier(iforce, mod1_chunk,
is_update,
effect->u.condition[0].right_saturation,
effect->u.condition[0].left_saturation,
effect->u.condition[0].right_coeff,
effect->u.condition[0].left_coeff,
effect->u.condition[0].deadband,
effect->u.condition[0].center);
if (param_err) return param_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
param_err = make_condition_modifier(iforce, mod2_chunk,
is_update,
effect->u.condition[1].right_saturation,
effect->u.condition[1].left_saturation,
effect->u.condition[1].right_coeff,
effect->u.condition[1].left_coeff,
effect->u.condition[1].deadband,
effect->u.condition[1].center);
if (param_err) return param_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
if (!is_update || need_core(iforce, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start, mod2_chunk->start,
type, 0xc0,
effect->replay.length, effect->replay.delay,
effect->trigger.button, effect->trigger.interval,
effect->direction);
}
/* If the parameter creation failed, we already returned an
* error code.
* If the core creation failed, we return its error code.
* Else: if a parameter was created, we return 0
* else we return 1;
*/
return core_err < 0 ? core_err : param_err;
}

View File

@@ -0,0 +1,543 @@
/*
* $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>");
MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
MODULE_LICENSE("GPL");
static signed short btn_joystick[] =
{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
static signed short btn_avb_pegasus[] =
{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
static signed short btn_wheel[] =
{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
static signed short btn_avb_tw[] =
{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
static signed short btn_avb_wheel[] =
{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3,
BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 };
static signed short abs_joystick[] =
{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
static signed short abs_avb_pegasus[] =
{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y,
ABS_HAT1X, ABS_HAT1Y, -1 };
static signed short abs_wheel[] =
{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
static signed short ff_iforce[] =
{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER,
FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN,
FF_AUTOCENTER, -1 };
static struct iforce_device iforce_device[] = {
{ 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce },
{ 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce },
{ 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce },
{ 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_avb_pegasus, abs_avb_pegasus, ff_iforce },
{ 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_avb_wheel, abs_wheel, ff_iforce },
{ 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce }, //?
{ 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //?
{ 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //?
{ 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
{ 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
};
static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct iforce* iforce = (struct iforce*)(dev->private);
unsigned char data[3];
if (type != EV_FF)
return -1;
switch (code) {
case FF_GAIN:
data[0] = value >> 9;
iforce_send_packet(iforce, FF_CMD_GAIN, data);
return 0;
case FF_AUTOCENTER:
data[0] = 0x03;
data[1] = value >> 9;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
data[0] = 0x04;
data[1] = 0x01;
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
return 0;
default: /* Play or stop an effect */
if (!CHECK_OWNERSHIP(code, iforce)) {
return -1;
}
if (value > 0) {
set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
}
else {
clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
}
iforce_control_playback(iforce, code, value);
return 0;
}
return -1;
}
/*
* Function called when an ioctl is performed on the event dev entry.
* It uploads an effect to the device
*/
static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
{
struct iforce* iforce = (struct iforce*)(dev->private);
int id;
int ret;
int is_update;
/* Check this effect type is supported by this device */
if (!test_bit(effect->type, iforce->dev.ffbit))
return -EINVAL;
/*
* If we want to create a new effect, get a free id
*/
if (effect->id == -1) {
for (id=0; id < FF_EFFECTS_MAX; ++id)
if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
return -ENOMEM;
effect->id = id;
iforce->core_effects[id].owner = current->pid;
iforce->core_effects[id].flags[0] = (1<<FF_CORE_IS_USED); /* Only IS_USED bit must be set */
is_update = FALSE;
}
else {
/* We want to update an effect */
if (!CHECK_OWNERSHIP(effect->id, iforce)) return -EACCES;
/* Parameter type cannot be updated */
if (effect->type != iforce->core_effects[effect->id].effect.type)
return -EINVAL;
/* Check the effect is not already being updated */
if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) {
return -EAGAIN;
}
is_update = TRUE;
}
/*
* Upload the effect
*/
switch (effect->type) {
case FF_PERIODIC:
ret = iforce_upload_periodic(iforce, effect, is_update);
break;
case FF_CONSTANT:
ret = iforce_upload_constant(iforce, effect, is_update);
break;
case FF_SPRING:
case FF_DAMPER:
ret = iforce_upload_condition(iforce, effect, is_update);
break;
default:
return -EINVAL;
}
if (ret == 0) {
/* A packet was sent, forbid new updates until we are notified
* that the packet was updated
*/
set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
}
iforce->core_effects[effect->id].effect = *effect;
return ret;
}
/*
* Erases an effect: it frees the effect id and mark as unused the memory
* allocated for the parameters
*/
static int iforce_erase_effect(struct input_dev *dev, int effect_id)
{
struct iforce* iforce = (struct iforce*)(dev->private);
int err = 0;
struct iforce_core_effect* core_effect;
/* Check who is trying to erase this effect */
if (iforce->core_effects[effect_id].owner != current->pid) {
printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner);
return -EACCES;
}
if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
return -EINVAL;
core_effect = iforce->core_effects + effect_id;
if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
/*TODO: remember to change that if more FF_MOD* bits are added */
core_effect->flags[0] = 0;
return err;
}
static int iforce_open(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
iforce->irq->dev = iforce->usbdev;
if (usb_submit_urb(iforce->irq, GFP_KERNEL))
return -EIO;
break;
#endif
}
/* Enable force feedback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
return 0;
}
static int iforce_flush(struct input_dev *dev, struct file *file)
{
struct iforce *iforce = dev->private;
int i;
/* Erase all effects this process owns */
for (i=0; i<dev->ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
current->pid == iforce->core_effects[i].owner) {
/* Stop effect */
input_report_ff(dev, i, 0);
/* Free ressources assigned to effect */
if (iforce_erase_effect(dev, i)) {
printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i);
}
}
}
return 0;
}
static void iforce_release(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
int i;
/* Check: no effect should be present in memory */
for (i=0; i<dev->ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
break;
}
if (i<dev->ff_effects_max) {
printk(KERN_WARNING "iforce_release: Device still owns effects\n");
}
/* Disable force feedback playback */
iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
usb_unlink_urb(iforce->irq);
/* The device was unplugged before the file
* was released */
if (iforce->usbdev == NULL) {
iforce_delete_device(iforce);
kfree(iforce);
}
break;
#endif
}
}
void iforce_delete_device(struct iforce *iforce)
{
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
iforce_usb_delete(iforce);
break;
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_232
case IFORCE_232:
//TODO: Wait for the last packets to be sent
break;
#endif
}
}
int iforce_init_device(struct iforce *iforce)
{
unsigned char c[] = "CEOV";
int i;
init_waitqueue_head(&iforce->wait);
spin_lock_init(&iforce->xmit_lock);
init_MUTEX(&iforce->mem_mutex);
iforce->xmit.buf = iforce->xmit_data;
iforce->dev.ff_effects_max = 10;
/*
* Input device fields.
*/
iforce->dev.id.bustype = BUS_USB;
iforce->dev.private = iforce;
iforce->dev.name = "Unknown I-Force device";
iforce->dev.open = iforce_open;
iforce->dev.close = iforce_release;
iforce->dev.flush = iforce_flush;
iforce->dev.event = iforce_input_event;
iforce->dev.upload_effect = iforce_upload_effect;
iforce->dev.erase_effect = iforce_erase_effect;
/*
* On-device memory allocation.
*/
iforce->device_memory.name = "I-Force device effect memory";
iforce->device_memory.start = 0;
iforce->device_memory.end = 200;
iforce->device_memory.flags = IORESOURCE_MEM;
iforce->device_memory.parent = NULL;
iforce->device_memory.child = NULL;
iforce->device_memory.sibling = NULL;
/*
* Wait until device ready - until it sends its first response.
*/
for (i = 0; i < 20; i++)
if (!iforce_get_id_packet(iforce, "O"))
break;
if (i == 20) { /* 5 seconds */
printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n");
return -1;
}
/*
* Get device info.
*/
if (!iforce_get_id_packet(iforce, "M"))
iforce->dev.id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
else
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n");
if (!iforce_get_id_packet(iforce, "P"))
iforce->dev.id.product = (iforce->edata[2] << 8) | iforce->edata[1];
else
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n");
if (!iforce_get_id_packet(iforce, "B"))
iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
else
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
if (!iforce_get_id_packet(iforce, "N"))
iforce->dev.ff_effects_max = iforce->edata[1];
else
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
/* Check if the device can store more effects than the driver can really handle */
if (iforce->dev.ff_effects_max > FF_EFFECTS_MAX) {
printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n",
iforce->dev.ff_effects_max, FF_EFFECTS_MAX);
iforce->dev.ff_effects_max = FF_EFFECTS_MAX;
}
/*
* Display additional info.
*/
for (i = 0; c[i]; i++)
if (!iforce_get_id_packet(iforce, c + i))
iforce_dump_packet("info", iforce->ecmd, iforce->edata);
/*
* Disable spring, enable force feedback.
* FIXME: We should use iforce_set_autocenter() et al here.
*/
iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
/*
* Find appropriate device entry
*/
for (i = 0; iforce_device[i].idvendor; i++)
if (iforce_device[i].idvendor == iforce->dev.id.vendor &&
iforce_device[i].idproduct == iforce->dev.id.product)
break;
iforce->type = iforce_device + i;
iforce->dev.name = iforce->type->name;
/*
* Set input device bitfields and ranges.
*/
iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
for (i = 0; iforce->type->btn[i] >= 0; i++) {
signed short t = iforce->type->btn[i];
set_bit(t, iforce->dev.keybit);
}
set_bit(BTN_DEAD, iforce->dev.keybit);
for (i = 0; iforce->type->abs[i] >= 0; i++) {
signed short t = iforce->type->abs[i];
set_bit(t, iforce->dev.absbit);
switch (t) {
case ABS_X:
case ABS_Y:
case ABS_WHEEL:
iforce->dev.absmax[t] = 1920;
iforce->dev.absmin[t] = -1920;
iforce->dev.absflat[t] = 128;
iforce->dev.absfuzz[t] = 16;
set_bit(t, iforce->dev.ffbit);
break;
case ABS_THROTTLE:
case ABS_GAS:
case ABS_BRAKE:
iforce->dev.absmax[t] = 255;
iforce->dev.absmin[t] = 0;
break;
case ABS_RUDDER:
iforce->dev.absmax[t] = 127;
iforce->dev.absmin[t] = -128;
break;
case ABS_HAT0X:
case ABS_HAT0Y:
case ABS_HAT1X:
case ABS_HAT1Y:
iforce->dev.absmax[t] = 1;
iforce->dev.absmin[t] = -1;
break;
}
}
for (i = 0; iforce->type->ff[i] >= 0; i++)
set_bit(iforce->type->ff[i], iforce->dev.ffbit);
/*
* Register input device.
*/
input_register_device(&iforce->dev);
printk(KERN_DEBUG "iforce->dev.open = %p\n", iforce->dev.open);
printk(KERN_INFO "input: %s [%d effects, %ld bytes memory]\n",
iforce->dev.name, iforce->dev.ff_effects_max,
iforce->device_memory.end);
return 0;
}
static int __init iforce_init(void)
{
#ifdef CONFIG_JOYSTICK_IFORCE_USB
usb_register(&iforce_usb_driver);
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_232
serio_register_driver(&iforce_serio_drv);
#endif
return 0;
}
static void __exit iforce_exit(void)
{
#ifdef CONFIG_JOYSTICK_IFORCE_USB
usb_deregister(&iforce_usb_driver);
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_232
serio_unregister_driver(&iforce_serio_drv);
#endif
}
module_init(iforce_init);
module_exit(iforce_exit);

View File

@@ -0,0 +1,319 @@
/*
* $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
static struct {
__s32 x;
__s32 y;
} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
{
int i;
printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
for (i = 0; i < LO(cmd); i++)
printk("%02x ", data[i]);
printk(")\n");
}
/*
* Send a packet of bytes to the device
*/
int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
{
/* Copy data to buffer */
int n = LO(cmd);
int c;
int empty;
int head, tail;
unsigned long flags;
/*
* Update head and tail of xmit buffer
*/
spin_lock_irqsave(&iforce->xmit_lock, flags);
head = iforce->xmit.head;
tail = iforce->xmit.tail;
if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n");
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return -1;
}
empty = head == tail;
XMIT_INC(iforce->xmit.head, n+2);
/*
* Store packet in xmit buffer
*/
iforce->xmit.buf[head] = HI(cmd);
XMIT_INC(head, 1);
iforce->xmit.buf[head] = LO(cmd);
XMIT_INC(head, 1);
c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE);
if (n < c) c=n;
memcpy(&iforce->xmit.buf[head],
data,
c);
if (n != c) {
memcpy(&iforce->xmit.buf[0],
data + c,
n - c);
}
XMIT_INC(head, n);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
/*
* If necessary, start the transmission
*/
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_232
case IFORCE_232:
if (empty)
iforce_serial_xmit(iforce);
break;
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
if (iforce->usbdev && empty &&
!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
iforce_usb_xmit(iforce);
}
break;
#endif
}
return 0;
}
/* Start or stop an effect */
int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
{
unsigned char data[3];
printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value);
data[0] = LO(id);
data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
data[2] = LO(value);
return iforce_send_packet(iforce, FF_CMD_PLAY, data);
}
/* Mark an effect that was being updated as ready. That means it can be updated
* again */
static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
{
int i;
for (i=0; i<iforce->dev.ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
(iforce->core_effects[i].mod1_chunk.start == addr ||
iforce->core_effects[i].mod2_chunk.start == addr)) {
clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags);
return 0;
}
}
printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr);
return -1;
}
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data, struct pt_regs *regs)
{
struct input_dev *dev = &iforce->dev;
int i;
static int being_used = 0;
if (being_used)
printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used);
being_used++;
#ifdef CONFIG_JOYSTICK_IFORCE_232
if (HI(iforce->expect_packet) == HI(cmd)) {
iforce->expect_packet = 0;
iforce->ecmd = cmd;
memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
wake_up(&iforce->wait);
}
#endif
if (!iforce->type) {
being_used--;
return;
}
switch (HI(cmd)) {
case 0x01: /* joystick position data */
case 0x03: /* wheel position data */
input_regs(dev, regs);
if (HI(cmd) == 1) {
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
} else {
input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
input_report_abs(dev, ABS_GAS, 255 - data[2]);
input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
}
input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
for (i = 0; iforce->type->btn[i] >= 0; i++)
input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
/* If there are untouched bits left, interpret them as the second hat */
if (i <= 8) {
int btns = data[6];
if (test_bit(ABS_HAT1X, dev->absbit)) {
if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1);
else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1);
else input_report_abs(dev, ABS_HAT1X, 0);
}
if (test_bit(ABS_HAT1Y, dev->absbit)) {
if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1);
else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1);
else input_report_abs(dev, ABS_HAT1Y, 0);
}
}
input_sync(dev);
break;
case 0x02: /* status report */
input_regs(dev, regs);
input_report_key(dev, BTN_DEAD, data[0] & 0x02);
input_sync(dev);
/* Check if an effect was just started or stopped */
i = data[1] & 0x7f;
if (data[1] & 0x80) {
if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report play event */
input_report_ff_status(dev, i, FF_STATUS_PLAYING);
}
}
else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report stop event */
input_report_ff_status(dev, i, FF_STATUS_STOPPED);
}
if (LO(cmd) > 3) {
int j;
for (j=3; j<LO(cmd); j+=2) {
mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
}
}
break;
}
being_used--;
}
int iforce_get_id_packet(struct iforce *iforce, char *packet)
{
DECLARE_WAITQUEUE(wait, current);
int timeout = HZ; /* 1 second */
switch (iforce->bus) {
case IFORCE_USB:
#ifdef CONFIG_JOYSTICK_IFORCE_USB
iforce->cr.bRequest = packet[0];
iforce->ctrl->dev = iforce->usbdev;
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&iforce->wait, &wait);
if (usb_submit_urb(iforce->ctrl, GFP_ATOMIC)) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&iforce->wait, &wait);
return -1;
}
while (timeout && iforce->ctrl->status == -EINPROGRESS)
timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING);
remove_wait_queue(&iforce->wait, &wait);
if (!timeout) {
usb_unlink_urb(iforce->ctrl);
return -1;
}
#else
printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n");
#endif
break;
case IFORCE_232:
#ifdef CONFIG_JOYSTICK_IFORCE_232
iforce->expect_packet = FF_CMD_QUERY;
iforce_send_packet(iforce, FF_CMD_QUERY, packet);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&iforce->wait, &wait);
while (timeout && iforce->expect_packet)
timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING);
remove_wait_queue(&iforce->wait, &wait);
if (!timeout) {
iforce->expect_packet = 0;
return -1;
}
#else
printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n");
#endif
break;
default:
printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n",
iforce->bus);
break;
}
return -(iforce->edata[0] != packet[0]);
}

View File

@@ -0,0 +1,170 @@
/*
* $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
void iforce_serial_xmit(struct iforce *iforce)
{
unsigned char cs;
int i;
unsigned long flags;
if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags);
return;
}
spin_lock_irqsave(&iforce->xmit_lock, flags);
again:
if (iforce->xmit.head == iforce->xmit.tail) {
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return;
}
cs = 0x2b;
serio_write(iforce->serio, 0x2b);
serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
cs ^= iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
cs ^= iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
}
serio_write(iforce->serio, cs);
if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
goto again;
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
}
static void iforce_serio_write_wakeup(struct serio *serio)
{
iforce_serial_xmit((struct iforce *)serio->private);
}
static irqreturn_t iforce_serio_irq(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct iforce* iforce = serio->private;
if (!iforce->pkt) {
if (data == 0x2b)
iforce->pkt = 1;
goto out;
}
if (!iforce->id) {
if (data > 3 && data != 0xff)
iforce->pkt = 0;
else
iforce->id = data;
goto out;
}
if (!iforce->len) {
if (data > IFORCE_MAX_LENGTH) {
iforce->pkt = 0;
iforce->id = 0;
} else {
iforce->len = data;
}
goto out;
}
if (iforce->idx < iforce->len) {
iforce->csum += iforce->data[iforce->idx++] = data;
goto out;
}
if (iforce->idx == iforce->len) {
iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data, regs);
iforce->pkt = 0;
iforce->id = 0;
iforce->len = 0;
iforce->idx = 0;
iforce->csum = 0;
}
out:
return IRQ_HANDLED;
}
static void iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
{
struct iforce *iforce;
if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
return;
if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
memset(iforce, 0, sizeof(struct iforce));
iforce->bus = IFORCE_232;
iforce->serio = serio;
serio->private = iforce;
if (serio_open(serio, drv)) {
kfree(iforce);
return;
}
if (iforce_init_device(iforce)) {
serio_close(serio);
kfree(iforce);
return;
}
}
static void iforce_serio_disconnect(struct serio *serio)
{
struct iforce* iforce = serio->private;
input_unregister_device(&iforce->dev);
serio_close(serio);
kfree(iforce);
}
struct serio_driver iforce_serio_drv = {
.driver = {
.name = "iforce",
},
.description = "RS232 I-Force joysticks and wheels driver",
.write_wakeup = iforce_serio_write_wakeup,
.interrupt = iforce_serio_irq,
.connect = iforce_serio_connect,
.disconnect = iforce_serio_disconnect,
};

View File

@@ -0,0 +1,243 @@
/*
* $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
void iforce_usb_xmit(struct iforce *iforce)
{
int n, c;
unsigned long flags;
spin_lock_irqsave(&iforce->xmit_lock, flags);
if (iforce->xmit.head == iforce->xmit.tail) {
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return;
}
((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
n = iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1);
iforce->out->transfer_buffer_length = n + 1;
iforce->out->dev = iforce->usbdev;
/* Copy rest of data then */
c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
if (n < c) c=n;
memcpy(iforce->out->transfer_buffer + 1,
&iforce->xmit.buf[iforce->xmit.tail],
c);
if (n != c) {
memcpy(iforce->out->transfer_buffer + 1 + c,
&iforce->xmit.buf[0],
n-c);
}
XMIT_INC(iforce->xmit.tail, n);
if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n);
}
/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
* As long as the urb completion handler is not called, the transmiting
* is considered to be running */
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
}
static void iforce_usb_irq(struct urb *urb, struct pt_regs *regs)
{
struct iforce *iforce = urb->context;
int status;
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d",
__FUNCTION__, urb->status);
return;
default:
dbg("%s - urb has status of: %d", __FUNCTION__, urb->status);
goto exit;
}
iforce_process_packet(iforce,
(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1, regs);
exit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
err ("%s - usb_submit_urb failed with result %d",
__FUNCTION__, status);
}
static void iforce_usb_out(struct urb *urb, struct pt_regs *regs)
{
struct iforce *iforce = urb->context;
if (urb->status) {
printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status);
return;
}
iforce_usb_xmit(iforce);
wake_up(&iforce->wait);
}
static void iforce_usb_ctrl(struct urb *urb, struct pt_regs *regs)
{
struct iforce *iforce = urb->context;
if (urb->status) return;
iforce->ecmd = 0xff00 | urb->actual_length;
wake_up(&iforce->wait);
}
static int iforce_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *epirq, *epout;
struct iforce *iforce;
interface = intf->cur_altsetting;
epirq = &interface->endpoint[0].desc;
epout = &interface->endpoint[1].desc;
if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
goto fail;
memset(iforce, 0, sizeof(struct iforce));
if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL))) {
goto fail;
}
if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) {
goto fail;
}
if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) {
goto fail;
}
iforce->bus = IFORCE_USB;
iforce->usbdev = dev;
iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
iforce->cr.wIndex = 0;
iforce->cr.wLength = 16;
usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
usb_fill_bulk_urb(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
iforce + 1, 32, iforce_usb_out, iforce);
usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
(void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
if (iforce_init_device(iforce)) goto fail;
usb_set_intfdata(intf, iforce);
return 0;
fail:
if (iforce) {
if (iforce->irq) usb_free_urb(iforce->irq);
if (iforce->out) usb_free_urb(iforce->out);
if (iforce->ctrl) usb_free_urb(iforce->ctrl);
kfree(iforce);
}
return -ENODEV;
}
/* Called by iforce_delete() */
void iforce_usb_delete(struct iforce* iforce)
{
usb_unlink_urb(iforce->irq);
/* Is it ok to unlink those ? */
usb_unlink_urb(iforce->out);
usb_unlink_urb(iforce->ctrl);
usb_free_urb(iforce->irq);
usb_free_urb(iforce->out);
usb_free_urb(iforce->ctrl);
}
static void iforce_usb_disconnect(struct usb_interface *intf)
{
struct iforce *iforce = usb_get_intfdata(intf);
int open = 0; /* FIXME! iforce->dev.handle->open; */
usb_set_intfdata(intf, NULL);
if (iforce) {
iforce->usbdev = NULL;
input_unregister_device(&iforce->dev);
if (!open) {
iforce_delete_device(iforce);
kfree(iforce);
}
}
}
static struct usb_device_id iforce_usb_ids [] = {
{ USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */
{ USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */
{ USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */
{ USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */
{ USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */
{ USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */
{ USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */
{ USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
{ USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
struct usb_driver iforce_usb_driver = {
.owner = THIS_MODULE,
.name = "iforce",
.probe = iforce_usb_probe,
.disconnect = iforce_usb_disconnect,
.id_table = iforce_usb_ids,
};

View File

@@ -0,0 +1,191 @@
/*
* $Id: iforce.h,v 1.13 2002/07/07 10:22:50 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/serio.h>
#include <linux/config.h>
#include <linux/circ_buf.h>
#include <asm/semaphore.h>
/* This module provides arbitrary resource management routines.
* I use it to manage the device's memory.
* Despite the name of this module, I am *not* going to access the ioports.
*/
#include <linux/ioport.h>
#define IFORCE_MAX_LENGTH 16
/* iforce::bus */
#define IFORCE_232 1
#define IFORCE_USB 2
#define FALSE 0
#define TRUE 1
#define FF_EFFECTS_MAX 32
/* Each force feedback effect is made of one core effect, which can be
* associated to at most to effect modifiers
*/
#define FF_MOD1_IS_USED 0
#define FF_MOD2_IS_USED 1
#define FF_CORE_IS_USED 2
#define FF_CORE_IS_PLAYED 3 /* Effect is currently being played */
#define FF_CORE_SHOULD_PLAY 4 /* User wants the effect to be played */
#define FF_CORE_UPDATE 5 /* Effect is being updated */
#define FF_MODCORE_MAX 5
#define CHECK_OWNERSHIP(i, iforce) \
((i) < FF_EFFECTS_MAX && i >= 0 && \
test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \
(current->pid == 0 || \
(iforce)->core_effects[(i)].owner == current->pid))
struct iforce_core_effect {
/* Information about where modifiers are stored in the device's memory */
struct resource mod1_chunk;
struct resource mod2_chunk;
unsigned long flags[NBITS(FF_MODCORE_MAX)];
pid_t owner;
/* Used to keep track of parameters of an effect. They are needed
* to know what parts of an effect changed in an update operation.
* We try to send only parameter packets if possible, as sending
* effect parameter requires the effect to be stoped and restarted
*/
struct ff_effect effect;
};
#define FF_CMD_EFFECT 0x010e
#define FF_CMD_ENVELOPE 0x0208
#define FF_CMD_MAGNITUDE 0x0303
#define FF_CMD_PERIOD 0x0407
#define FF_CMD_CONDITION 0x050a
#define FF_CMD_AUTOCENTER 0x4002
#define FF_CMD_PLAY 0x4103
#define FF_CMD_ENABLE 0x4201
#define FF_CMD_GAIN 0x4301
#define FF_CMD_QUERY 0xff01
/* Buffer for async write */
#define XMIT_SIZE 256
#define XMIT_INC(var, n) (var)+=n; (var)&= XMIT_SIZE -1
/* iforce::xmit_flags */
#define IFORCE_XMIT_RUNNING 0
#define IFORCE_XMIT_AGAIN 1
struct iforce_device {
u16 idvendor;
u16 idproduct;
char *name;
signed short *btn;
signed short *abs;
signed short *ff;
};
struct iforce {
struct input_dev dev; /* Input device interface */
struct iforce_device *type;
int bus;
unsigned char data[IFORCE_MAX_LENGTH];
unsigned char edata[IFORCE_MAX_LENGTH];
u16 ecmd;
u16 expect_packet;
#ifdef CONFIG_JOYSTICK_IFORCE_232
struct serio *serio; /* RS232 transfer */
int idx, pkt, len, id;
unsigned char csum;
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_USB
struct usb_device *usbdev; /* USB transfer */
struct urb *irq, *out, *ctrl;
struct usb_ctrlrequest cr;
#endif
spinlock_t xmit_lock;
/* Buffer used for asynchronous sending of bytes to the device */
struct circ_buf xmit;
unsigned char xmit_data[XMIT_SIZE];
long xmit_flags[1];
/* Force Feedback */
wait_queue_head_t wait;
struct resource device_memory;
struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
struct semaphore mem_mutex;
};
/* Get hi and low bytes of a 16-bits int */
#define HI(a) ((unsigned char)((a) >> 8))
#define LO(a) ((unsigned char)((a) & 0xff))
/* For many parameters, it seems that 0x80 is a special value that should
* be avoided. Instead, we replace this value by 0x7f
*/
#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8))
/* Encode a time value */
#define TIME_SCALE(a) (a)
/* Public functions */
/* iforce-serio.c */
void iforce_serial_xmit(struct iforce *iforce);
/* iforce-usb.c */
void iforce_usb_xmit(struct iforce *iforce);
void iforce_usb_delete(struct iforce *iforce);
/* iforce-main.c */
int iforce_init_device(struct iforce *iforce);
void iforce_delete_device(struct iforce *iforce);
/* iforce-packets.c */
int iforce_control_playback(struct iforce*, u16 id, unsigned int);
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data, struct pt_regs *regs);
int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
int iforce_get_id_packet(struct iforce *iforce, char *packet);
/* iforce-ff.c */
int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update);
int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update);
/* Public variables */
extern struct serio_driver iforce_serio_drv;
extern struct usb_driver iforce_usb_driver;

View File

@@ -0,0 +1,314 @@
/*
* $Id: interact.c,v 1.16 2002/01/22 20:28:25 vojtech Exp $
*
* Copyright (c) 2001 Vojtech Pavlik
*
* Based on the work of:
* Toby Deshane
*/
/*
* InterAct digital gamepad/joystick driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("InterAct digital joystick driver");
MODULE_LICENSE("GPL");
#define INTERACT_MAX_START 400 /* 400 us */
#define INTERACT_MAX_STROBE 40 /* 40 us */
#define INTERACT_MAX_LENGTH 32 /* 32 bits */
#define INTERACT_REFRESH_TIME HZ/50 /* 20 ms */
#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */
#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */
struct interact {
struct gameport *gameport;
struct input_dev dev;
struct timer_list timer;
int used;
int bads;
int reads;
unsigned char type;
unsigned char length;
char phys[32];
};
static short interact_abs_hhfx[] =
{ ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
static short interact_abs_pp8d[] =
{ ABS_X, ABS_Y, -1 };
static short interact_btn_hhfx[] =
{ BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 };
static short interact_btn_pp8d[] =
{ BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
struct interact_type {
int id;
short *abs;
short *btn;
char *name;
unsigned char length;
unsigned char b8;
};
static struct interact_type interact_type[] = {
{ 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX", 32, 4 },
{ 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
{ 0 }};
/*
* interact_read_packet() reads and InterAct joystick data.
*/
static int interact_read_packet(struct gameport *gameport, int length, u32 *data)
{
unsigned long flags;
unsigned char u, v;
unsigned int t, s;
int i;
i = 0;
data[0] = data[1] = data[2] = 0;
t = gameport_time(gameport, INTERACT_MAX_START);
s = gameport_time(gameport, INTERACT_MAX_STROBE);
local_irq_save(flags);
gameport_trigger(gameport);
v = gameport_read(gameport);
while (t > 0 && i < length) {
t--;
u = v; v = gameport_read(gameport);
if (v & ~u & 0x40) {
data[0] = (data[0] << 1) | ((v >> 4) & 1);
data[1] = (data[1] << 1) | ((v >> 5) & 1);
data[2] = (data[2] << 1) | ((v >> 7) & 1);
i++;
t = s;
}
}
local_irq_restore(flags);
return i;
}
/*
* interact_timer() reads and analyzes InterAct joystick data.
*/
static void interact_timer(unsigned long private)
{
struct interact *interact = (struct interact *) private;
struct input_dev *dev = &interact->dev;
u32 data[3];
int i;
interact->reads++;
if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
interact->bads++;
} else {
for (i = 0; i < 3; i++)
data[i] <<= INTERACT_MAX_LENGTH - interact->length;
switch (interact->type) {
case INTERACT_TYPE_HHFX:
for (i = 0; i < 4; i++)
input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
for (i = 0; i < 2; i++)
input_report_abs(dev, ABS_HAT0Y - i,
((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1));
for (i = 0; i < 8; i++)
input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
for (i = 0; i < 4; i++)
input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
break;
case INTERACT_TYPE_PP8D:
for (i = 0; i < 2; i++)
input_report_abs(dev, interact_abs_pp8d[i],
((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1));
for (i = 0; i < 8; i++)
input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
break;
}
}
input_sync(dev);
mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME);
}
/*
* interact_open() is a callback from the input open routine.
*/
static int interact_open(struct input_dev *dev)
{
struct interact *interact = dev->private;
if (!interact->used++)
mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME);
return 0;
}
/*
* interact_close() is a callback from the input close routine.
*/
static void interact_close(struct input_dev *dev)
{
struct interact *interact = dev->private;
if (!--interact->used)
del_timer(&interact->timer);
}
/*
* interact_connect() probes for InterAct joysticks.
*/
static void interact_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct interact *interact;
__u32 data[3];
int i, t;
if (!(interact = kmalloc(sizeof(struct interact), GFP_KERNEL)))
return;
memset(interact, 0, sizeof(struct interact));
gameport->private = interact;
interact->gameport = gameport;
init_timer(&interact->timer);
interact->timer.data = (long) interact;
interact->timer.function = interact_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data);
if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) {
goto fail2;
}
for (i = 0; interact_type[i].length; i++)
if (interact_type[i].id == (data[2] >> 16))
break;
if (!interact_type[i].length) {
printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
gameport->phys, i, data[0], data[1], data[2]);
goto fail2;
}
sprintf(interact->phys, "%s/input0", gameport->phys);
interact->type = i;
interact->length = interact_type[i].length;
interact->dev.private = interact;
interact->dev.open = interact_open;
interact->dev.close = interact_close;
interact->dev.name = interact_type[i].name;
interact->dev.phys = interact->phys;
interact->dev.id.bustype = BUS_GAMEPORT;
interact->dev.id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
interact->dev.id.product = interact_type[i].id;
interact->dev.id.version = 0x0100;
interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
set_bit(t, interact->dev.absbit);
if (i < interact_type[interact->type].b8) {
interact->dev.absmin[t] = 0;
interact->dev.absmax[t] = 255;
} else {
interact->dev.absmin[t] = -1;
interact->dev.absmax[t] = 1;
}
}
for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
set_bit(t, interact->dev.keybit);
input_register_device(&interact->dev);
printk(KERN_INFO "input: %s on %s\n",
interact_type[interact->type].name, gameport->phys);
return;
fail2: gameport_close(gameport);
fail1: kfree(interact);
}
static void interact_disconnect(struct gameport *gameport)
{
struct interact *interact = gameport->private;
input_unregister_device(&interact->dev);
gameport_close(gameport);
kfree(interact);
}
static struct gameport_dev interact_dev = {
.connect = interact_connect,
.disconnect = interact_disconnect,
};
int __init interact_init(void)
{
gameport_register_device(&interact_dev);
return 0;
}
void __exit interact_exit(void)
{
gameport_unregister_device(&interact_dev);
}
module_init(interact_init);
module_exit(interact_exit);

View File

@@ -0,0 +1,151 @@
/*
* $Id: joydump.c,v 1.1 2002/01/23 06:56:16 jsimmons Exp $
*
* Copyright (c) 1996-2001 Vojtech Pavlik
*/
/*
* This is just a very simple driver that can dump the data
* out of the joystick port into the syslog ...
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/gameport.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Gameport data dumper module");
MODULE_LICENSE("GPL");
#define BUF_SIZE 256
struct joydump {
unsigned int time;
unsigned char data;
};
static void __devinit joydump_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct joydump buf[BUF_SIZE];
int axes[4], buttons;
int i, j, t, timeout;
unsigned long flags;
unsigned char u;
printk(KERN_INFO "joydump: ,------------------- START ------------------.\n");
printk(KERN_INFO "joydump: | Dumping gameport%s.\n", gameport->phys);
printk(KERN_INFO "joydump: | Speed: %4d kHz. |\n", gameport->speed);
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) {
printk(KERN_INFO "joydump: | Raw mode not available - trying cooked. |\n");
if (gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) {
printk(KERN_INFO "joydump: | Cooked not available either. Failing. |\n");
printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
return;
}
gameport_cooked_read(gameport, axes, &buttons);
for (i = 0; i < 4; i++)
printk(KERN_INFO "joydump: | Axis %d: %4d. |\n", i, axes[i]);
printk(KERN_INFO "joydump: | Buttons %02x. |\n", buttons);
printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
}
timeout = gameport_time(gameport, 10000); /* 10 ms */
t = 0;
i = 1;
local_irq_save(flags);
u = gameport_read(gameport);
buf[0].data = u;
buf[0].time = t;
gameport_trigger(gameport);
while (i < BUF_SIZE && t < timeout) {
buf[i].data = gameport_read(gameport);
if (buf[i].data ^ u) {
u = buf[i].data;
buf[i].time = t;
i++;
}
t++;
}
local_irq_restore(flags);
/*
* Dump data.
*/
t = i;
printk(KERN_INFO "joydump: >------------------- DATA -------------------<\n");
printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", 0, 0, 0);
for (j = 7; j >= 0; j--)
printk("%d",(buf[0].data >> j) & 1);
printk(" |\n");
for (i = 1; i < t; i++) {
printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ",
i, buf[i].time - buf[i-1].time);
for (j = 7; j >= 0; j--)
printk("%d",(buf[i].data >> j) & 1);
printk(" |\n");
}
printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
}
static void __devexit joydump_disconnect(struct gameport *gameport)
{
gameport_close(gameport);
}
static struct gameport_dev joydump_dev = {
.connect = joydump_connect,
.disconnect = joydump_disconnect,
};
static int __init joydump_init(void)
{
gameport_register_device(&joydump_dev);
return 0;
}
static void __exit joydump_exit(void)
{
gameport_unregister_device(&joydump_dev);
}
module_init(joydump_init);
module_exit(joydump_exit);

View File

@@ -0,0 +1,230 @@
/*
* $Id: magellan.c,v 1.16 2002/01/22 20:28:39 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Magellan and Space Mouse 6dof controller driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Definitions & global arrays.
*/
#define MAGELLAN_MAX_LENGTH 32
static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
static char *magellan_name = "LogiCad3D Magellan / SpaceMouse";
/*
* Per-Magellan data.
*/
struct magellan {
struct input_dev dev;
int idx;
unsigned char data[MAGELLAN_MAX_LENGTH];
char phys[32];
};
/*
* magellan_crunch_nibbles() verifies that the bytes sent from the Magellan
* have correct upper nibbles for the lower ones, if not, the packet will
* be thrown away. It also strips these upper halves to simplify further
* processing.
*/
static int magellan_crunch_nibbles(unsigned char *data, int count)
{
static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?";
do {
if (data[count] == nibbles[data[count] & 0xf])
data[count] = data[count] & 0xf;
else
return -1;
} while (--count);
return 0;
}
static void magellan_process_packet(struct magellan* magellan, struct pt_regs *regs)
{
struct input_dev *dev = &magellan->dev;
unsigned char *data = magellan->data;
int i, t;
if (!magellan->idx) return;
input_regs(dev, regs);
switch (magellan->data[0]) {
case 'd': /* Axis data */
if (magellan->idx != 25) return;
if (magellan_crunch_nibbles(data, 24)) return;
for (i = 0; i < 6; i++)
input_report_abs(dev, magellan_axes[i],
(data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 |
data[(i << 2) + 3] << 4 | data[(i << 2) + 4]) - 32768);
break;
case 'k': /* Button data */
if (magellan->idx != 4) return;
if (magellan_crunch_nibbles(data, 3)) return;
t = (data[1] << 1) | (data[2] << 5) | data[3];
for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1);
break;
}
input_sync(dev);
}
static irqreturn_t magellan_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct magellan* magellan = serio->private;
if (data == '\r') {
magellan_process_packet(magellan, regs);
magellan->idx = 0;
} else {
if (magellan->idx < MAGELLAN_MAX_LENGTH)
magellan->data[magellan->idx++] = data;
}
return IRQ_HANDLED;
}
/*
* magellan_disconnect() is the opposite of magellan_connect()
*/
static void magellan_disconnect(struct serio *serio)
{
struct magellan* magellan = serio->private;
input_unregister_device(&magellan->dev);
serio_close(serio);
kfree(magellan);
}
/*
* magellan_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Magellan, and if found, registers
* it as an input device.
*/
static void magellan_connect(struct serio *serio, struct serio_driver *drv)
{
struct magellan *magellan;
int i, t;
if (serio->type != (SERIO_RS232 | SERIO_MAGELLAN))
return;
if (!(magellan = kmalloc(sizeof(struct magellan), GFP_KERNEL)))
return;
memset(magellan, 0, sizeof(struct magellan));
magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < 9; i++)
set_bit(magellan_buttons[i], magellan->dev.keybit);
for (i = 0; i < 6; i++) {
t = magellan_axes[i];
set_bit(t, magellan->dev.absbit);
magellan->dev.absmin[t] = -360;
magellan->dev.absmax[t] = 360;
}
sprintf(magellan->phys, "%s/input0", serio->phys);
init_input_dev(&magellan->dev);
magellan->dev.private = magellan;
magellan->dev.name = magellan_name;
magellan->dev.phys = magellan->phys;
magellan->dev.id.bustype = BUS_RS232;
magellan->dev.id.vendor = SERIO_MAGELLAN;
magellan->dev.id.product = 0x0001;
magellan->dev.id.version = 0x0100;
serio->private = magellan;
if (serio_open(serio, drv)) {
kfree(magellan);
return;
}
input_register_device(&magellan->dev);
printk(KERN_INFO "input: %s on %s\n", magellan_name, serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_driver magellan_drv = {
.driver = {
.name = "magellan",
},
.description = DRIVER_DESC,
.interrupt = magellan_interrupt,
.connect = magellan_connect,
.disconnect = magellan_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init magellan_init(void)
{
serio_register_driver(&magellan_drv);
return 0;
}
void __exit magellan_exit(void)
{
serio_unregister_driver(&magellan_drv);
}
module_init(magellan_init);
module_exit(magellan_exit);

View File

@@ -0,0 +1,780 @@
/*
* $Id: sidewinder.c,v 1.29 2002/01/22 20:28:51 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*/
/*
* Microsoft SideWinder joystick family driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/gameport.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Microsoft SideWinder joystick family driver");
MODULE_LICENSE("GPL");
/*
* These are really magic values. Changing them can make a problem go away,
* as well as break everything.
*/
#define SW_DEBUG
#define SW_START 400 /* The time we wait for the first bit [400 us] */
#define SW_STROBE 45 /* Max time per bit [45 us] */
#define SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */
#define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */
#define SW_END 8 /* Number of bits before end of packet to kick */
#define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */
#define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */
#define SW_OK 64 /* Number of packet read successes to switch optimization back on */
#define SW_LENGTH 512 /* Max number of bits in a packet */
#define SW_REFRESH HZ/50 /* Time to wait between updates of joystick data [20 ms] */
#ifdef SW_DEBUG
#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
#else
#define dbg(format, arg...) do {} while (0)
#endif
/*
* SideWinder joystick types ...
*/
#define SW_ID_3DP 0
#define SW_ID_GP 1
#define SW_ID_PP 2
#define SW_ID_FFP 3
#define SW_ID_FSP 4
#define SW_ID_FFW 5
/*
* Names, buttons, axes ...
*/
static char *sw_name[] = { "3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro",
"Force Feedback Wheel" };
static char sw_abs[][7] = {
{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
{ ABS_X, ABS_Y },
{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
{ ABS_RX, ABS_RUDDER, ABS_THROTTLE }};
static char sw_bit[][7] = {
{ 10, 10, 9, 10, 1, 1 },
{ 1, 1 },
{ 10, 10, 6, 7, 1, 1 },
{ 10, 10, 6, 7, 1, 1 },
{ 10, 10, 6, 1, 1 },
{ 10, 7, 7, 1, 1 }};
static short sw_btn[][12] = {
{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE },
{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE },
{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT },
{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }};
static struct {
int x;
int y;
} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
struct sw {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev[4];
char name[64];
char phys[4][32];
int length;
int type;
int bits;
int number;
int fail;
int ok;
int reads;
int bads;
int used;
};
/*
* sw_read_packet() is a function which reads either a data packet, or an
* identification packet from a SideWinder joystick. The protocol is very,
* very, very braindamaged. Microsoft patented it in US patent #5628686.
*/
static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id)
{
unsigned long flags;
int timeout, bitout, sched, i, kick, start, strobe;
unsigned char pending, u, v;
i = -id; /* Don't care about data, only want ID */
timeout = id ? gameport_time(gameport, SW_TIMEOUT) : 0; /* Set up global timeout for ID packet */
kick = id ? gameport_time(gameport, SW_KICK) : 0; /* Set up kick timeout for ID packet */
start = gameport_time(gameport, SW_START);
strobe = gameport_time(gameport, SW_STROBE);
bitout = start;
pending = 0;
sched = 0;
local_irq_save(flags); /* Quiet, please */
gameport_trigger(gameport); /* Trigger */
v = gameport_read(gameport);
do {
bitout--;
u = v;
v = gameport_read(gameport);
} while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */
if (bitout > 0) bitout = strobe; /* Extend time if not timed out */
while ((timeout > 0 || bitout > 0) && (i < length)) {
timeout--;
bitout--; /* Decrement timers */
sched--;
u = v;
v = gameport_read(gameport);
if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */
if (i >= 0) /* Want this data */
buf[i] = v >> 5; /* Store it */
i++; /* Advance index */
bitout = strobe; /* Extend timeout for next bit */
}
if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */
sched = kick; /* Schedule second trigger */
kick = 0; /* Don't schedule next time on falling edge */
pending = 1; /* Mark schedule */
}
if (pending && sched < 0 && (i > -SW_END)) { /* Second trigger time */
gameport_trigger(gameport); /* Trigger */
bitout = start; /* Long bit timeout */
pending = 0; /* Unmark schedule */
timeout = 0; /* Switch from global to bit timeouts */
}
}
local_irq_restore(flags); /* Done - relax */
#ifdef SW_DEBUG
{
int j;
printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i);
for (j = 0; j < i; j++) printk("%d", buf[j]);
printk("]\n");
}
#endif
return i;
}
/*
* sw_get_bits() and GB() compose bits from the triplet buffer into a __u64.
* Parameter 'pos' is bit number inside packet where to start at, 'num' is number
* of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
* is number of bits per triplet.
*/
#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits)
static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits)
{
__u64 data = 0;
int tri = pos % bits; /* Start position */
int i = pos / bits;
int bit = 0;
while (num--) {
data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */
if (tri == bits) {
i++; /* Next triplet */
tri = 0;
}
}
return data;
}
/*
* sw_init_digital() initializes a SideWinder 3D Pro joystick
* into digital mode.
*/
static void sw_init_digital(struct gameport *gameport)
{
int seq[] = { 140, 140+725, 140+300, 0 };
unsigned long flags;
int i, t;
local_irq_save(flags);
i = 0;
do {
gameport_trigger(gameport); /* Trigger */
t = gameport_time(gameport, SW_TIMEOUT);
while ((gameport_read(gameport) & 1) && t) t--; /* Wait for axis to fall back to 0 */
udelay(seq[i]); /* Delay magic time */
} while (seq[++i]);
gameport_trigger(gameport); /* Last trigger */
local_irq_restore(flags);
}
/*
* sw_parity() computes parity of __u64
*/
static int sw_parity(__u64 t)
{
int x = t ^ (t >> 32);
x ^= x >> 16;
x ^= x >> 8;
x ^= x >> 4;
x ^= x >> 2;
x ^= x >> 1;
return x & 1;
}
/*
* sw_ccheck() checks synchronization bits and computes checksum of nibbles.
*/
static int sw_check(__u64 t)
{
unsigned char sum = 0;
if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */
return -1;
while (t) { /* Sum */
sum += t & 0xf;
t >>= 4;
}
return sum & 0xf;
}
/*
* sw_parse() analyzes SideWinder joystick data, and writes the results into
* the axes and buttons arrays.
*/
static int sw_parse(unsigned char *buf, struct sw *sw)
{
int hat, i, j;
struct input_dev *dev = sw->dev;
switch (sw->type) {
case SW_ID_3DP:
if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8) return -1;
input_report_abs(dev, ABS_X, (GB( 3,3) << 7) | GB(16,7));
input_report_abs(dev, ABS_Y, (GB( 0,3) << 7) | GB(24,7));
input_report_abs(dev, ABS_RZ, (GB(35,2) << 7) | GB(40,7));
input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7));
input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
for (j = 0; j < 7; j++)
input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1));
input_report_key(dev, BTN_BASE4, !GB(38,1));
input_report_key(dev, BTN_BASE5, !GB(37,1));
input_sync(dev);
return 0;
case SW_ID_GP:
for (i = 0; i < sw->number; i ++) {
if (sw_parity(GB(i*15,15))) return -1;
input_report_abs(dev + i, ABS_X, GB(i*15+3,1) - GB(i*15+2,1));
input_report_abs(dev + i, ABS_Y, GB(i*15+0,1) - GB(i*15+1,1));
for (j = 0; j < 10; j++)
input_report_key(dev + i, sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1));
input_sync(dev + i);
}
return 0;
case SW_ID_PP:
case SW_ID_FFP:
if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8) return -1;
input_report_abs(dev, ABS_X, GB( 9,10));
input_report_abs(dev, ABS_Y, GB(19,10));
input_report_abs(dev, ABS_RZ, GB(36, 6));
input_report_abs(dev, ABS_THROTTLE, GB(29, 7));
input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
for (j = 0; j < 9; j++)
input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1));
input_sync(dev);
return 0;
case SW_ID_FSP:
if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8) return -1;
input_report_abs(dev, ABS_X, GB( 0,10));
input_report_abs(dev, ABS_Y, GB(16,10));
input_report_abs(dev, ABS_THROTTLE, GB(32, 6));
input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
for (j = 0; j < 6; j++)
input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1));
input_report_key(dev, BTN_TR, !GB(26,1));
input_report_key(dev, BTN_START, !GB(27,1));
input_report_key(dev, BTN_MODE, !GB(38,1));
input_report_key(dev, BTN_SELECT, !GB(39,1));
input_sync(dev);
return 0;
case SW_ID_FFW:
if (!sw_parity(GB(0,33))) return -1;
input_report_abs(dev, ABS_RX, GB( 0,10));
input_report_abs(dev, ABS_RUDDER, GB(10, 6));
input_report_abs(dev, ABS_THROTTLE, GB(16, 6));
for (j = 0; j < 8; j++)
input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1));
input_sync(dev);
return 0;
}
return -1;
}
/*
* sw_read() reads SideWinder joystick data, and reinitializes
* the joystick in case of persistent problems. This is the function that is
* called from the generic code to poll the joystick.
*/
static int sw_read(struct sw *sw)
{
unsigned char buf[SW_LENGTH];
int i;
i = sw_read_packet(sw->gameport, buf, sw->length, 0);
if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */
if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */
printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s"
" - going to reinitialize.\n", sw->gameport->phys);
sw->fail = SW_FAIL; /* Reinitialize */
i = 128; /* Bogus value */
}
if (i < 66 && GB(0,64) == GB(i*3-66,64)) /* 1 == 3 */
i = 66; /* Everything is fine */
if (i < 66 && GB(0,64) == GB(66,64)) /* 1 == 2 */
i = 66; /* Everything is fine */
if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) { /* 2 == 3 */
memmove(buf, buf + i - 22, 22); /* Move data */
i = 66; /* Carry on */
}
}
if (i == sw->length && !sw_parse(buf, sw)) { /* Parse data */
sw->fail = 0;
sw->ok++;
if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */
&& sw->ok > SW_OK) {
printk(KERN_INFO "sidewinder.c: No more trouble on %s"
" - enabling optimization again.\n", sw->gameport->phys);
sw->length = 22;
}
return 0;
}
sw->ok = 0;
sw->fail++;
if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */
printk(KERN_INFO "sidewinder.c: Many bit errors on %s"
" - disabling optimization.\n", sw->gameport->phys);
sw->length = 66;
}
if (sw->fail < SW_FAIL) return -1; /* Not enough, don't reinitialize yet */
printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s"
" - reinitializing joystick.\n", sw->gameport->phys);
if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */
udelay(3 * SW_TIMEOUT);
sw_init_digital(sw->gameport);
}
udelay(SW_TIMEOUT);
i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0); /* Read normal data packet */
udelay(SW_TIMEOUT);
sw_read_packet(sw->gameport, buf, SW_LENGTH, i); /* Read ID packet, this initializes the stick */
sw->fail = SW_FAIL;
return -1;
}
static void sw_timer(unsigned long private)
{
struct sw *sw = (void *) private;
sw->reads++;
if (sw_read(sw)) sw->bads++;
mod_timer(&sw->timer, jiffies + SW_REFRESH);
}
static int sw_open(struct input_dev *dev)
{
struct sw *sw = dev->private;
if (!sw->used++)
mod_timer(&sw->timer, jiffies + SW_REFRESH);
return 0;
}
static void sw_close(struct input_dev *dev)
{
struct sw *sw = dev->private;
if (!--sw->used)
del_timer(&sw->timer);
}
/*
* sw_print_packet() prints the contents of a SideWinder packet.
*/
static void sw_print_packet(char *name, int length, unsigned char *buf, char bits)
{
int i;
printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length);
for (i = (((length + 3) >> 2) - 1); i >= 0; i--)
printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits));
printk("]\n");
}
/*
* sw_3dp_id() translates the 3DP id into a human legible string.
* Unfortunately I don't know how to do this for the other SW types.
*/
static void sw_3dp_id(unsigned char *buf, char *comment)
{
int i;
char pnp[8], rev[9];
for (i = 0; i < 7; i++) /* ASCII PnP ID */
pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1);
for (i = 0; i < 8; i++) /* ASCII firmware revision */
rev[i] = sw_get_bits(buf, 88+8*i, 8, 1);
pnp[7] = rev[8] = 0;
sprintf(comment, " [PnP %d.%02d id %s rev %s]",
(int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */
sw_get_bits(buf, 16, 6, 1)) / 100,
(int) ((sw_get_bits(buf, 8, 6, 1) << 6) |
sw_get_bits(buf, 16, 6, 1)) % 100,
pnp, rev);
}
/*
* sw_guess_mode() checks the upper two button bits for toggling -
* indication of that the joystick is in 3-bit mode. This is documented
* behavior for 3DP ID packet, and for example the FSP does this in
* normal packets instead. Fun ...
*/
static int sw_guess_mode(unsigned char *buf, int len)
{
int i;
unsigned char xor = 0;
for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6;
return !!xor * 2 + 1;
}
/*
* sw_connect() probes for SideWinder type joysticks.
*/
static void sw_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct sw *sw;
int i, j, k, l;
unsigned char *buf = NULL; /* [SW_LENGTH] */
unsigned char *idbuf = NULL; /* [SW_LENGTH] */
unsigned char m = 1;
char comment[40];
comment[0] = 0;
if (!(sw = kmalloc(sizeof(struct sw), GFP_KERNEL))) return;
memset(sw, 0, sizeof(struct sw));
buf = kmalloc(SW_LENGTH, GFP_KERNEL);
idbuf = kmalloc(SW_LENGTH, GFP_KERNEL);
if (!buf || !idbuf)
goto fail1;
gameport->private = sw;
sw->gameport = gameport;
init_timer(&sw->timer);
sw->timer.data = (long) sw;
sw->timer.function = sw_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
dbg("Init 0: Opened %s, io %#x, speed %d",
gameport->phys, gameport->io, gameport->speed);
i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */
udelay(SW_TIMEOUT);
dbg("Init 1: Mode %d. Length %d.", m , i);
if (!i) { /* No data. 3d Pro analog mode? */
sw_init_digital(gameport); /* Switch to digital */
udelay(SW_TIMEOUT);
i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */
udelay(SW_TIMEOUT);
dbg("Init 1b: Length %d.", i);
if (!i) goto fail2; /* No data -> FAIL */
}
j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Read ID. This initializes the stick */
m |= sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */
dbg("Init 2: Mode %d. ID Length %d.", m , j);
if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */
udelay(SW_TIMEOUT);
i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */
dbg("Init 2b: Mode %d. Length %d.", m, i);
if (!i) goto fail2;
udelay(SW_TIMEOUT);
j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Retry reading ID */
dbg("Init 2c: ID Length %d.", j);
}
sw->type = -1;
k = SW_FAIL; /* Try SW_FAIL times */
l = 0;
do {
k--;
udelay(SW_TIMEOUT);
i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read data packet */
dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k);
if (i > l) { /* Longer? As we can only lose bits, it makes */
/* no sense to try detection for a packet shorter */
l = i; /* than the previous one */
sw->number = 1;
sw->gameport = gameport;
sw->length = i;
sw->bits = m;
dbg("Init 3a: Case %d.\n", i * m);
switch (i * m) {
case 60:
sw->number++;
case 45: /* Ambiguous packet length */
if (j <= 40) { /* ID length less or eq 40 -> FSP */
case 43:
sw->type = SW_ID_FSP;
break;
}
sw->number++;
case 30:
sw->number++;
case 15:
sw->type = SW_ID_GP;
break;
case 33:
case 31:
sw->type = SW_ID_FFW;
break;
case 48: /* Ambiguous */
if (j == 14) { /* ID length 14*3 -> FFP */
sw->type = SW_ID_FFP;
sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on");
} else
sw->type = SW_ID_PP;
break;
case 66:
sw->bits = 3;
case 198:
sw->length = 22;
case 64:
sw->type = SW_ID_3DP;
if (j == 160) sw_3dp_id(idbuf, comment);
break;
}
}
} while (k && (sw->type == -1));
if (sw->type == -1) {
printk(KERN_WARNING "sidewinder.c: unknown joystick device detected "
"on %s, contact <vojtech@ucw.cz>\n", gameport->phys);
sw_print_packet("ID", j * 3, idbuf, 3);
sw_print_packet("Data", i * m, buf, m);
goto fail2;
}
#ifdef SW_DEBUG
sw_print_packet("ID", j * 3, idbuf, 3);
sw_print_packet("Data", i * m, buf, m);
#endif
k = i;
l = j;
for (i = 0; i < sw->number; i++) {
int bits, code;
sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]);
sprintf(sw->phys[i], "%s/input%d", gameport->phys, i);
sw->dev[i].private = sw;
sw->dev[i].open = sw_open;
sw->dev[i].close = sw_close;
sw->dev[i].name = sw->name;
sw->dev[i].phys = sw->phys[i];
sw->dev[i].id.bustype = BUS_GAMEPORT;
sw->dev[i].id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
sw->dev[i].id.product = sw->type;
sw->dev[i].id.version = 0x0100;
sw->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
code = sw_abs[sw->type][j];
set_bit(code, sw->dev[i].absbit);
sw->dev[i].absmax[code] = (1 << bits) - 1;
sw->dev[i].absmin[code] = (bits == 1) ? -1 : 0;
sw->dev[i].absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0;
if (code != ABS_THROTTLE)
sw->dev[i].absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0;
}
for (j = 0; (code = sw_btn[sw->type][j]); j++)
set_bit(code, sw->dev[i].keybit);
input_register_device(sw->dev + i);
printk(KERN_INFO "input: %s%s on %s [%d-bit id %d data %d]\n",
sw->name, comment, gameport->phys, m, l, k);
}
return;
fail2: gameport_close(gameport);
fail1: kfree(sw);
kfree(buf);
kfree(idbuf);
}
static void sw_disconnect(struct gameport *gameport)
{
int i;
struct sw *sw = gameport->private;
for (i = 0; i < sw->number; i++)
input_unregister_device(sw->dev + i);
gameport_close(gameport);
kfree(sw);
}
static struct gameport_dev sw_dev = {
.connect = sw_connect,
.disconnect = sw_disconnect,
};
int __init sw_init(void)
{
gameport_register_device(&sw_dev);
return 0;
}
void __exit sw_exit(void)
{
gameport_unregister_device(&sw_dev);
}
module_init(sw_init);
module_exit(sw_exit);

View File

@@ -0,0 +1,300 @@
/*
* $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* David Thompson
* Joseph Krahn
*/
/*
* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define SPACEBALL_MAX_LENGTH 128
#define SPACEBALL_MAX_ID 8
#define SPACEBALL_1003 1
#define SPACEBALL_2003B 3
#define SPACEBALL_2003C 4
#define SPACEBALL_3003C 7
#define SPACEBALL_4000FLX 8
#define SPACEBALL_4000FLX_L 9
static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };
static char *spaceball_names[] = {
"?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B",
"SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController",
"SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" };
/*
* Per-Ball data.
*/
struct spaceball {
struct input_dev dev;
struct serio *serio;
int idx;
int escape;
unsigned char data[SPACEBALL_MAX_LENGTH];
char phys[32];
};
/*
* spaceball_process_packet() decodes packets the driver receives from the
* SpaceBall.
*/
static void spaceball_process_packet(struct spaceball* spaceball, struct pt_regs *regs)
{
struct input_dev *dev = &spaceball->dev;
unsigned char *data = spaceball->data;
int i;
if (spaceball->idx < 2) return;
input_regs(dev, regs);
switch (spaceball->data[0]) {
case 'D': /* Ball data */
if (spaceball->idx != 15) return;
for (i = 0; i < 6; i++)
input_report_abs(dev, spaceball_axes[i],
(__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));
break;
case 'K': /* Button data */
if (spaceball->idx != 3) return;
input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20));
input_report_key(dev, BTN_2, data[2] & 0x02);
input_report_key(dev, BTN_3, data[2] & 0x04);
input_report_key(dev, BTN_4, data[2] & 0x08);
input_report_key(dev, BTN_5, data[1] & 0x01);
input_report_key(dev, BTN_6, data[1] & 0x02);
input_report_key(dev, BTN_7, data[1] & 0x04);
input_report_key(dev, BTN_8, data[1] & 0x10);
break;
case '.': /* Advanced button data */
if (spaceball->idx != 3) return;
input_report_key(dev, BTN_1, data[2] & 0x01);
input_report_key(dev, BTN_2, data[2] & 0x02);
input_report_key(dev, BTN_3, data[2] & 0x04);
input_report_key(dev, BTN_4, data[2] & 0x08);
input_report_key(dev, BTN_5, data[2] & 0x10);
input_report_key(dev, BTN_6, data[2] & 0x20);
input_report_key(dev, BTN_7, data[2] & 0x80);
input_report_key(dev, BTN_8, data[1] & 0x01);
input_report_key(dev, BTN_9, data[1] & 0x02);
input_report_key(dev, BTN_A, data[1] & 0x04);
input_report_key(dev, BTN_B, data[1] & 0x08);
input_report_key(dev, BTN_C, data[1] & 0x10);
input_report_key(dev, BTN_MODE, data[1] & 0x20);
break;
case 'E': /* Device error */
spaceball->data[spaceball->idx - 1] = 0;
printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
break;
case '?': /* Bad command packet */
spaceball->data[spaceball->idx - 1] = 0;
printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);
break;
}
input_sync(dev);
}
/*
* Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,
* and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which
* can occur in the axis values.
*/
static irqreturn_t spaceball_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct spaceball *spaceball = serio->private;
switch (data) {
case 0xd:
spaceball_process_packet(spaceball, regs);
spaceball->idx = 0;
spaceball->escape = 0;
break;
case '^':
if (!spaceball->escape) {
spaceball->escape = 1;
break;
}
spaceball->escape = 0;
case 'M':
case 'Q':
case 'S':
if (spaceball->escape) {
spaceball->escape = 0;
data &= 0x1f;
}
default:
if (spaceball->escape)
spaceball->escape = 0;
if (spaceball->idx < SPACEBALL_MAX_LENGTH)
spaceball->data[spaceball->idx++] = data;
break;
}
return IRQ_HANDLED;
}
/*
* spaceball_disconnect() is the opposite of spaceball_connect()
*/
static void spaceball_disconnect(struct serio *serio)
{
struct spaceball* spaceball = serio->private;
input_unregister_device(&spaceball->dev);
serio_close(serio);
kfree(spaceball);
}
/*
* spaceball_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Magellan, and if found, registers
* it as an input device.
*/
static void spaceball_connect(struct serio *serio, struct serio_driver *drv)
{
struct spaceball *spaceball;
int i, t, id;
if ((serio->type & ~SERIO_ID) != (SERIO_RS232 | SERIO_SPACEBALL))
return;
if ((id = (serio->type & SERIO_ID) >> 8) > SPACEBALL_MAX_ID)
return;
if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL)))
return;
memset(spaceball, 0, sizeof(struct spaceball));
spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
switch (id) {
case SPACEBALL_4000FLX:
case SPACEBALL_4000FLX_L:
spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_9);
spaceball->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_MODE);
default:
spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4)
| BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7) | BIT(BTN_8);
case SPACEBALL_3003C:
spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_1) | BIT(BTN_8);
}
for (i = 0; i < 6; i++) {
t = spaceball_axes[i];
set_bit(t, spaceball->dev.absbit);
spaceball->dev.absmin[t] = i < 3 ? -8000 : -1600;
spaceball->dev.absmax[t] = i < 3 ? 8000 : 1600;
spaceball->dev.absflat[t] = i < 3 ? 40 : 8;
spaceball->dev.absfuzz[t] = i < 3 ? 8 : 2;
}
spaceball->serio = serio;
spaceball->dev.private = spaceball;
sprintf(spaceball->phys, "%s/input0", serio->phys);
init_input_dev(&spaceball->dev);
spaceball->dev.name = spaceball_names[id];
spaceball->dev.phys = spaceball->phys;
spaceball->dev.id.bustype = BUS_RS232;
spaceball->dev.id.vendor = SERIO_SPACEBALL;
spaceball->dev.id.product = id;
spaceball->dev.id.version = 0x0100;
serio->private = spaceball;
if (serio_open(serio, drv)) {
kfree(spaceball);
return;
}
input_register_device(&spaceball->dev);
printk(KERN_INFO "input: %s on serio%s\n",
spaceball_names[id], serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_driver spaceball_drv = {
.driver = {
.name = "spaceball",
},
.description = DRIVER_DESC,
.interrupt = spaceball_interrupt,
.connect = spaceball_connect,
.disconnect = spaceball_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init spaceball_init(void)
{
serio_register_driver(&spaceball_drv);
return 0;
}
void __exit spaceball_exit(void)
{
serio_unregister_driver(&spaceball_drv);
}
module_init(spaceball_init);
module_exit(spaceball_exit);

View File

@@ -0,0 +1,244 @@
/*
* $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* David Thompson
*/
/*
* SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#define DRIVER_DESC "SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define SPACEORB_MAX_LENGTH 64
static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A };
static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
static char *spaceorb_name = "SpaceTec SpaceOrb 360 / Avenger";
/*
* Per-Orb data.
*/
struct spaceorb {
struct input_dev dev;
struct serio *serio;
int idx;
unsigned char data[SPACEORB_MAX_LENGTH];
char phys[32];
};
static unsigned char spaceorb_xor[] = "SpaceWare";
static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout",
"Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" };
/*
* spaceorb_process_packet() decodes packets the driver receives from the
* SpaceOrb.
*/
static void spaceorb_process_packet(struct spaceorb *spaceorb, struct pt_regs *regs)
{
struct input_dev *dev = &spaceorb->dev;
unsigned char *data = spaceorb->data;
unsigned char c = 0;
int axes[6];
int i;
if (spaceorb->idx < 2) return;
for (i = 0; i < spaceorb->idx; i++) c ^= data[i];
if (c) return;
input_regs(dev, regs);
switch (data[0]) {
case 'R': /* Reset packet */
spaceorb->data[spaceorb->idx - 1] = 0;
for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++);
printk(KERN_INFO "input: %s [%s] on %s\n",
spaceorb_name, spaceorb->data + i, spaceorb->serio->phys);
break;
case 'D': /* Ball + button data */
if (spaceorb->idx != 12) return;
for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i];
axes[0] = ( data[2] << 3) | (data[ 3] >> 4);
axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1);
axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5);
axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2);
axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6);
axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3);
for (i = 0; i < 6; i++)
input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0));
for (i = 0; i < 6; i++)
input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1);
break;
case 'K': /* Button data */
if (spaceorb->idx != 5) return;
for (i = 0; i < 7; i++)
input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1);
break;
case 'E': /* Error packet */
if (spaceorb->idx != 4) return;
printk(KERN_ERR "joy-spaceorb: Device error. [ ");
for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]);
printk("]\n");
break;
}
input_sync(dev);
}
static irqreturn_t spaceorb_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct spaceorb* spaceorb = serio->private;
if (~data & 0x80) {
if (spaceorb->idx) spaceorb_process_packet(spaceorb, regs);
spaceorb->idx = 0;
}
if (spaceorb->idx < SPACEORB_MAX_LENGTH)
spaceorb->data[spaceorb->idx++] = data & 0x7f;
return IRQ_HANDLED;
}
/*
* spaceorb_disconnect() is the opposite of spaceorb_connect()
*/
static void spaceorb_disconnect(struct serio *serio)
{
struct spaceorb* spaceorb = serio->private;
input_unregister_device(&spaceorb->dev);
serio_close(serio);
kfree(spaceorb);
}
/*
* spaceorb_connect() is the routine that is called when someone adds a
* new serio device. It looks for the SpaceOrb/Avenger, and if found, registers
* it as an input device.
*/
static void spaceorb_connect(struct serio *serio, struct serio_driver *drv)
{
struct spaceorb *spaceorb;
int i, t;
if (serio->type != (SERIO_RS232 | SERIO_SPACEORB))
return;
if (!(spaceorb = kmalloc(sizeof(struct spaceorb), GFP_KERNEL)))
return;
memset(spaceorb, 0, sizeof(struct spaceorb));
spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < 6; i++)
set_bit(spaceorb_buttons[i], spaceorb->dev.keybit);
for (i = 0; i < 6; i++) {
t = spaceorb_axes[i];
set_bit(t, spaceorb->dev.absbit);
spaceorb->dev.absmin[t] = -508;
spaceorb->dev.absmax[t] = 508;
}
spaceorb->serio = serio;
spaceorb->dev.private = spaceorb;
sprintf(spaceorb->phys, "%s/input0", serio->phys);
init_input_dev(&spaceorb->dev);
spaceorb->dev.name = spaceorb_name;
spaceorb->dev.phys = spaceorb->phys;
spaceorb->dev.id.bustype = BUS_RS232;
spaceorb->dev.id.vendor = SERIO_SPACEORB;
spaceorb->dev.id.product = 0x0001;
spaceorb->dev.id.version = 0x0100;
serio->private = spaceorb;
if (serio_open(serio, drv)) {
kfree(spaceorb);
return;
}
input_register_device(&spaceorb->dev);
}
/*
* The serio device structure.
*/
static struct serio_driver spaceorb_drv = {
.driver = {
.name = "spaceorb",
},
.description = DRIVER_DESC,
.interrupt = spaceorb_interrupt,
.connect = spaceorb_connect,
.disconnect = spaceorb_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init spaceorb_init(void)
{
serio_register_driver(&spaceorb_drv);
return 0;
}
void __exit spaceorb_exit(void)
{
serio_unregister_driver(&spaceorb_drv);
}
module_init(spaceorb_init);
module_exit(spaceorb_exit);

View File

@@ -0,0 +1,218 @@
/*
* $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2000 Mark Fletcher
*/
/*
* Gravis Stinger gamepad driver for Linux
*/
/*
* This program is free warftware; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Gravis Stinger gamepad driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define STINGER_MAX_LENGTH 8
static char *stinger_name = "Gravis Stinger";
/*
* Per-Stinger data.
*/
struct stinger {
struct input_dev dev;
int idx;
unsigned char data[STINGER_MAX_LENGTH];
char phys[32];
};
/*
* stinger_process_packet() decodes packets the driver receives from the
* Stinger. It updates the data accordingly.
*/
static void stinger_process_packet(struct stinger *stinger, struct pt_regs *regs)
{
struct input_dev *dev = &stinger->dev;
unsigned char *data = stinger->data;
if (!stinger->idx) return;
input_regs(dev, regs);
input_report_key(dev, BTN_A, ((data[0] & 0x20) >> 5));
input_report_key(dev, BTN_B, ((data[0] & 0x10) >> 4));
input_report_key(dev, BTN_C, ((data[0] & 0x08) >> 3));
input_report_key(dev, BTN_X, ((data[0] & 0x04) >> 2));
input_report_key(dev, BTN_Y, ((data[3] & 0x20) >> 5));
input_report_key(dev, BTN_Z, ((data[3] & 0x10) >> 4));
input_report_key(dev, BTN_TL, ((data[3] & 0x08) >> 3));
input_report_key(dev, BTN_TR, ((data[3] & 0x04) >> 2));
input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1));
input_report_key(dev, BTN_START, (data[3] & 0x01));
input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6));
input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F));
input_sync(dev);
return;
}
/*
* stinger_interrupt() is called by the low level driver when characters
* are ready for us. We then buffer them for further processing, or call the
* packet processing routine.
*/
static irqreturn_t stinger_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct stinger* stinger = serio->private;
/* All Stinger packets are 4 bytes */
if (stinger->idx < STINGER_MAX_LENGTH)
stinger->data[stinger->idx++] = data;
if (stinger->idx == 4) {
stinger_process_packet(stinger, regs);
stinger->idx = 0;
}
return IRQ_HANDLED;
}
/*
* stinger_disconnect() is the opposite of stinger_connect()
*/
static void stinger_disconnect(struct serio *serio)
{
struct stinger* stinger = serio->private;
input_unregister_device(&stinger->dev);
serio_close(serio);
kfree(stinger);
}
/*
* stinger_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Stinger, and if found, registers
* it as an input device.
*/
static void stinger_connect(struct serio *serio, struct serio_driver *drv)
{
struct stinger *stinger;
int i;
if (serio->type != (SERIO_RS232 | SERIO_STINGER))
return;
if (!(stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL)))
return;
memset(stinger, 0, sizeof(struct stinger));
stinger->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
stinger->dev.keybit[LONG(BTN_A)] = BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_X) | \
BIT(BTN_Y) | BIT(BTN_Z) | BIT(BTN_TL) | BIT(BTN_TR) | \
BIT(BTN_START) | BIT(BTN_SELECT);
stinger->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
sprintf(stinger->phys, "%s/serio0", serio->phys);
init_input_dev(&stinger->dev);
stinger->dev.name = stinger_name;
stinger->dev.phys = stinger->phys;
stinger->dev.id.bustype = BUS_RS232;
stinger->dev.id.vendor = SERIO_STINGER;
stinger->dev.id.product = 0x0001;
stinger->dev.id.version = 0x0100;
for (i = 0; i < 2; i++) {
stinger->dev.absmax[ABS_X+i] = 64;
stinger->dev.absmin[ABS_X+i] = -64;
stinger->dev.absflat[ABS_X+i] = 4;
}
stinger->dev.private = stinger;
serio->private = stinger;
if (serio_open(serio, drv)) {
kfree(stinger);
return;
}
input_register_device(&stinger->dev);
printk(KERN_INFO "input: %s on %s\n", stinger_name, serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_driver stinger_drv = {
.driver = {
.name = "stinger",
},
.description = DRIVER_DESC,
.interrupt = stinger_interrupt,
.connect = stinger_connect,
.disconnect = stinger_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init stinger_init(void)
{
serio_register_driver(&stinger_drv);
return 0;
}
void __exit stinger_exit(void)
{
serio_unregister_driver(&stinger_drv);
}
module_init(stinger_init);
module_exit(stinger_exit);

View File

@@ -0,0 +1,382 @@
/*
* $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*
* Based on the work of:
* Trystan Larey-Williams
*/
/*
* ThrustMaster DirectConnect (BSP) joystick family driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("ThrustMaster DirectConnect joystick driver");
MODULE_LICENSE("GPL");
#define TMDC_MAX_START 400 /* 400 us */
#define TMDC_MAX_STROBE 45 /* 45 us */
#define TMDC_MAX_LENGTH 13
#define TMDC_REFRESH_TIME HZ/50 /* 20 ms */
#define TMDC_MODE_M3DI 1
#define TMDC_MODE_3DRP 3
#define TMDC_MODE_AT 4
#define TMDC_MODE_FM 8
#define TMDC_MODE_FGP 163
#define TMDC_BYTE_ID 10
#define TMDC_BYTE_REV 11
#define TMDC_BYTE_DEF 12
#define TMDC_ABS 7
#define TMDC_ABS_HAT 4
#define TMDC_BTN 16
static unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 };
static unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 };
static signed char tmdc_abs[TMDC_ABS] =
{ ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ };
static signed char tmdc_abs_hat[TMDC_ABS_HAT] =
{ ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
static signed char tmdc_abs_at[TMDC_ABS] =
{ ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE };
static signed char tmdc_abs_fm[TMDC_ABS] =
{ ABS_RX, ABS_RY, ABS_X, ABS_Y };
static short tmdc_btn_pad[TMDC_BTN] =
{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR };
static short tmdc_btn_joy[TMDC_BTN] =
{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE,
BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z };
static short tmdc_btn_fm[TMDC_BTN] =
{ BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 };
static short tmdc_btn_at[TMDC_BTN] =
{ BTN_TRIGGER, BTN_THUMB2, BTN_PINKIE, BTN_THUMB, BTN_BASE6, BTN_BASE5, BTN_BASE4,
BTN_BASE3, BTN_BASE2, BTN_BASE };
static struct {
int x;
int y;
} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}};
struct tmdc {
struct gameport *gameport;
struct timer_list timer;
struct input_dev dev[2];
char name[2][64];
char phys[2][32];
int mode[2];
signed char *abs[2];
short *btn[2];
unsigned char absc[2];
unsigned char btnc[2][4];
unsigned char btno[2][4];
int used;
int reads;
int bads;
unsigned char exists;
};
/*
* tmdc_read_packet() reads a ThrustMaster packet.
*/
static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH])
{
unsigned char u, v, w, x;
unsigned long flags;
int i[2], j[2], t[2], p, k;
p = gameport_time(gameport, TMDC_MAX_STROBE);
for (k = 0; k < 2; k++) {
t[k] = gameport_time(gameport, TMDC_MAX_START);
i[k] = j[k] = 0;
}
local_irq_save(flags);
gameport_trigger(gameport);
w = gameport_read(gameport) >> 4;
do {
x = w;
w = gameport_read(gameport) >> 4;
for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) {
if (~v & u & 2) {
if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue;
t[k] = p;
if (j[k] == 0) { /* Start bit */
if (~v & 1) t[k] = 0;
data[k][i[k]] = 0; j[k]++; continue;
}
if (j[k] == 9) { /* Stop bit */
if (v & 1) t[k] = 0;
j[k] = 0; i[k]++; continue;
}
data[k][i[k]] |= (~v & 1) << (j[k]++ - 1); /* Data bit */
}
t[k]--;
}
} while (t[0] > 0 || t[1] > 0);
local_irq_restore(flags);
return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1);
}
/*
* tmdc_read() reads and analyzes ThrustMaster joystick data.
*/
static void tmdc_timer(unsigned long private)
{
unsigned char data[2][TMDC_MAX_LENGTH];
struct tmdc *tmdc = (void *) private;
struct input_dev *dev;
unsigned char r, bad = 0;
int i, j, k, l;
tmdc->reads++;
if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists)
bad = 1;
else
for (j = 0; j < 2; j++)
if (r & (1 << j) & tmdc->exists) {
if (data[j][TMDC_BYTE_ID] != tmdc->mode[j]) {
bad = 1;
continue;
}
dev = tmdc->dev + j;
for (i = 0; i < tmdc->absc[j]; i++) {
if (tmdc->abs[j][i] < 0) continue;
input_report_abs(dev, tmdc->abs[j][i], data[j][tmdc_byte_a[i]]);
}
switch (tmdc->mode[j]) {
case TMDC_MODE_M3DI:
i = tmdc_byte_d[0];
input_report_abs(dev, ABS_HAT0X, ((data[j][i] >> 3) & 1) - ((data[j][i] >> 1) & 1));
input_report_abs(dev, ABS_HAT0Y, ((data[j][i] >> 2) & 1) - ( data[j][i] & 1));
break;
case TMDC_MODE_AT:
i = tmdc_byte_a[3];
input_report_abs(dev, ABS_HAT0X, tmdc_hat_to_axis[(data[j][i] - 141) / 25].x);
input_report_abs(dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[j][i] - 141) / 25].y);
break;
}
for (k = l = 0; k < 4; k++) {
for (i = 0; i < tmdc->btnc[j][k]; i++)
input_report_key(dev, tmdc->btn[j][i + l],
((data[j][tmdc_byte_d[k]] >> (i + tmdc->btno[j][k])) & 1));
l += tmdc->btnc[j][k];
}
input_sync(dev);
}
tmdc->bads += bad;
mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME);
}
static int tmdc_open(struct input_dev *dev)
{
struct tmdc *tmdc = dev->private;
if (!tmdc->used++)
mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME);
return 0;
}
static void tmdc_close(struct input_dev *dev)
{
struct tmdc *tmdc = dev->private;
if (!--tmdc->used)
del_timer(&tmdc->timer);
}
/*
* tmdc_probe() probes for ThrustMaster type joysticks.
*/
static void tmdc_connect(struct gameport *gameport, struct gameport_dev *dev)
{
struct models {
unsigned char id;
char *name;
char abs;
char hats;
char btnc[4];
char btno[4];
signed char *axes;
short *buttons;
} models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
{ 3, "ThrustMaster Rage 3D Gamepad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
{ 4, "ThrustMaster Attack Throttle", 5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at },
{ 8, "ThrustMaster FragMaster", 4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm },
{ 163, "Thrustmaster Fusion GamePad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
{ 0, "Unknown %d-axis, %d-button TM device %d", 0, 0, { 0, 0 }, { 0, 0 }, tmdc_abs, tmdc_btn_joy }};
unsigned char data[2][TMDC_MAX_LENGTH];
struct tmdc *tmdc;
int i, j, k, l, m;
if (!(tmdc = kmalloc(sizeof(struct tmdc), GFP_KERNEL)))
return;
memset(tmdc, 0, sizeof(struct tmdc));
gameport->private = tmdc;
tmdc->gameport = gameport;
init_timer(&tmdc->timer);
tmdc->timer.data = (long) tmdc;
tmdc->timer.function = tmdc_timer;
if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
goto fail1;
if (!(tmdc->exists = tmdc_read_packet(gameport, data)))
goto fail2;
for (j = 0; j < 2; j++)
if (tmdc->exists & (1 << j)) {
tmdc->mode[j] = data[j][TMDC_BYTE_ID];
for (m = 0; models[m].id && models[m].id != tmdc->mode[j]; m++);
tmdc->abs[j] = models[m].axes;
tmdc->btn[j] = models[m].buttons;
if (!models[m].id) {
models[m].abs = data[j][TMDC_BYTE_DEF] >> 4;
for (k = 0; k < 4; k++)
models[m].btnc[k] = k < (data[j][TMDC_BYTE_DEF] & 0xf) ? 8 : 0;
}
tmdc->absc[j] = models[m].abs;
for (k = 0; k < 4; k++) {
tmdc->btnc[j][k] = models[m].btnc[k];
tmdc->btno[j][k] = models[m].btno[k];
}
sprintf(tmdc->name[j], models[m].name, models[m].abs,
(data[j][TMDC_BYTE_DEF] & 0xf) << 3, tmdc->mode[j]);
sprintf(tmdc->phys[j], "%s/input%d", gameport->phys, j);
tmdc->dev[j].private = tmdc;
tmdc->dev[j].open = tmdc_open;
tmdc->dev[j].close = tmdc_close;
tmdc->dev[j].name = tmdc->name[j];
tmdc->dev[j].phys = tmdc->phys[j];
tmdc->dev[j].id.bustype = BUS_GAMEPORT;
tmdc->dev[j].id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
tmdc->dev[j].id.product = models[m].id;
tmdc->dev[j].id.version = 0x0100;
tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) {
if (tmdc->abs[j][i] < 0) continue;
set_bit(tmdc->abs[j][i], tmdc->dev[j].absbit);
tmdc->dev[j].absmin[tmdc->abs[j][i]] = 8;
tmdc->dev[j].absmax[tmdc->abs[j][i]] = 248;
tmdc->dev[j].absfuzz[tmdc->abs[j][i]] = 2;
tmdc->dev[j].absflat[tmdc->abs[j][i]] = 4;
}
for (i = 0; i < models[m].hats && i < TMDC_ABS_HAT; i++) {
set_bit(tmdc_abs_hat[i], tmdc->dev[j].absbit);
tmdc->dev[j].absmin[tmdc_abs_hat[i]] = -1;
tmdc->dev[j].absmax[tmdc_abs_hat[i]] = 1;
}
for (k = l = 0; k < 4; k++) {
for (i = 0; i < models[m].btnc[k] && i < TMDC_BTN; i++)
set_bit(tmdc->btn[j][i + l], tmdc->dev[j].keybit);
l += models[m].btnc[k];
}
input_register_device(tmdc->dev + j);
printk(KERN_INFO "input: %s on %s\n", tmdc->name[j], gameport->phys);
}
return;
fail2: gameport_close(gameport);
fail1: kfree(tmdc);
}
static void tmdc_disconnect(struct gameport *gameport)
{
struct tmdc *tmdc = gameport->private;
int i;
for (i = 0; i < 2; i++)
if (tmdc->exists & (1 << i))
input_unregister_device(tmdc->dev + i);
gameport_close(gameport);
kfree(tmdc);
}
static struct gameport_dev tmdc_dev = {
.connect = tmdc_connect,
.disconnect = tmdc_disconnect,
};
int __init tmdc_init(void)
{
gameport_register_device(&tmdc_dev);
return 0;
}
void __exit tmdc_exit(void)
{
gameport_unregister_device(&tmdc_dev);
}
module_init(tmdc_init);
module_exit(tmdc_exit);

View File

@@ -0,0 +1,258 @@
/*
* $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $
*
* Copyright (c) 1998-2001 Vojtech Pavlik
*
* Based on the work of:
* Steffen Schwenke
*/
/*
* TurboGraFX parallel port interface driver for Linux.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/parport.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
MODULE_LICENSE("GPL");
static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
static int tgfx_nargs __initdata = 0;
module_param_array_named(map, tgfx, int, &tgfx_nargs, 0);
MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
static int tgfx_nargs_2 __initdata = 0;
module_param_array_named(map2, tgfx_2, int, &tgfx_nargs_2, 0);
MODULE_PARM_DESC(map2, "Describes second set of devices");
static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
static int tgfx_nargs_3 __initdata = 0;
module_param_array_named(map3, tgfx_3, int, &tgfx_nargs_3, 0);
MODULE_PARM_DESC(map3, "Describes third set of devices");
__obsolete_setup("tgfx=");
__obsolete_setup("tgfx_2=");
__obsolete_setup("tgfx_3=");
#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */
#define TGFX_TRIGGER 0x08
#define TGFX_UP 0x10
#define TGFX_DOWN 0x20
#define TGFX_LEFT 0x40
#define TGFX_RIGHT 0x80
#define TGFX_THUMB 0x02
#define TGFX_THUMB2 0x04
#define TGFX_TOP 0x01
#define TGFX_TOP2 0x08
static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 };
static char *tgfx_name = "TurboGraFX Multisystem joystick";
struct tgfx {
struct pardevice *pd;
struct timer_list timer;
struct input_dev dev[7];
char phys[7][32];
int sticks;
int used;
} *tgfx_base[3];
/*
* tgfx_timer() reads and analyzes TurboGraFX joystick data.
*/
static void tgfx_timer(unsigned long private)
{
struct tgfx *tgfx = (void *) private;
struct input_dev *dev;
int data1, data2, i;
for (i = 0; i < 7; i++)
if (tgfx->sticks & (1 << i)) {
dev = tgfx->dev + i;
parport_write_data(tgfx->pd->port, ~(1 << i));
data1 = parport_read_status(tgfx->pd->port) ^ 0x7f;
data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */
input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT));
input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP ));
input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER));
input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB ));
input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 ));
input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP ));
input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 ));
input_sync(dev);
}
mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
}
static int tgfx_open(struct input_dev *dev)
{
struct tgfx *tgfx = dev->private;
if (!tgfx->used++) {
parport_claim(tgfx->pd);
parport_write_control(tgfx->pd->port, 0x04);
mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
}
return 0;
}
static void tgfx_close(struct input_dev *dev)
{
struct tgfx *tgfx = dev->private;
if (!--tgfx->used) {
del_timer(&tgfx->timer);
parport_write_control(tgfx->pd->port, 0x00);
parport_release(tgfx->pd);
}
}
/*
* tgfx_probe() probes for tg gamepads.
*/
static struct tgfx __init *tgfx_probe(int *config, int nargs)
{
struct tgfx *tgfx;
struct parport *pp;
int i, j;
if (config[0] < 0)
return NULL;
if (nargs < 2) {
printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
return NULL;
}
pp = parport_find_number(config[0]);
if (!pp) {
printk(KERN_ERR "turbografx.c: no such parport\n");
return NULL;
}
if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) {
parport_put_port(pp);
return NULL;
}
memset(tgfx, 0, sizeof(struct tgfx));
tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
parport_put_port(pp);
if (!tgfx->pd) {
printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n");
kfree(tgfx);
return NULL;
}
init_timer(&tgfx->timer);
tgfx->timer.data = (long) tgfx;
tgfx->timer.function = tgfx_timer;
tgfx->sticks = 0;
for (i = 0; i < nargs - 1; i++)
if (config[i+1] > 0 && config[i+1] < 6) {
tgfx->sticks |= (1 << i);
tgfx->dev[i].private = tgfx;
tgfx->dev[i].open = tgfx_open;
tgfx->dev[i].close = tgfx_close;
sprintf(tgfx->phys[i], "%s/input0", tgfx->pd->port->name);
tgfx->dev[i].name = tgfx_name;
tgfx->dev[i].phys = tgfx->phys[i];
tgfx->dev[i].id.bustype = BUS_PARPORT;
tgfx->dev[i].id.vendor = 0x0003;
tgfx->dev[i].id.product = config[i+1];
tgfx->dev[i].id.version = 0x0100;
tgfx->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
tgfx->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
for (j = 0; j < config[i+1]; j++)
set_bit(tgfx_buttons[j], tgfx->dev[i].keybit);
tgfx->dev[i].absmin[ABS_X] = -1; tgfx->dev[i].absmax[ABS_X] = 1;
tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1;
input_register_device(tgfx->dev + i);
printk(KERN_INFO "input: %d-button Multisystem joystick on %s\n",
config[i+1], tgfx->pd->port->name);
}
if (!tgfx->sticks) {
parport_unregister_device(tgfx->pd);
kfree(tgfx);
return NULL;
}
return tgfx;
}
int __init tgfx_init(void)
{
tgfx_base[0] = tgfx_probe(tgfx, tgfx_nargs);
tgfx_base[1] = tgfx_probe(tgfx_2, tgfx_nargs_2);
tgfx_base[2] = tgfx_probe(tgfx_3, tgfx_nargs_3);
if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2])
return 0;
return -ENODEV;
}
void __exit tgfx_exit(void)
{
int i, j;
for (i = 0; i < 3; i++)
if (tgfx_base[i]) {
for (j = 0; j < 7; j++)
if (tgfx_base[i]->sticks & (1 << j))
input_unregister_device(tgfx_base[i]->dev + j);
parport_unregister_device(tgfx_base[i]->pd);
}
}
module_init(tgfx_init);
module_exit(tgfx_exit);

View File

@@ -0,0 +1,275 @@
/*
* $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $
*
* derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp"
*
* Copyright (c) 2001 Arndt Schoenewald
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2000 Mark Fletcher
*
* Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
*/
/*
* Driver to use Handykey's Twiddler (the first edition, i.e. the one with
* the RS232 interface) as a joystick under Linux
*
* The Twiddler is a one-handed chording keyboard featuring twelve buttons on
* the front, six buttons on the top, and a built-in tilt sensor. The buttons
* on the front, which are grouped as four rows of three buttons, are pressed
* by the four fingers (this implies only one button per row can be held down
* at the same time) and the buttons on the top are for the thumb. The tilt
* sensor delivers X and Y axis data depending on how the Twiddler is held.
* Additional information can be found at http://www.handykey.com.
*
* This driver does not use the Twiddler for its intended purpose, i.e. as
* a chording keyboard, but as a joystick: pressing and releasing a button
* immediately sends a corresponding button event, and tilting it generates
* corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
* controller with amazing 18 buttons :-)
*
* Note: The Twiddler2 (the successor of the Twiddler that connects directly
* to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
*
* For questions or feedback regarding this driver module please contact:
* Arndt Schoenewald <arndt@quelltext.com>
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
MODULE_DESCRIPTION("Handykey Twiddler keyboard as a joystick driver");
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define TWIDJOY_MAX_LENGTH 5
static char *twidjoy_name = "Handykey Twiddler";
static struct twidjoy_button_spec {
int bitshift;
int bitmask;
int buttons[3];
}
twidjoy_buttons[] = {
{ 0, 3, { BTN_A, BTN_B, BTN_C } },
{ 2, 3, { BTN_X, BTN_Y, BTN_Z } },
{ 4, 3, { BTN_TL, BTN_TR, BTN_TR2 } },
{ 6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
{ 8, 1, { BTN_BASE5 } },
{ 9, 1, { BTN_BASE } },
{ 10, 1, { BTN_BASE3 } },
{ 11, 1, { BTN_BASE4 } },
{ 12, 1, { BTN_BASE2 } },
{ 13, 1, { BTN_BASE6 } },
{ 0, 0, { 0 } }
};
/*
* Per-Twiddler data.
*/
struct twidjoy {
struct input_dev dev;
int idx;
unsigned char data[TWIDJOY_MAX_LENGTH];
char phys[32];
};
/*
* twidjoy_process_packet() decodes packets the driver receives from the
* Twiddler. It updates the data accordingly.
*/
static void twidjoy_process_packet(struct twidjoy *twidjoy, struct pt_regs *regs)
{
if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
struct input_dev *dev = &twidjoy->dev;
unsigned char *data = twidjoy->data;
struct twidjoy_button_spec *bp;
int button_bits, abs_x, abs_y;
button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
input_regs(dev, regs);
for (bp = twidjoy_buttons; bp->bitmask; bp++) {
int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift;
int i;
for (i = 0; i < bp->bitmask; i++)
input_report_key(dev, bp->buttons[i], i+1 == value);
}
abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2);
if (data[4] & 0x08) abs_x -= 256;
abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0);
if (data[3] & 0x02) abs_y -= 256;
input_report_abs(dev, ABS_X, -abs_x);
input_report_abs(dev, ABS_Y, +abs_y);
input_sync(dev);
}
return;
}
/*
* twidjoy_interrupt() is called by the low level driver when characters
* are ready for us. We then buffer them for further processing, or call the
* packet processing routine.
*/
static irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct twidjoy *twidjoy = serio->private;
/* All Twiddler packets are 5 bytes. The fact that the first byte
* has a MSB of 0 and all other bytes have a MSB of 1 can be used
* to check and regain sync. */
if ((data & 0x80) == 0)
twidjoy->idx = 0; /* this byte starts a new packet */
else if (twidjoy->idx == 0)
return IRQ_HANDLED; /* wrong MSB -- ignore this byte */
if (twidjoy->idx < TWIDJOY_MAX_LENGTH)
twidjoy->data[twidjoy->idx++] = data;
if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
twidjoy_process_packet(twidjoy, regs);
twidjoy->idx = 0;
}
return IRQ_HANDLED;
}
/*
* twidjoy_disconnect() is the opposite of twidjoy_connect()
*/
static void twidjoy_disconnect(struct serio *serio)
{
struct twidjoy *twidjoy = serio->private;
input_unregister_device(&twidjoy->dev);
serio_close(serio);
kfree(twidjoy);
}
/*
* twidjoy_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Twiddler, and if found, registers
* it as an input device.
*/
static void twidjoy_connect(struct serio *serio, struct serio_driver *drv)
{
struct twidjoy_button_spec *bp;
struct twidjoy *twidjoy;
int i;
if (serio->type != (SERIO_RS232 | SERIO_TWIDJOY))
return;
if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL)))
return;
memset(twidjoy, 0, sizeof(struct twidjoy));
sprintf(twidjoy->phys, "%s/input0", serio->phys);
init_input_dev(&twidjoy->dev);
twidjoy->dev.name = twidjoy_name;
twidjoy->dev.phys = twidjoy->phys;
twidjoy->dev.id.bustype = BUS_RS232;
twidjoy->dev.id.vendor = SERIO_TWIDJOY;
twidjoy->dev.id.product = 0x0001;
twidjoy->dev.id.version = 0x0100;
twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (bp = twidjoy_buttons; bp->bitmask; bp++) {
for (i = 0; i < bp->bitmask; i++)
set_bit(bp->buttons[i], twidjoy->dev.keybit);
}
twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
for (i = 0; i < 2; i++) {
twidjoy->dev.absmax[ABS_X+i] = 50;
twidjoy->dev.absmin[ABS_X+i] = -50;
/* TODO: arndt 20010708: Are these values appropriate? */
twidjoy->dev.absfuzz[ABS_X+i] = 4;
twidjoy->dev.absflat[ABS_X+i] = 4;
}
twidjoy->dev.private = twidjoy;
serio->private = twidjoy;
if (serio_open(serio, drv)) {
kfree(twidjoy);
return;
}
input_register_device(&twidjoy->dev);
printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_driver twidjoy_drv = {
.driver = {
.name = "twidjoy",
},
.description = DRIVER_DESC,
.interrupt = twidjoy_interrupt,
.connect = twidjoy_connect,
.disconnect = twidjoy_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init twidjoy_init(void)
{
serio_register_driver(&twidjoy_drv);
return 0;
}
void __exit twidjoy_exit(void)
{
serio_unregister_driver(&twidjoy_drv);
}
module_init(twidjoy_init);
module_exit(twidjoy_exit);

View File

@@ -0,0 +1,230 @@
/*
* $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Logitech WingMan Warrior joystick driver for Linux
*/
/*
* This program is free warftware; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Logitech WingMan Warrior joystick driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Constants.
*/
#define WARRIOR_MAX_LENGTH 16
static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 };
static char *warrior_name = "Logitech WingMan Warrior";
/*
* Per-Warrior data.
*/
struct warrior {
struct input_dev dev;
int idx, len;
unsigned char data[WARRIOR_MAX_LENGTH];
char phys[32];
};
/*
* warrior_process_packet() decodes packets the driver receives from the
* Warrior. It updates the data accordingly.
*/
static void warrior_process_packet(struct warrior *warrior, struct pt_regs *regs)
{
struct input_dev *dev = &warrior->dev;
unsigned char *data = warrior->data;
if (!warrior->idx) return;
input_regs(dev, regs);
switch ((data[0] >> 4) & 7) {
case 1: /* Button data */
input_report_key(dev, BTN_TRIGGER, data[3] & 1);
input_report_key(dev, BTN_THUMB, (data[3] >> 1) & 1);
input_report_key(dev, BTN_TOP, (data[3] >> 2) & 1);
input_report_key(dev, BTN_TOP2, (data[3] >> 3) & 1);
break;
case 3: /* XY-axis info->data */
input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)));
input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
break;
case 5: /* Throttle, spinner, hat info->data */
input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0));
input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0));
input_report_rel(dev, REL_DIAL, (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5));
break;
}
input_sync(dev);
}
/*
* warrior_interrupt() is called by the low level driver when characters
* are ready for us. We then buffer them for further processing, or call the
* packet processing routine.
*/
static irqreturn_t warrior_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct warrior* warrior = serio->private;
if (data & 0x80) {
if (warrior->idx) warrior_process_packet(warrior, regs);
warrior->idx = 0;
warrior->len = warrior_lengths[(data >> 4) & 7];
}
if (warrior->idx < warrior->len)
warrior->data[warrior->idx++] = data;
if (warrior->idx == warrior->len) {
if (warrior->idx) warrior_process_packet(warrior, regs);
warrior->idx = 0;
warrior->len = 0;
}
return IRQ_HANDLED;
}
/*
* warrior_disconnect() is the opposite of warrior_connect()
*/
static void warrior_disconnect(struct serio *serio)
{
struct warrior* warrior = serio->private;
input_unregister_device(&warrior->dev);
serio_close(serio);
kfree(warrior);
}
/*
* warrior_connect() is the routine that is called when someone adds a
* new serio device. It looks for the Warrior, and if found, registers
* it as an input device.
*/
static void warrior_connect(struct serio *serio, struct serio_driver *drv)
{
struct warrior *warrior;
int i;
if (serio->type != (SERIO_RS232 | SERIO_WARRIOR))
return;
if (!(warrior = kmalloc(sizeof(struct warrior), GFP_KERNEL)))
return;
memset(warrior, 0, sizeof(struct warrior));
warrior->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
warrior->dev.keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2);
warrior->dev.relbit[0] = BIT(REL_DIAL);
warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y);
sprintf(warrior->phys, "%s/input0", serio->phys);
init_input_dev(&warrior->dev);
warrior->dev.name = warrior_name;
warrior->dev.phys = warrior->phys;
warrior->dev.id.bustype = BUS_RS232;
warrior->dev.id.vendor = SERIO_WARRIOR;
warrior->dev.id.product = 0x0001;
warrior->dev.id.version = 0x0100;
for (i = 0; i < 2; i++) {
warrior->dev.absmax[ABS_X+i] = -64;
warrior->dev.absmin[ABS_X+i] = 64;
warrior->dev.absflat[ABS_X+i] = 8;
}
warrior->dev.absmax[ABS_THROTTLE] = -112;
warrior->dev.absmin[ABS_THROTTLE] = 112;
for (i = 0; i < 2; i++) {
warrior->dev.absmax[ABS_HAT0X+i] = -1;
warrior->dev.absmin[ABS_HAT0X+i] = 1;
}
warrior->dev.private = warrior;
serio->private = warrior;
if (serio_open(serio, drv)) {
kfree(warrior);
return;
}
input_register_device(&warrior->dev);
printk(KERN_INFO "input: Logitech WingMan Warrior on %s\n", serio->phys);
}
/*
* The serio device structure.
*/
static struct serio_driver warrior_drv = {
.driver = {
.name = "warrior",
},
.description = DRIVER_DESC,
.interrupt = warrior_interrupt,
.connect = warrior_connect,
.disconnect = warrior_disconnect,
};
/*
* The functions for inserting/removing us as a module.
*/
int __init warrior_init(void)
{
serio_register_driver(&warrior_drv);
return 0;
}
void __exit warrior_exit(void)
{
serio_unregister_driver(&warrior_drv);
}
module_init(warrior_init);
module_exit(warrior_exit);

View File

@@ -0,0 +1,246 @@
cmd_drivers/input/keyboard/atkbd.o := gcc -Wp,-MD,drivers/input/keyboard/.atkbd.o.d -nostdinc -iwithprefix include -D__KERNEL__ -Iinclude -Wall -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -fno-unit-at-a-time -march=pentium-mmx -Iinclude/asm-i386/mach-default -Wdeclaration-after-statement -DKBUILD_BASENAME=atkbd -DKBUILD_MODNAME=atkbd -c -o drivers/input/keyboard/atkbd.o drivers/input/keyboard/atkbd.c
deps_drivers/input/keyboard/atkbd.o := \
drivers/input/keyboard/atkbd.c \
include/linux/delay.h \
include/asm/delay.h \
include/linux/module.h \
$(wildcard include/config/modules.h) \
$(wildcard include/config/modversions.h) \
$(wildcard include/config/module/unload.h) \
$(wildcard include/config/kallsyms.h) \
include/linux/config.h \
$(wildcard include/config/h.h) \
include/linux/sched.h \
$(wildcard include/config/keys.h) \
$(wildcard include/config/schedstats.h) \
$(wildcard include/config/smp.h) \
$(wildcard include/config/numa.h) \
$(wildcard include/config/security.h) \
$(wildcard include/config/preempt.h) \
$(wildcard include/config/magic/sysrq.h) \
include/asm/param.h \
include/linux/capability.h \
include/linux/types.h \
$(wildcard include/config/uid16.h) \
include/linux/posix_types.h \
include/linux/stddef.h \
include/linux/compiler.h \
include/linux/compiler-gcc3.h \
include/linux/compiler-gcc.h \
include/asm/posix_types.h \
include/asm/types.h \
$(wildcard include/config/highmem64g.h) \
$(wildcard include/config/lbd.h) \
include/linux/spinlock.h \
$(wildcard include/config/debug/spinlock.h) \
$(wildcard include/config/lockmeter.h) \
include/linux/preempt.h \
include/linux/linkage.h \
include/asm/linkage.h \
$(wildcard include/config/regparm.h) \
$(wildcard include/config/x86/alignment/16.h) \
include/linux/thread_info.h \
include/linux/bitops.h \
include/asm/bitops.h \
include/asm/thread_info.h \
$(wildcard include/config/4kstacks.h) \
$(wildcard include/config/debug/stack/usage.h) \
include/asm/page.h \
$(wildcard include/config/x86/use/3dnow.h) \
$(wildcard include/config/x86/pae.h) \
$(wildcard include/config/hugetlb/page.h) \
$(wildcard include/config/highmem4g.h) \
$(wildcard include/config/discontigmem.h) \
include/asm/processor.h \
$(wildcard include/config/mk8.h) \
$(wildcard include/config/mk7.h) \
include/asm/vm86.h \
include/asm/math_emu.h \
include/asm/sigcontext.h \
include/asm/segment.h \
include/asm/cpufeature.h \
include/asm/msr.h \
include/asm/system.h \
$(wildcard include/config/x86/cmpxchg.h) \
$(wildcard include/config/x86/oostore.h) \
include/linux/kernel.h \
$(wildcard include/config/debug/spinlock/sleep.h) \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.1/include/stdarg.h \
include/asm/byteorder.h \
$(wildcard include/config/x86/bswap.h) \
include/linux/byteorder/little_endian.h \
include/linux/byteorder/swab.h \
include/linux/byteorder/generic.h \
include/asm/bug.h \
include/asm-generic/bug.h \
include/linux/cache.h \
include/asm/cache.h \
$(wildcard include/config/x86/l1/cache/shift.h) \
include/linux/threads.h \
$(wildcard include/config/nr/cpus.h) \
include/asm/percpu.h \
include/asm-generic/percpu.h \
include/linux/stringify.h \
include/linux/timex.h \
$(wildcard include/config/time/interpolation.h) \
include/linux/time.h \
include/linux/seqlock.h \
include/asm/timex.h \
$(wildcard include/config/x86/elan.h) \
$(wildcard include/config/x86/tsc.h) \
$(wildcard include/config/x86/generic.h) \
include/linux/jiffies.h \
include/asm/div64.h \
include/linux/rbtree.h \
include/linux/cpumask.h \
$(wildcard include/config/hotplug/cpu.h) \
include/linux/bitmap.h \
include/linux/string.h \
include/asm/string.h \
include/asm/semaphore.h \
include/asm/atomic.h \
$(wildcard include/config/m386.h) \
include/linux/wait.h \
include/linux/list.h \
include/linux/prefetch.h \
include/asm/current.h \
include/linux/rwsem.h \
$(wildcard include/config/rwsem/generic/spinlock.h) \
include/asm/rwsem.h \
include/asm/ptrace.h \
$(wildcard include/config/frame/pointer.h) \
include/asm/mmu.h \
include/linux/smp.h \
include/linux/sem.h \
$(wildcard include/config/sysvipc.h) \
include/linux/ipc.h \
include/asm/ipcbuf.h \
include/asm/sembuf.h \
include/linux/signal.h \
include/asm/signal.h \
include/asm/siginfo.h \
include/asm-generic/siginfo.h \
include/linux/resource.h \
include/asm/resource.h \
include/linux/securebits.h \
include/linux/fs_struct.h \
include/linux/completion.h \
include/linux/pid.h \
include/linux/percpu.h \
include/linux/slab.h \
$(wildcard include/config/.h) \
include/linux/gfp.h \
include/linux/mmzone.h \
$(wildcard include/config/force/max/zoneorder.h) \
include/linux/numa.h \
include/linux/topology.h \
$(wildcard include/config/sched/smt.h) \
include/asm/topology.h \
include/asm-generic/topology.h \
include/linux/init.h \
$(wildcard include/config/hotplug.h) \
include/linux/kmalloc_sizes.h \
$(wildcard include/config/mmu.h) \
$(wildcard include/config/large/allocs.h) \
include/linux/param.h \
include/linux/timer.h \
include/linux/aio.h \
include/linux/workqueue.h \
include/linux/aio_abi.h \
include/linux/stat.h \
include/asm/stat.h \
include/linux/kmod.h \
$(wildcard include/config/kmod.h) \
include/linux/errno.h \
include/asm/errno.h \
include/asm-generic/errno.h \
include/asm-generic/errno-base.h \
include/linux/elf.h \
include/asm/elf.h \
include/asm/user.h \
include/linux/utsname.h \
include/linux/kobject.h \
include/linux/sysfs.h \
$(wildcard include/config/sysfs.h) \
include/linux/kref.h \
include/linux/kobject_uevent.h \
$(wildcard include/config/kobject/uevent.h) \
include/linux/moduleparam.h \
include/asm/local.h \
include/asm/module.h \
$(wildcard include/config/m486.h) \
$(wildcard include/config/m586.h) \
$(wildcard include/config/m586tsc.h) \
$(wildcard include/config/m586mmx.h) \
$(wildcard include/config/m686.h) \
$(wildcard include/config/mpentiumii.h) \
$(wildcard include/config/mpentiumiii.h) \
$(wildcard include/config/mpentiumm.h) \
$(wildcard include/config/mpentium4.h) \
$(wildcard include/config/mk6.h) \
$(wildcard include/config/mcrusoe.h) \
$(wildcard include/config/mefficeon.h) \
$(wildcard include/config/mwinchipc6.h) \
$(wildcard include/config/mwinchip2.h) \
$(wildcard include/config/mwinchip3d.h) \
$(wildcard include/config/mcyrixiii.h) \
$(wildcard include/config/mviac3/2.h) \
include/linux/interrupt.h \
$(wildcard include/config/generic/hardirqs.h) \
$(wildcard include/config/generic/irq/probe.h) \
include/linux/hardirq.h \
include/linux/smp_lock.h \
$(wildcard include/config/lock/kernel.h) \
include/asm/hardirq.h \
include/linux/irq.h \
$(wildcard include/config/arch/s390.h) \
include/asm/irq.h \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/irqbalance.h) \
include/asm-i386/mach-default/irq_vectors.h \
include/asm-i386/mach-default/irq_vectors_limits.h \
$(wildcard include/config/pci/msi.h) \
$(wildcard include/config/x86/io/apic.h) \
include/asm/hw_irq.h \
include/linux/profile.h \
$(wildcard include/config/proc/fs.h) \
$(wildcard include/config/profiling.h) \
include/asm/sections.h \
include/asm-generic/sections.h \
include/linux/irq_cpustat.h \
include/linux/input.h \
include/linux/fs.h \
$(wildcard include/config/dnotify.h) \
$(wildcard include/config/quota.h) \
$(wildcard include/config/epoll.h) \
$(wildcard include/config/auditsyscall.h) \
include/linux/limits.h \
include/linux/kdev_t.h \
include/linux/ioctl.h \
include/asm/ioctl.h \
include/linux/dcache.h \
include/linux/rcupdate.h \
include/linux/prio_tree.h \
include/linux/radix-tree.h \
include/linux/audit.h \
$(wildcard include/config/audit.h) \
include/linux/quota.h \
include/linux/dqblk_xfs.h \
include/linux/dqblk_v1.h \
include/linux/dqblk_v2.h \
include/linux/nfs_fs_i.h \
include/linux/nfs.h \
include/linux/sunrpc/msg_prot.h \
include/linux/fcntl.h \
include/asm/fcntl.h \
include/linux/err.h \
include/linux/serio.h \
include/linux/device.h \
include/linux/ioport.h \
include/linux/pm.h \
$(wildcard include/config/pm.h) \
drivers/input/keyboard/atkbd.o: $(deps_drivers/input/keyboard/atkbd.o)
$(deps_drivers/input/keyboard/atkbd.o):

View File

@@ -0,0 +1 @@
cmd_drivers/input/keyboard/built-in.o := ld -m elf_i386 -r -o drivers/input/keyboard/built-in.o drivers/input/keyboard/atkbd.o

View File

@@ -0,0 +1,98 @@
#
# Input core configuration
#
config INPUT_KEYBOARD
bool "Keyboards" if EMBEDDED || !X86
default y
depends on INPUT
help
Say Y here, and a list of supported keyboards will be displayed.
This option doesn't affect the kernel.
If unsure, say Y.
config KEYBOARD_ATKBD
tristate "AT keyboard support" if !PC
default y
depends on INPUT && INPUT_KEYBOARD
select SERIO
select SERIO_I8042 if PC
select SERIO_GSCPS2 if GSC
help
Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
you'll need this, unless you have a different type keyboard (USB, ADB
or other). This also works for AT and PS/2 keyboards connected over a
PS/2 to serial converter.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called atkbd.
config KEYBOARD_SUNKBD
tristate "Sun Type 4 and Type 5 keyboard support"
depends on INPUT && INPUT_KEYBOARD
select SERIO
help
Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
connected either to the Sun keyboard connector or to an serial
(RS-232) port via a simple adapter.
To compile this driver as a module, choose M here: the
module will be called sunkbd.
config KEYBOARD_LKKBD
tristate "DECstation/VAXstation LK201/LK401 keyboard support"
depends on INPUT && INPUT_KEYBOARD
select SERIO
help
Say Y here if you want to use a LK201 or LK401 style serial
keyboard. This keyboard is also useable on PCs if you attach
it with the inputattach program. The connector pinout is
described within lkkbd.c.
To compile this driver as a module, choose M here: the
module will be called lkkbd.
config KEYBOARD_XTKBD
tristate "XT Keyboard support"
depends on INPUT && INPUT_KEYBOARD
select SERIO
help
Say Y here if you want to use the old IBM PC/XT keyboard (or
compatible) on your system. This is only possible with a
parallel port keyboard adapter, you cannot connect it to the
keyboard port on a PC that runs Linux.
To compile this driver as a module, choose M here: the
module will be called xtkbd.
config KEYBOARD_NEWTON
tristate "Newton keyboard"
depends on INPUT && INPUT_KEYBOARD
select SERIO
help
Say Y here if you have a Newton keyboard on a serial port.
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
config KEYBOARD_MAPLE
tristate "Maple bus keyboard support"
depends on SH_DREAMCAST && INPUT && INPUT_KEYBOARD && MAPLE
help
Say Y here if you have a DreamCast console running Linux and have
a keyboard attached to its Maple bus.
To compile this driver as a module, choose M here: the
module will be called maple_keyb.
config KEYBOARD_AMIGA
tristate "Amiga keyboard"
depends on AMIGA && INPUT && INPUT_KEYBOARD
help
Say Y here if you are running Linux on any AMIGA and have a keyboard
attached.
To compile this driver as a module, choose M here: the
module will be called amikbd.

View File

@@ -0,0 +1,14 @@
#
# Makefile for the input core drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o

View File

@@ -0,0 +1,241 @@
/*
* $Id: amikbd.c,v 1.13 2002/02/01 16:02:24 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Hamish Macdonald
*/
/*
* Amiga keyboard driver for Linux/m68k
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Amiga keyboard driver");
MODULE_LICENSE("GPL");
static unsigned char amikbd_keycode[0x78] = {
[0] = KEY_GRAVE,
[1] = KEY_1,
[2] = KEY_2,
[3] = KEY_3,
[4] = KEY_4,
[5] = KEY_5,
[6] = KEY_6,
[7] = KEY_7,
[8] = KEY_8,
[9] = KEY_9,
[10] = KEY_0,
[11] = KEY_MINUS,
[12] = KEY_EQUAL,
[13] = KEY_BACKSLASH,
[15] = KEY_KP0,
[16] = KEY_Q,
[17] = KEY_W,
[18] = KEY_E,
[19] = KEY_R,
[20] = KEY_T,
[21] = KEY_Y,
[22] = KEY_U,
[23] = KEY_I,
[24] = KEY_O,
[25] = KEY_P,
[26] = KEY_LEFTBRACE,
[27] = KEY_RIGHTBRACE,
[29] = KEY_KP1,
[30] = KEY_KP2,
[31] = KEY_KP3,
[32] = KEY_A,
[33] = KEY_S,
[34] = KEY_D,
[35] = KEY_F,
[36] = KEY_G,
[37] = KEY_H,
[38] = KEY_J,
[39] = KEY_K,
[40] = KEY_L,
[41] = KEY_SEMICOLON,
[42] = KEY_APOSTROPHE,
[43] = KEY_BACKSLASH,
[45] = KEY_KP4,
[46] = KEY_KP5,
[47] = KEY_KP6,
[48] = KEY_102ND,
[49] = KEY_Z,
[50] = KEY_X,
[51] = KEY_C,
[52] = KEY_V,
[53] = KEY_B,
[54] = KEY_N,
[55] = KEY_M,
[56] = KEY_COMMA,
[57] = KEY_DOT,
[58] = KEY_SLASH,
[60] = KEY_KPDOT,
[61] = KEY_KP7,
[62] = KEY_KP8,
[63] = KEY_KP9,
[64] = KEY_SPACE,
[65] = KEY_BACKSPACE,
[66] = KEY_TAB,
[67] = KEY_KPENTER,
[68] = KEY_ENTER,
[69] = KEY_ESC,
[70] = KEY_DELETE,
[74] = KEY_KPMINUS,
[76] = KEY_UP,
[77] = KEY_DOWN,
[78] = KEY_RIGHT,
[79] = KEY_LEFT,
[80] = KEY_F1,
[81] = KEY_F2,
[82] = KEY_F3,
[83] = KEY_F4,
[84] = KEY_F5,
[85] = KEY_F6,
[86] = KEY_F7,
[87] = KEY_F8,
[88] = KEY_F9,
[89] = KEY_F10,
[90] = KEY_KPLEFTPAREN,
[91] = KEY_KPRIGHTPAREN,
[92] = KEY_KPSLASH,
[93] = KEY_KPASTERISK,
[94] = KEY_KPPLUS,
[95] = KEY_HELP,
[96] = KEY_LEFTSHIFT,
[97] = KEY_RIGHTSHIFT,
[98] = KEY_CAPSLOCK,
[99] = KEY_LEFTCTRL,
[100] = KEY_LEFTALT,
[101] = KEY_RIGHTALT,
[102] = KEY_LEFTMETA,
[103] = KEY_RIGHTMETA
};
static const char *amikbd_messages[8] = {
[0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
[1] = KERN_WARNING "amikbd: keyboard lost sync\n",
[2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
[3] = KERN_WARNING "amikbd: keyboard controller failure\n",
[4] = KERN_ERR "amikbd: keyboard selftest failure\n",
[5] = KERN_INFO "amikbd: initiate power-up key stream\n",
[6] = KERN_INFO "amikbd: terminate power-up key stream\n",
[7] = KERN_WARNING "amikbd: keyboard interrupt\n"
};
static struct input_dev amikbd_dev;
static char *amikbd_name = "Amiga keyboard";
static char *amikbd_phys = "amikbd/input0";
static irqreturn_t amikbd_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
unsigned char scancode, down;
scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */
ciaa.cra |= 0x40; /* switch SP pin to output for handshake */
udelay(85); /* wait until 85 us have expired */
ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */
down = !(scancode & 1); /* lowest bit is release bit */
scancode >>= 1;
if (scancode < 0x78) { /* scancodes < 0x78 are keys */
scancode = amikbd_keycode[scancode];
input_regs(&amikbd_dev, fp);
if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
input_report_key(&amikbd_dev, scancode, 1);
input_report_key(&amikbd_dev, scancode, 0);
input_sync(&amikbd_dev);
} else {
input_report_key(&amikbd_dev, scancode, down);
input_sync(&amikbd_dev);
}
} else /* scancodes >= 0x78 are error codes */
printk(amikbd_messages[scancode - 0x78]);
return IRQ_HANDLED;
}
static int __init amikbd_init(void)
{
int i;
if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
return -EIO;
if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb"))
return -EBUSY;
init_input_dev(&amikbd_dev);
amikbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
amikbd_dev.keycode = amikbd_keycode;
amikbd_dev.keycodesize = sizeof(unsigned char);
amikbd_dev.keycodemax = ARRAY_SIZE(amikbd_keycode);
for (i = 0; i < 0x78; i++)
if (amikbd_keycode[i])
set_bit(amikbd_keycode[i], amikbd_dev.keybit);
ciaa.cra &= ~0x41; /* serial data in, turn off TA */
request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", amikbd_interrupt);
amikbd_dev.name = amikbd_name;
amikbd_dev.phys = amikbd_phys;
amikbd_dev.id.bustype = BUS_AMIGA;
amikbd_dev.id.vendor = 0x0001;
amikbd_dev.id.product = 0x0001;
amikbd_dev.id.version = 0x0100;
input_register_device(&amikbd_dev);
printk(KERN_INFO "input: %s\n", amikbd_name);
return 0;
}
static void __exit amikbd_exit(void)
{
input_unregister_device(&amikbd_dev);
free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
release_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100);
}
module_init(amikbd_init);
module_exit(amikbd_exit);

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,113 @@
/*
* drivers/input/keyboard/hpps2atkbd.h
*
* Copyright (c) 2004 Helge Deller <deller@gmx.de>
* Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
* Copyright (c) 2002 Thibaut Varene <varenet@esiee.fr>
* Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
*
* HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
/* undefine if you have a RDI PRECISIONBOOK */
#define STANDARD_KEYBOARD
#if defined(STANDARD_KEYBOARD)
# define CONFLICT(x,y) x
#else
# define CONFLICT(x,y) y
#endif
/* sadly RDI (Tadpole) decided to ship a different keyboard layout
than HP for their PS/2 laptop keyboard which leads to conflicting
keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
HP: RDI: */
#define C_07 CONFLICT( KEY_F12, KEY_F1 )
#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL )
#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK )
#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL )
#define C_61 CONFLICT( KEY_102ND, KEY_LEFT )
/* Raw SET 2 scancode table */
/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07,
/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2,
/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3,
/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4,
/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5,
/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6,
/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7,
/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8,
/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9,
/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10,
/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ,
/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK,
/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT,
/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP,
/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK,
/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_103RD,
/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 90 */ KEY_RESERVED, KEY_RIGHTALT, KEY_SYSRQ, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA,
/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA,
/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE,
/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE,
/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED,
/* These are offset for escaped keycodes: */
/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
#undef STANDARD_KEYBOARD
#undef CONFLICT
#undef C_07
#undef C_11
#undef C_14
#undef C_58
#undef C_61

View File

@@ -0,0 +1,736 @@
/*
* Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
*/
/*
* LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
*/
/*
* DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
* and VAXstations, but can also be used on any standard RS232 with an
* adaptor).
*
* DISCLAIMER: This works for _me_. If you break anything by using the
* information given below, I will _not_ be liable!
*
* RJ11 pinout: To DB9: Or DB25:
* 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD)
* 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND)
* 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD)
* 3 - +12V (from HDD drive connector), DON'T connect to DB9 or DB25!!!
*
* Pin numbers for DB9 and DB25 are noted on the plug (quite small:). For
* RJ11, it's like this:
*
* __=__ Hold the plug in front of you, cable downwards,
* /___/| nose is hidden behind the plug. Now, pin 1 is at
* |1234|| the left side, pin 4 at the right and 2 and 3 are
* |IIII|| in between, of course:)
* | ||
* |____|/
* || So the adaptor consists of three connected cables
* || for data transmission (RxD and TxD) and signal ground.
* Additionally, you have to get +12V from somewhere.
* Most easily, you'll get that from a floppy or HDD power connector.
* It's the yellow cable there (black is ground and red is +5V).
*
* The keyboard and all the commands it understands are documented in
* "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
* document is LK201 specific, but LK401 is mostly compatible. It comes
* up in LK201 mode and doesn't report any of the additional keys it
* has. These need to be switched on with the LK_CMD_ENABLE_LK401
* command. You'll find this document (scanned .pdf file) on MANX,
* a search engine specific to DEC documentation. Try
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* email or by paper mail:
* Jan-Benedict Glaw, Lilienstraße 16, 33790 Hörste (near Halle/Westf.),
* Germany.
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#define DRIVER_DESC "LK keyboard driver"
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
/*
* Known parameters:
* bell_volume
* keyclick_volume
* ctrlclick_volume
*
* Please notice that there's not yet an API to set these at runtime.
*/
static int bell_volume = 100; /* % */
module_param (bell_volume, int, 0);
MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
static int keyclick_volume = 100; /* % */
module_param (keyclick_volume, int, 0);
MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
static int ctrlclick_volume = 100; /* % */
module_param (ctrlclick_volume, int, 0);
MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
static int lk201_compose_is_alt = 0;
module_param (lk201_compose_is_alt, int, 0);
MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
"will act as an Alt key");
#undef LKKBD_DEBUG
#ifdef LKKBD_DEBUG
#define DBG(x...) printk (x)
#else
#define DBG(x...) do {} while (0)
#endif
/* LED control */
#define LK_LED_WAIT 0x81
#define LK_LED_COMPOSE 0x82
#define LK_LED_SHIFTLOCK 0x84
#define LK_LED_SCROLLLOCK 0x88
#define LK_CMD_LED_ON 0x13
#define LK_CMD_LED_OFF 0x11
/* Mode control */
#define LK_MODE_DOWN 0x80
#define LK_MODE_AUTODOWN 0x82
#define LK_MODE_UPDOWN 0x86
#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
/* Misc commands */
#define LK_CMD_ENABLE_KEYCLICK 0x1b
#define LK_CMD_DISABLE_KEYCLICK 0x99
#define LK_CMD_DISABLE_BELL 0xa1
#define LK_CMD_SOUND_BELL 0xa7
#define LK_CMD_ENABLE_BELL 0x23
#define LK_CMD_DISABLE_CTRCLICK 0xb9
#define LK_CMD_ENABLE_CTRCLICK 0xbb
#define LK_CMD_SET_DEFAULTS 0xd3
#define LK_CMD_POWERCYCLE_RESET 0xfd
#define LK_CMD_ENABLE_LK401 0xe9
#define LK_CMD_REQUEST_ID 0xab
/* Misc responses from keyboard */
#define LK_STUCK_KEY 0x3d
#define LK_SELFTEST_FAILED 0x3e
#define LK_ALL_KEYS_UP 0xb3
#define LK_METRONOME 0xb4
#define LK_OUTPUT_ERROR 0xb5
#define LK_INPUT_ERROR 0xb6
#define LK_KBD_LOCKED 0xb7
#define LK_KBD_TEST_MODE_ACK 0xb8
#define LK_PREFIX_KEY_DOWN 0xb9
#define LK_MODE_CHANGE_ACK 0xba
#define LK_RESPONSE_RESERVED 0xbb
#define LK_NUM_KEYCODES 256
#define LK_NUM_IGNORE_BYTES 6
typedef u_int16_t lk_keycode_t;
static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
[0x56] = KEY_F1,
[0x57] = KEY_F2,
[0x58] = KEY_F3,
[0x59] = KEY_F4,
[0x5a] = KEY_F5,
[0x64] = KEY_F6,
[0x65] = KEY_F7,
[0x66] = KEY_F8,
[0x67] = KEY_F9,
[0x68] = KEY_F10,
[0x71] = KEY_F11,
[0x72] = KEY_F12,
[0x73] = KEY_F13,
[0x74] = KEY_F14,
[0x7c] = KEY_F15,
[0x7d] = KEY_F16,
[0x80] = KEY_F17,
[0x81] = KEY_F18,
[0x82] = KEY_F19,
[0x83] = KEY_F20,
[0x8a] = KEY_FIND,
[0x8b] = KEY_INSERT,
[0x8c] = KEY_DELETE,
[0x8d] = KEY_SELECT,
[0x8e] = KEY_PAGEUP,
[0x8f] = KEY_PAGEDOWN,
[0x92] = KEY_KP0,
[0x94] = KEY_KPDOT,
[0x95] = KEY_KPENTER,
[0x96] = KEY_KP1,
[0x97] = KEY_KP2,
[0x98] = KEY_KP3,
[0x99] = KEY_KP4,
[0x9a] = KEY_KP5,
[0x9b] = KEY_KP6,
[0x9c] = KEY_KPCOMMA,
[0x9d] = KEY_KP7,
[0x9e] = KEY_KP8,
[0x9f] = KEY_KP9,
[0xa0] = KEY_KPMINUS,
[0xa1] = KEY_PROG1,
[0xa2] = KEY_PROG2,
[0xa3] = KEY_PROG3,
[0xa4] = KEY_PROG4,
[0xa7] = KEY_LEFT,
[0xa8] = KEY_RIGHT,
[0xa9] = KEY_DOWN,
[0xaa] = KEY_UP,
[0xab] = KEY_RIGHTSHIFT,
[0xac] = KEY_LEFTALT,
[0xad] = KEY_COMPOSE, /* Right Compose, that is. */
[0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
[0xaf] = KEY_LEFTCTRL,
[0xb0] = KEY_CAPSLOCK,
[0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
[0xb2] = KEY_RIGHTALT,
[0xbc] = KEY_BACKSPACE,
[0xbd] = KEY_ENTER,
[0xbe] = KEY_TAB,
[0xbf] = KEY_ESC,
[0xc0] = KEY_1,
[0xc1] = KEY_Q,
[0xc2] = KEY_A,
[0xc3] = KEY_Z,
[0xc5] = KEY_2,
[0xc6] = KEY_W,
[0xc7] = KEY_S,
[0xc8] = KEY_X,
[0xc9] = KEY_102ND,
[0xcb] = KEY_3,
[0xcc] = KEY_E,
[0xcd] = KEY_D,
[0xce] = KEY_C,
[0xd0] = KEY_4,
[0xd1] = KEY_R,
[0xd2] = KEY_F,
[0xd3] = KEY_V,
[0xd4] = KEY_SPACE,
[0xd6] = KEY_5,
[0xd7] = KEY_T,
[0xd8] = KEY_G,
[0xd9] = KEY_B,
[0xdb] = KEY_6,
[0xdc] = KEY_Y,
[0xdd] = KEY_H,
[0xde] = KEY_N,
[0xe0] = KEY_7,
[0xe1] = KEY_U,
[0xe2] = KEY_J,
[0xe3] = KEY_M,
[0xe5] = KEY_8,
[0xe6] = KEY_I,
[0xe7] = KEY_K,
[0xe8] = KEY_COMMA,
[0xea] = KEY_9,
[0xeb] = KEY_O,
[0xec] = KEY_L,
[0xed] = KEY_DOT,
[0xef] = KEY_0,
[0xf0] = KEY_P,
[0xf2] = KEY_SEMICOLON,
[0xf3] = KEY_SLASH,
[0xf5] = KEY_EQUAL,
[0xf6] = KEY_RIGHTBRACE,
[0xf7] = KEY_BACKSLASH,
[0xf9] = KEY_MINUS,
[0xfa] = KEY_LEFTBRACE,
[0xfb] = KEY_APOSTROPHE,
};
#define CHECK_LED(LED, BITS) do { \
if (test_bit (LED, lk->dev.led)) \
leds_on |= BITS; \
else \
leds_off |= BITS; \
} while (0)
/*
* Per-keyboard data
*/
struct lkkbd {
lk_keycode_t keycode[LK_NUM_KEYCODES];
int ignore_bytes;
unsigned char id[LK_NUM_IGNORE_BYTES];
struct input_dev dev;
struct serio *serio;
struct work_struct tq;
char name[64];
char phys[32];
char type;
int bell_volume;
int keyclick_volume;
int ctrlclick_volume;
};
/*
* Calculate volume parameter byte for a given volume.
*/
static unsigned char
volume_to_hw (int volume_percent)
{
unsigned char ret = 0;
if (volume_percent < 0)
volume_percent = 0;
if (volume_percent > 100)
volume_percent = 100;
if (volume_percent >= 0)
ret = 7;
if (volume_percent >= 13) /* 12.5 */
ret = 6;
if (volume_percent >= 25)
ret = 5;
if (volume_percent >= 38) /* 37.5 */
ret = 4;
if (volume_percent >= 50)
ret = 3;
if (volume_percent >= 63) /* 62.5 */
ret = 2; /* This is the default volume */
if (volume_percent >= 75)
ret = 1;
if (volume_percent >= 88) /* 87.5 */
ret = 0;
ret |= 0x80;
return ret;
}
static void
lkkbd_detection_done (struct lkkbd *lk)
{
int i;
/*
* Reset setting for Compose key. Let Compose be KEY_COMPOSE.
*/
lk->keycode[0xb1] = KEY_COMPOSE;
/*
* Print keyboard name and modify Compose=Alt on user's request.
*/
switch (lk->id[4]) {
case 1:
sprintf (lk->name, "DEC LK201 keyboard");
if (lk201_compose_is_alt)
lk->keycode[0xb1] = KEY_LEFTALT;
break;
case 2:
sprintf (lk->name, "DEC LK401 keyboard");
break;
default:
sprintf (lk->name, "Unknown DEC keyboard");
printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
"please report to Jan-Benedict Glaw "
"<jbglaw@lug-owl.de>\n", lk->phys);
printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
printk (" 0x%02x", lk->id[i]);
printk ("\n");
break;
}
printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
lk->phys, lk->name);
/*
* Report errors during keyboard boot-up.
*/
switch (lk->id[2]) {
case 0x00:
/* All okay */
break;
case LK_STUCK_KEY:
printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
"%s\n", lk->phys);
break;
case LK_SELFTEST_FAILED:
printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
"at %s, keyboard may not work "
"properly\n", lk->phys);
break;
default:
printk (KERN_ERR "lkkbd: Unknown error %02x on "
"keyboard at %s\n", lk->id[2],
lk->phys);
break;
}
/*
* Try to hint user if there's a stuck key.
*/
if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
"is 0x%04x\n", lk->id[3],
lk->keycode[lk->id[3]]);
return;
}
/*
* lkkbd_interrupt() is called by the low level driver when a character
* is received.
*/
static irqreturn_t
lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
struct pt_regs *regs)
{
struct lkkbd *lk = serio->private;
int i;
DBG (KERN_INFO "Got byte 0x%02x\n", data);
if (lk->ignore_bytes > 0) {
DBG (KERN_INFO "Ignoring a byte on %s\n",
lk->name);
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
if (lk->ignore_bytes == 0)
lkkbd_detection_done (lk);
return IRQ_HANDLED;
}
switch (data) {
case LK_ALL_KEYS_UP:
input_regs (&lk->dev, regs);
for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
if (lk->keycode[i] != KEY_RESERVED)
input_report_key (&lk->dev, lk->keycode[i], 0);
input_sync (&lk->dev);
break;
case LK_METRONOME:
DBG (KERN_INFO "Got LK_METRONOME and don't "
"know how to handle...\n");
break;
case LK_OUTPUT_ERROR:
DBG (KERN_INFO "Got LK_OUTPUT_ERROR and don't "
"know how to handle...\n");
break;
case LK_INPUT_ERROR:
DBG (KERN_INFO "Got LK_INPUT_ERROR and don't "
"know how to handle...\n");
break;
case LK_KBD_LOCKED:
DBG (KERN_INFO "Got LK_KBD_LOCKED and don't "
"know how to handle...\n");
break;
case LK_KBD_TEST_MODE_ACK:
DBG (KERN_INFO "Got LK_KBD_TEST_MODE_ACK and don't "
"know how to handle...\n");
break;
case LK_PREFIX_KEY_DOWN:
DBG (KERN_INFO "Got LK_PREFIX_KEY_DOWN and don't "
"know how to handle...\n");
break;
case LK_MODE_CHANGE_ACK:
DBG (KERN_INFO "Got LK_MODE_CHANGE_ACK and ignored "
"it properly...\n");
break;
case LK_RESPONSE_RESERVED:
DBG (KERN_INFO "Got LK_RESPONSE_RESERVED and don't "
"know how to handle...\n");
break;
case 0x01:
DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
schedule_work (&lk->tq);
break;
default:
if (lk->keycode[data] != KEY_RESERVED) {
input_regs (&lk->dev, regs);
if (!test_bit (lk->keycode[data], lk->dev.key))
input_report_key (&lk->dev, lk->keycode[data], 1);
else
input_report_key (&lk->dev, lk->keycode[data], 0);
input_sync (&lk->dev);
} else
printk (KERN_WARNING "%s: Unknown key with "
"scancode 0x%02x on %s.\n",
__FILE__, data, lk->name);
}
return IRQ_HANDLED;
}
/*
* lkkbd_event() handles events from the input module.
*/
static int
lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
int value)
{
struct lkkbd *lk = dev->private;
unsigned char leds_on = 0;
unsigned char leds_off = 0;
switch (type) {
case EV_LED:
CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK);
CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE);
CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK);
CHECK_LED (LED_SLEEP, LK_LED_WAIT);
if (leds_on != 0) {
lk->serio->write (lk->serio, LK_CMD_LED_ON);
lk->serio->write (lk->serio, leds_on);
}
if (leds_off != 0) {
lk->serio->write (lk->serio, LK_CMD_LED_OFF);
lk->serio->write (lk->serio, leds_off);
}
return 0;
case EV_SND:
switch (code) {
case SND_CLICK:
if (value == 0) {
DBG ("%s: Deactivating key clicks\n", __FUNCTION__);
lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
} else {
DBG ("%s: Activating key clicks\n", __FUNCTION__);
lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
}
return 0;
case SND_BELL:
if (value != 0)
lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
return 0;
}
break;
default:
printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n",
__FUNCTION__, type, code, value);
}
return -1;
}
/*
* lkkbd_reinit() sets leds and beeps to a state the computer remembers they
* were in.
*/
static void
lkkbd_reinit (void *data)
{
struct lkkbd *lk = data;
int division;
unsigned char leds_on = 0;
unsigned char leds_off = 0;
/* Ask for ID */
lk->serio->write (lk->serio, LK_CMD_REQUEST_ID);
/* Reset parameters */
lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS);
/* Set LEDs */
CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK);
CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE);
CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK);
CHECK_LED (LED_SLEEP, LK_LED_WAIT);
if (leds_on != 0) {
lk->serio->write (lk->serio, LK_CMD_LED_ON);
lk->serio->write (lk->serio, leds_on);
}
if (leds_off != 0) {
lk->serio->write (lk->serio, LK_CMD_LED_OFF);
lk->serio->write (lk->serio, leds_off);
}
/*
* Try to activate extended LK401 mode. This command will
* only work with a LK401 keyboard and grants access to
* LAlt, RAlt, RCompose and RShift.
*/
lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401);
/* Set all keys to UPDOWN mode */
for (division = 1; division <= 14; division++)
lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
division));
/* Enable bell and set volume */
lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL);
lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume));
/* Enable/disable keyclick (and possibly set volume) */
if (test_bit (SND_CLICK, lk->dev.snd)) {
lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
} else {
lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
}
/* Sound the bell if needed */
if (test_bit (SND_BELL, lk->dev.snd))
lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
}
/*
* lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
*/
static void
lkkbd_connect (struct serio *serio, struct serio_driver *drv)
{
struct lkkbd *lk;
int i;
if ((serio->type & SERIO_TYPE) != SERIO_RS232)
return;
if ((serio->type & SERIO_PROTO) != SERIO_LKKBD)
return;
if (!(lk = kmalloc (sizeof (struct lkkbd), GFP_KERNEL)))
return;
memset (lk, 0, sizeof (struct lkkbd));
init_input_dev (&lk->dev);
set_bit (EV_KEY, lk->dev.evbit);
set_bit (EV_LED, lk->dev.evbit);
set_bit (EV_SND, lk->dev.evbit);
set_bit (EV_REP, lk->dev.evbit);
set_bit (LED_CAPSL, lk->dev.ledbit);
set_bit (LED_SLEEP, lk->dev.ledbit);
set_bit (LED_COMPOSE, lk->dev.ledbit);
set_bit (LED_SCROLLL, lk->dev.ledbit);
set_bit (SND_BELL, lk->dev.sndbit);
set_bit (SND_CLICK, lk->dev.sndbit);
lk->serio = serio;
INIT_WORK (&lk->tq, lkkbd_reinit, lk);
lk->bell_volume = bell_volume;
lk->keyclick_volume = keyclick_volume;
lk->ctrlclick_volume = ctrlclick_volume;
lk->dev.keycode = lk->keycode;
lk->dev.keycodesize = sizeof (lk_keycode_t);
lk->dev.keycodemax = LK_NUM_KEYCODES;
lk->dev.event = lkkbd_event;
lk->dev.private = lk;
serio->private = lk;
if (serio_open (serio, drv)) {
kfree (lk);
return;
}
sprintf (lk->name, "DEC LK keyboard");
sprintf (lk->phys, "%s/input0", serio->phys);
memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES);
for (i = 0; i < LK_NUM_KEYCODES; i++)
set_bit (lk->keycode[i], lk->dev.keybit);
lk->dev.name = lk->name;
lk->dev.phys = lk->phys;
lk->dev.id.bustype = BUS_RS232;
lk->dev.id.vendor = SERIO_LKKBD;
lk->dev.id.product = 0;
lk->dev.id.version = 0x0100;
input_register_device (&lk->dev);
printk (KERN_INFO "input: %s on %s, initiating reset\n", lk->name, serio->phys);
lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET);
}
/*
* lkkbd_disconnect() unregisters and closes behind us.
*/
static void
lkkbd_disconnect (struct serio *serio)
{
struct lkkbd *lk = serio->private;
input_unregister_device (&lk->dev);
serio_close (serio);
kfree (lk);
}
static struct serio_driver lkkbd_drv = {
.driver = {
.name = "lkkbd",
},
.description = DRIVER_DESC,
.connect = lkkbd_connect,
.disconnect = lkkbd_disconnect,
.interrupt = lkkbd_interrupt,
};
/*
* The functions for insering/removing us as a module.
*/
int __init
lkkbd_init (void)
{
serio_register_driver(&lkkbd_drv);
return 0;
}
void __exit
lkkbd_exit (void)
{
serio_unregister_driver(&lkkbd_drv);
}
module_init (lkkbd_init);
module_exit (lkkbd_exit);

View File

@@ -0,0 +1,190 @@
/*
* $Id: maple_keyb.c,v 1.4 2004/03/22 01:18:15 lethal Exp $
* SEGA Dreamcast keyboard driver
* Based on drivers/usb/usbkbd.c
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/maple.h>
MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>");
MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
MODULE_LICENSE("GPL");
static unsigned char dc_kbd_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140
};
struct dc_kbd {
struct input_dev dev;
unsigned char new[8];
unsigned char old[8];
int open;
};
static void dc_scan_kbd(struct dc_kbd *kbd)
{
int i;
struct input_dev *dev = &kbd->dev;
for(i=0; i<8; i++)
input_report_key(dev,
dc_kbd_keycode[i+224],
(kbd->new[0]>>i)&1);
for(i=2; i<8; i++) {
if(kbd->old[i]>3&&memscan(kbd->new+2, kbd->old[i], 6)==NULL) {
if(dc_kbd_keycode[kbd->old[i]])
input_report_key(dev,
dc_kbd_keycode[kbd->old[i]],
0);
else
printk("Unknown key (scancode %#x) released.",
kbd->old[i]);
}
if(kbd->new[i]>3&&memscan(kbd->old+2, kbd->new[i], 6)!=NULL) {
if(dc_kbd_keycode[kbd->new[i]])
input_report_key(dev,
dc_kbd_keycode[kbd->new[i]],
1);
else
printk("Unknown key (scancode %#x) pressed.",
kbd->new[i]);
}
}
input_sync(dev);
memcpy(kbd->old, kbd->new, 8);
}
static void dc_kbd_callback(struct mapleq *mq)
{
struct maple_device *mapledev = mq->dev;
struct dc_kbd *kbd = mapledev->private_data;
unsigned long *buf = mq->recvbuf;
if (buf[1] == mapledev->function) {
memcpy(kbd->new, buf+2, 8);
dc_scan_kbd(kbd);
}
}
static int dc_kbd_open(struct input_dev *dev)
{
struct dc_kbd *kbd = dev->private;
kbd->open++;
return 0;
}
static void dc_kbd_close(struct input_dev *dev)
{
struct dc_kbd *kbd = dev->private;
kbd->open--;
}
static int dc_kbd_connect(struct maple_device *dev)
{
int i;
unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
struct dc_kbd *kbd;
if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL)))
return -1;
memset(kbd, 0, sizeof(struct dc_kbd));
dev->private_data = kbd;
kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
init_input_dev(&kbd->dev);
for (i=0; i<255; i++)
set_bit(dc_kbd_keycode[i], kbd->dev.keybit);
clear_bit(0, kbd->dev.keybit);
kbd->dev.private = kbd;
kbd->dev.open = dc_kbd_open;
kbd->dev.close = dc_kbd_close;
kbd->dev.event = NULL;
kbd->dev.name = dev->product_name;
kbd->dev.id.bustype = BUS_MAPLE;
input_register_device(&kbd->dev);
maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD);
printk(KERN_INFO "input: keyboard(0x%lx): %s\n", data, kbd->dev.name);
return 0;
}
static void dc_kbd_disconnect(struct maple_device *dev)
{
struct dc_kbd *kbd = dev->private_data;
input_unregister_device(&kbd->dev);
kfree(kbd);
}
static struct maple_driver dc_kbd_driver = {
.function = MAPLE_FUNC_KEYBOARD,
.name = "Dreamcast keyboard",
.connect = dc_kbd_connect,
.disconnect = dc_kbd_disconnect,
};
static int __init dc_kbd_init(void)
{
maple_register_driver(&dc_kbd_driver);
return 0;
}
static void __exit dc_kbd_exit(void)
{
maple_unregister_driver(&dc_kbd_driver);
}
module_init(dc_kbd_init);
module_exit(dc_kbd_exit);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) 2000 Justin Cormack
*/
/*
* Newton keyboard driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail:
* Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#define DRIVER_DESC "Newton keyboard driver"
MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define NKBD_KEY 0x7f
#define NKBD_PRESS 0x80
static unsigned char nkbd_keycode[128] = {
KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
};
static char *nkbd_name = "Newton Keyboard";
struct nkbd {
unsigned char keycode[128];
struct input_dev dev;
struct serio *serio;
char phys[32];
};
irqreturn_t nkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct nkbd *nkbd = serio->private;
/* invalid scan codes are probably the init sequence, so we ignore them */
if (nkbd->keycode[data & NKBD_KEY]) {
input_regs(&nkbd->dev, regs);
input_report_key(&nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
input_sync(&nkbd->dev);
}
else if (data == 0xe7) /* end of init sequence */
printk(KERN_INFO "input: %s on %s\n", nkbd_name, serio->phys);
return IRQ_HANDLED;
}
void nkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct nkbd *nkbd;
int i;
if (serio->type != (SERIO_RS232 | SERIO_NEWTON))
return;
if (!(nkbd = kmalloc(sizeof(struct nkbd), GFP_KERNEL)))
return;
memset(nkbd, 0, sizeof(struct nkbd));
nkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
nkbd->serio = serio;
init_input_dev(&nkbd->dev);
nkbd->dev.keycode = nkbd->keycode;
nkbd->dev.keycodesize = sizeof(unsigned char);
nkbd->dev.keycodemax = ARRAY_SIZE(nkbd_keycode);
nkbd->dev.private = nkbd;
serio->private = nkbd;
if (serio_open(serio, drv)) {
kfree(nkbd);
return;
}
memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
for (i = 0; i < 128; i++)
set_bit(nkbd->keycode[i], nkbd->dev.keybit);
clear_bit(0, nkbd->dev.keybit);
sprintf(nkbd->phys, "%s/input0", serio->phys);
nkbd->dev.name = nkbd_name;
nkbd->dev.phys = nkbd->phys;
nkbd->dev.id.bustype = BUS_RS232;
nkbd->dev.id.vendor = SERIO_NEWTON;
nkbd->dev.id.product = 0x0001;
nkbd->dev.id.version = 0x0100;
input_register_device(&nkbd->dev);
printk(KERN_INFO "input: %s on %s\n", nkbd_name, serio->phys);
}
void nkbd_disconnect(struct serio *serio)
{
struct nkbd *nkbd = serio->private;
input_unregister_device(&nkbd->dev);
serio_close(serio);
kfree(nkbd);
}
struct serio_driver nkbd_drv = {
.driver = {
.name = "newtonkbd",
},
.description = DRIVER_DESC,
.interrupt = nkbd_interrupt,
.connect = nkbd_connect,
.disconnect = nkbd_disconnect,
};
int __init nkbd_init(void)
{
serio_register_driver(&nkbd_drv);
return 0;
}
void __exit nkbd_exit(void)
{
serio_unregister_driver(&nkbd_drv);
}
module_init(nkbd_init);
module_exit(nkbd_exit);

View File

@@ -0,0 +1,332 @@
/*
* $Id: sunkbd.c,v 1.14 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Sun keyboard driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#define DRIVER_DESC "Sun keyboard driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static unsigned char sunkbd_keycode[128] = {
0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64, 0,
65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
};
#define SUNKBD_CMD_RESET 0x1
#define SUNKBD_CMD_BELLON 0x2
#define SUNKBD_CMD_BELLOFF 0x3
#define SUNKBD_CMD_CLICK 0xa
#define SUNKBD_CMD_NOCLICK 0xb
#define SUNKBD_CMD_SETLED 0xe
#define SUNKBD_CMD_LAYOUT 0xf
#define SUNKBD_RET_RESET 0xff
#define SUNKBD_RET_ALLUP 0x7f
#define SUNKBD_RET_LAYOUT 0xfe
#define SUNKBD_LAYOUT_5_MASK 0x20
#define SUNKBD_RELEASE 0x80
#define SUNKBD_KEY 0x7f
/*
* Per-keyboard data.
*/
struct sunkbd {
unsigned char keycode[128];
struct input_dev dev;
struct serio *serio;
struct work_struct tq;
wait_queue_head_t wait;
char name[64];
char phys[32];
char type;
volatile s8 reset;
volatile s8 layout;
};
/*
* sunkbd_interrupt() is called by the low level driver when a character
* is received.
*/
static irqreturn_t sunkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct sunkbd* sunkbd = serio->private;
if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
wake_up_interruptible(&sunkbd->wait);
goto out;
}
if (sunkbd->layout == -1) {
sunkbd->layout = data;
wake_up_interruptible(&sunkbd->wait);
goto out;
}
switch (data) {
case SUNKBD_RET_RESET:
schedule_work(&sunkbd->tq);
sunkbd->reset = -1;
break;
case SUNKBD_RET_LAYOUT:
sunkbd->layout = -1;
break;
case SUNKBD_RET_ALLUP: /* All keys released */
break;
default:
if (sunkbd->keycode[data & SUNKBD_KEY]) {
input_regs(&sunkbd->dev, regs);
input_report_key(&sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
input_sync(&sunkbd->dev);
} else {
printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
}
}
out:
return IRQ_HANDLED;
}
/*
* sunkbd_event() handles events from the input module.
*/
static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct sunkbd *sunkbd = dev->private;
switch (type) {
case EV_LED:
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
sunkbd->serio->write(sunkbd->serio,
(!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
(!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
return 0;
case EV_SND:
switch (code) {
case SND_CLICK:
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
return 0;
case SND_BELL:
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
return 0;
}
break;
}
return -1;
}
/*
* sunkbd_initialize() checks for a Sun keyboard attached, and determines
* its type.
*/
static int sunkbd_initialize(struct sunkbd *sunkbd)
{
sunkbd->reset = -2;
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
if (sunkbd->reset <0)
return -1;
sunkbd->type = sunkbd->reset;
if (sunkbd->type == 4) { /* Type 4 keyboard */
sunkbd->layout = -2;
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
if (sunkbd->layout < 0) return -1;
if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
}
return 0;
}
/*
* sunkbd_reinit() sets leds and beeps to a state the computer remembers they
* were in.
*/
static void sunkbd_reinit(void *data)
{
struct sunkbd *sunkbd = data;
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
sunkbd->serio->write(sunkbd->serio,
(!!test_bit(LED_CAPSL, sunkbd->dev.led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev.led) << 2) |
(!!test_bit(LED_COMPOSE, sunkbd->dev.led) << 1) | !!test_bit(LED_NUML, sunkbd->dev.led));
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev.snd));
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev.snd));
}
/*
* sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
*/
static void sunkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct sunkbd *sunkbd;
int i;
if ((serio->type & SERIO_TYPE) != SERIO_RS232)
return;
if ((serio->type & SERIO_PROTO) && (serio->type & SERIO_PROTO) != SERIO_SUNKBD)
return;
if (!(sunkbd = kmalloc(sizeof(struct sunkbd), GFP_KERNEL)))
return;
memset(sunkbd, 0, sizeof(struct sunkbd));
init_input_dev(&sunkbd->dev);
init_waitqueue_head(&sunkbd->wait);
sunkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
sunkbd->dev.ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
sunkbd->dev.sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
sunkbd->serio = serio;
INIT_WORK(&sunkbd->tq, sunkbd_reinit, sunkbd);
sunkbd->dev.keycode = sunkbd->keycode;
sunkbd->dev.keycodesize = sizeof(unsigned char);
sunkbd->dev.keycodemax = ARRAY_SIZE(sunkbd_keycode);
sunkbd->dev.event = sunkbd_event;
sunkbd->dev.private = sunkbd;
serio->private = sunkbd;
if (serio_open(serio, drv)) {
kfree(sunkbd);
return;
}
if (sunkbd_initialize(sunkbd) < 0) {
serio_close(serio);
kfree(sunkbd);
return;
}
sprintf(sunkbd->name, "Sun Type %d keyboard", sunkbd->type);
memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
for (i = 0; i < 128; i++)
set_bit(sunkbd->keycode[i], sunkbd->dev.keybit);
clear_bit(0, sunkbd->dev.keybit);
sprintf(sunkbd->phys, "%s/input0", serio->phys);
sunkbd->dev.name = sunkbd->name;
sunkbd->dev.phys = sunkbd->phys;
sunkbd->dev.id.bustype = BUS_RS232;
sunkbd->dev.id.vendor = SERIO_SUNKBD;
sunkbd->dev.id.product = sunkbd->type;
sunkbd->dev.id.version = 0x0100;
input_register_device(&sunkbd->dev);
printk(KERN_INFO "input: %s on %s\n", sunkbd->name, serio->phys);
}
/*
* sunkbd_disconnect() unregisters and closes behind us.
*/
static void sunkbd_disconnect(struct serio *serio)
{
struct sunkbd *sunkbd = serio->private;
input_unregister_device(&sunkbd->dev);
serio_close(serio);
kfree(sunkbd);
}
static struct serio_driver sunkbd_drv = {
.driver = {
.name = "sunkbd",
},
.description = DRIVER_DESC,
.interrupt = sunkbd_interrupt,
.connect = sunkbd_connect,
.disconnect = sunkbd_disconnect,
};
/*
* The functions for insering/removing us as a module.
*/
int __init sunkbd_init(void)
{
serio_register_driver(&sunkbd_drv);
return 0;
}
void __exit sunkbd_exit(void)
{
serio_unregister_driver(&sunkbd_drv);
}
module_init(sunkbd_init);
module_exit(sunkbd_exit);

View File

@@ -0,0 +1,170 @@
/*
* $Id: xtkbd.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* XT keyboard driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#define DRIVER_DESC "XT keyboard driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define XTKBD_EMUL0 0xe0
#define XTKBD_EMUL1 0xe1
#define XTKBD_KEY 0x7f
#define XTKBD_RELEASE 0x80
static unsigned char xtkbd_keycode[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
106
};
static char *xtkbd_name = "XT Keyboard";
struct xtkbd {
unsigned char keycode[256];
struct input_dev dev;
struct serio *serio;
char phys[32];
};
irqreturn_t xtkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct xtkbd *xtkbd = serio->private;
switch (data) {
case XTKBD_EMUL0:
case XTKBD_EMUL1:
break;
default:
if (xtkbd->keycode[data & XTKBD_KEY]) {
input_regs(&xtkbd->dev, regs);
input_report_key(&xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
input_sync(&xtkbd->dev);
} else {
printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
}
}
return IRQ_HANDLED;
}
void xtkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct xtkbd *xtkbd;
int i;
if ((serio->type & SERIO_TYPE) != SERIO_XT)
return;
if (!(xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL)))
return;
memset(xtkbd, 0, sizeof(struct xtkbd));
xtkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
xtkbd->serio = serio;
init_input_dev(&xtkbd->dev);
xtkbd->dev.keycode = xtkbd->keycode;
xtkbd->dev.keycodesize = sizeof(unsigned char);
xtkbd->dev.keycodemax = ARRAY_SIZE(xtkbd_keycode);
xtkbd->dev.private = xtkbd;
serio->private = xtkbd;
if (serio_open(serio, drv)) {
kfree(xtkbd);
return;
}
memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
for (i = 0; i < 255; i++)
set_bit(xtkbd->keycode[i], xtkbd->dev.keybit);
clear_bit(0, xtkbd->dev.keybit);
sprintf(xtkbd->phys, "%s/input0", serio->phys);
xtkbd->dev.name = xtkbd_name;
xtkbd->dev.phys = xtkbd->phys;
xtkbd->dev.id.bustype = BUS_XTKBD;
xtkbd->dev.id.vendor = 0x0001;
xtkbd->dev.id.product = 0x0001;
xtkbd->dev.id.version = 0x0100;
input_register_device(&xtkbd->dev);
printk(KERN_INFO "input: %s on %s\n", xtkbd_name, serio->phys);
}
void xtkbd_disconnect(struct serio *serio)
{
struct xtkbd *xtkbd = serio->private;
input_unregister_device(&xtkbd->dev);
serio_close(serio);
kfree(xtkbd);
}
struct serio_driver xtkbd_drv = {
.driver = {
.name = "xtkbd",
},
.description = DRIVER_DESC,
.interrupt = xtkbd_interrupt,
.connect = xtkbd_connect,
.disconnect = xtkbd_disconnect,
};
int __init xtkbd_init(void)
{
serio_register_driver(&xtkbd_drv);
return 0;
}
void __exit xtkbd_exit(void)
{
serio_unregister_driver(&xtkbd_drv);
}
module_init(xtkbd_init);
module_exit(xtkbd_exit);

View File

@@ -0,0 +1,52 @@
#
# Input misc drivers configuration
#
config INPUT_MISC
bool "Misc"
depends on INPUT
help
Say Y here, and a list of miscellaneous input drivers will be displayed.
Everything that didn't fit into the other categories is here. This option
doesn't affect the kernel.
If unsure, say Y.
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on (ALPHA || X86 || X86_64 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES) && INPUT && INPUT_MISC
help
Say Y here if you want the standard PC Speaker to be used for
bells and whistles.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called pcspkr.
config INPUT_SPARCSPKR
tristate "SPARC Speaker support"
depends on (SPARC32 || SPARC64) && INPUT && INPUT_MISC && PCI
help
Say Y here if you want the standard Speaker on Sparc PCI systems
to be used for bells and whistles.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called sparcspkr.
config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K && INPUT && INPUT_MISC
config INPUT_UINPUT
tristate "User level driver support"
depends on INPUT && INPUT_MISC
help
Say Y here if you want to support user level drivers for input
subsystem accessible under char device 10:223 - /dev/input/uinput.
To compile this driver as a module, choose M here: the
module will be called uinput.

View File

@@ -0,0 +1,11 @@
#
# Makefile for the input misc drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_98SPKR) += 98spkr.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o

View File

@@ -0,0 +1,83 @@
/*
* m68k beeper driver for Linux
*
* Copyright (c) 2002 Richard Zidlicky
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
*
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <asm/machdep.h>
#include <asm/io.h>
MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
MODULE_DESCRIPTION("m68k beeper driver");
MODULE_LICENSE("GPL");
static char m68kspkr_name[] = "m68k beeper";
static char m68kspkr_phys[] = "m68k/generic";
static struct input_dev m68kspkr_dev;
static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
mach_beep(count, -1);
return 0;
}
static int __init m68kspkr_init(void)
{
if (!mach_beep){
printk("%s: no lowlevel beep support\n", m68kspkr_name);
return -1;
}
m68kspkr_dev.evbit[0] = BIT(EV_SND);
m68kspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
m68kspkr_dev.event = m68kspkr_event;
m68kspkr_dev.name = m68kspkr_name;
m68kspkr_dev.phys = m68kspkr_phys;
m68kspkr_dev.id.bustype = BUS_HOST;
m68kspkr_dev.id.vendor = 0x001f;
m68kspkr_dev.id.product = 0x0001;
m68kspkr_dev.id.version = 0x0100;
input_register_device(&m68kspkr_dev);
printk(KERN_INFO "input: %s\n", m68kspkr_name);
return 0;
}
static void __exit m68kspkr_exit(void)
{
input_unregister_device(&m68kspkr_dev);
}
module_init(m68kspkr_init);
module_exit(m68kspkr_exit);

View File

@@ -0,0 +1,95 @@
/*
* PC Speaker beeper driver for Linux
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
*
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <asm/8253pit.h>
#include <asm/io.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("PC Speaker beeper driver");
MODULE_LICENSE("GPL");
static char pcspkr_name[] = "PC Speaker";
static char pcspkr_phys[] = "isa0061/input0";
static struct input_dev pcspkr_dev;
spinlock_t i8253_beep_lock = SPIN_LOCK_UNLOCKED;
static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = PIT_TICK_RATE / value;
spin_lock_irqsave(&i8253_beep_lock, flags);
if (count) {
/* enable counter 2 */
outb_p(inb_p(0x61) | 3, 0x61);
/* set command for counter 2, 2 byte write */
outb_p(0xB6, 0x43);
/* select desired HZ */
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
} else {
/* disable counter 2 */
outb(inb_p(0x61) & 0xFC, 0x61);
}
spin_unlock_irqrestore(&i8253_beep_lock, flags);
return 0;
}
static int __init pcspkr_init(void)
{
pcspkr_dev.evbit[0] = BIT(EV_SND);
pcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
pcspkr_dev.event = pcspkr_event;
pcspkr_dev.name = pcspkr_name;
pcspkr_dev.phys = pcspkr_phys;
pcspkr_dev.id.bustype = BUS_ISA;
pcspkr_dev.id.vendor = 0x001f;
pcspkr_dev.id.product = 0x0001;
pcspkr_dev.id.version = 0x0100;
input_register_device(&pcspkr_dev);
printk(KERN_INFO "input: %s\n", pcspkr_name);
return 0;
}
static void __exit pcspkr_exit(void)
{
input_unregister_device(&pcspkr_dev);
}
module_init(pcspkr_init);
module_exit(pcspkr_exit);

View File

@@ -0,0 +1,189 @@
/*
* Driver for PC-speaker like devices found on various Sparc systems.
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 2002 David S. Miller (davem@redhat.com)
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <asm/io.h>
#include <asm/ebus.h>
#ifdef CONFIG_SPARC64
#include <asm/isa.h>
#endif
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_DESCRIPTION("PC Speaker beeper driver");
MODULE_LICENSE("GPL");
static unsigned long beep_iobase;
static char *sparcspkr_isa_name = "Sparc ISA Speaker";
static char *sparcspkr_ebus_name = "Sparc EBUS Speaker";
static char *sparcspkr_phys = "sparc/input0";
static struct input_dev sparcspkr_dev;
spinlock_t beep_lock = SPIN_LOCK_UNLOCKED;
static void __init init_sparcspkr_struct(void)
{
sparcspkr_dev.evbit[0] = BIT(EV_SND);
sparcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
sparcspkr_dev.phys = sparcspkr_phys;
sparcspkr_dev.id.bustype = BUS_ISA;
sparcspkr_dev.id.vendor = 0x001f;
sparcspkr_dev.id.product = 0x0001;
sparcspkr_dev.id.version = 0x0100;
}
static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
spin_lock_irqsave(&beep_lock, flags);
/* EBUS speaker only has on/off state, the frequency does not
* appear to be programmable.
*/
if (count) {
if (beep_iobase & 0x2UL)
outb(1, beep_iobase);
else
outl(1, beep_iobase);
} else {
if (beep_iobase & 0x2UL)
outb(0, beep_iobase);
else
outl(0, beep_iobase);
}
spin_unlock_irqrestore(&beep_lock, flags);
return 0;
}
static int __init init_ebus_beep(struct linux_ebus_device *edev)
{
beep_iobase = edev->resource[0].start;
init_sparcspkr_struct();
sparcspkr_dev.name = sparcspkr_ebus_name;
sparcspkr_dev.event = ebus_spkr_event;
input_register_device(&sparcspkr_dev);
printk(KERN_INFO "input: %s\n", sparcspkr_ebus_name);
return 0;
}
#ifdef CONFIG_SPARC64
static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
spin_lock_irqsave(&beep_lock, flags);
if (count) {
/* enable counter 2 */
outb(inb(beep_iobase + 0x61) | 3, beep_iobase + 0x61);
/* set command for counter 2, 2 byte write */
outb(0xB6, beep_iobase + 0x43);
/* select desired HZ */
outb(count & 0xff, beep_iobase + 0x42);
outb((count >> 8) & 0xff, beep_iobase + 0x42);
} else {
/* disable counter 2 */
outb(inb_p(beep_iobase + 0x61) & 0xFC, beep_iobase + 0x61);
}
spin_unlock_irqrestore(&beep_lock, flags);
return 0;
}
static int __init init_isa_beep(struct sparc_isa_device *isa_dev)
{
beep_iobase = isa_dev->resource.start;
init_sparcspkr_struct();
sparcspkr_dev.name = sparcspkr_isa_name;
sparcspkr_dev.event = isa_spkr_event;
sparcspkr_dev.id.bustype = BUS_ISA;
input_register_device(&sparcspkr_dev);
printk(KERN_INFO "input: %s\n", sparcspkr_isa_name);
return 0;
}
#endif
static int __init sparcspkr_init(void)
{
struct linux_ebus *ebus;
struct linux_ebus_device *edev = NULL;
#ifdef CONFIG_SPARC64
struct sparc_isa_bridge *isa_br;
struct sparc_isa_device *isa_dev;
#endif
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_name, "beep"))
return init_ebus_beep(edev);
}
}
#ifdef CONFIG_SPARC64
for_each_isa(isa_br) {
for_each_isadev(isa_dev, isa_br) {
/* A hack, the beep device's base lives in
* the DMA isa node.
*/
if (!strcmp(isa_dev->prom_name, "dma"))
return init_isa_beep(isa_dev);
}
}
#endif
return -ENODEV;
}
static void __exit sparcspkr_exit(void)
{
input_unregister_device(&sparcspkr_dev);
}
module_init(sparcspkr_init);
module_exit(sparcspkr_exit);

View File

@@ -0,0 +1,431 @@
/*
* User level driver support for input subsystem
*
* Heavily based on evdev.c by Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
* 0.1 20/06/2002
* - first public version
*/
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uinput.h>
static int uinput_dev_open(struct input_dev *dev)
{
return 0;
}
static void uinput_dev_close(struct input_dev *dev)
{
}
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct uinput_device *udev;
udev = (struct uinput_device *)dev->private;
udev->buff[udev->head].type = type;
udev->buff[udev->head].code = code;
udev->buff[udev->head].value = value;
do_gettimeofday(&udev->buff[udev->head].time);
udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
wake_up_interruptible(&udev->waitq);
return 0;
}
static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
{
return 0;
}
static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
{
return 0;
}
static int uinput_create_device(struct uinput_device *udev)
{
if (!udev->dev->name) {
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
return -EINVAL;
}
udev->dev->open = uinput_dev_open;
udev->dev->close = uinput_dev_close;
udev->dev->event = uinput_dev_event;
udev->dev->upload_effect = uinput_dev_upload_effect;
udev->dev->erase_effect = uinput_dev_erase_effect;
udev->dev->private = udev;
init_waitqueue_head(&(udev->waitq));
input_register_device(udev->dev);
set_bit(UIST_CREATED, &(udev->state));
return 0;
}
static int uinput_destroy_device(struct uinput_device *udev)
{
if (!test_bit(UIST_CREATED, &(udev->state))) {
printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
return -EINVAL;
}
input_unregister_device(udev->dev);
clear_bit(UIST_CREATED, &(udev->state));
return 0;
}
static int uinput_open(struct inode *inode, struct file *file)
{
struct uinput_device *newdev;
struct input_dev *newinput;
newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
if (!newdev)
goto error;
memset(newdev, 0, sizeof(struct uinput_device));
newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
if (!newinput)
goto cleanup;
memset(newinput, 0, sizeof(struct input_dev));
newdev->dev = newinput;
file->private_data = newdev;
return 0;
cleanup:
kfree(newdev);
error:
return -ENOMEM;
}
static int uinput_validate_absbits(struct input_dev *dev)
{
unsigned int cnt;
int retval = 0;
for (cnt = 0; cnt < ABS_MAX; cnt++) {
if (!test_bit(cnt, dev->absbit))
continue;
if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
(dev->absmax[cnt] <= dev->absmin[cnt])) {
printk(KERN_DEBUG
"%s: invalid abs[%02x] min:%d max:%d\n",
UINPUT_NAME, cnt,
dev->absmin[cnt], dev->absmax[cnt]);
retval = -EINVAL;
break;
}
if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
(dev->absflat[cnt] > dev->absmax[cnt])) {
printk(KERN_DEBUG
"%s: absflat[%02x] out of range: %d "
"(min:%d/max:%d)\n",
UINPUT_NAME, cnt, dev->absflat[cnt],
dev->absmin[cnt], dev->absmax[cnt]);
retval = -EINVAL;
break;
}
}
return retval;
}
static int uinput_alloc_device(struct file *file, const char __user *buffer, size_t count)
{
struct uinput_user_dev *user_dev;
struct input_dev *dev;
struct uinput_device *udev;
int size,
retval;
retval = count;
udev = (struct uinput_device *)file->private_data;
dev = udev->dev;
user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
if (!user_dev) {
retval = -ENOMEM;
goto exit;
}
if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
retval = -EFAULT;
goto exit;
}
if (NULL != dev->name)
kfree(dev->name);
size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
dev->name = kmalloc(size, GFP_KERNEL);
if (!dev->name) {
retval = -ENOMEM;
goto exit;
}
strlcpy(dev->name, user_dev->name, size);
dev->id.bustype = user_dev->id.bustype;
dev->id.vendor = user_dev->id.vendor;
dev->id.product = user_dev->id.product;
dev->id.version = user_dev->id.version;
dev->ff_effects_max = user_dev->ff_effects_max;
size = sizeof(int) * (ABS_MAX + 1);
memcpy(dev->absmax, user_dev->absmax, size);
memcpy(dev->absmin, user_dev->absmin, size);
memcpy(dev->absfuzz, user_dev->absfuzz, size);
memcpy(dev->absflat, user_dev->absflat, size);
/* check if absmin/absmax/absfuzz/absflat are filled as
* told in Documentation/input/input-programming.txt */
if (test_bit(EV_ABS, dev->evbit)) {
retval = uinput_validate_absbits(dev);
if (retval < 0)
kfree(dev->name);
}
exit:
kfree(user_dev);
return retval;
}
static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct uinput_device *udev = file->private_data;
if (test_bit(UIST_CREATED, &(udev->state))) {
struct input_event ev;
if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
return -EFAULT;
input_event(udev->dev, ev.type, ev.code, ev.value);
}
else
count = uinput_alloc_device(file, buffer, count);
return count;
}
static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct uinput_device *udev = file->private_data;
int retval = 0;
if (!test_bit(UIST_CREATED, &(udev->state)))
return -ENODEV;
if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(udev->waitq,
(udev->head != udev->tail) ||
!test_bit(UIST_CREATED, &(udev->state)));
if (retval)
return retval;
if (!test_bit(UIST_CREATED, &(udev->state)))
return -ENODEV;
while ((udev->head != udev->tail) &&
(retval + sizeof(struct input_event) <= count)) {
if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
sizeof(struct input_event))) return -EFAULT;
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
retval += sizeof(struct input_event);
}
return retval;
}
static unsigned int uinput_poll(struct file *file, poll_table *wait)
{
struct uinput_device *udev = file->private_data;
if (!test_bit(UIST_CREATED, &(udev->state)))
return 0;
poll_wait(file, &udev->waitq, wait);
if (udev->head != udev->tail)
return POLLIN | POLLRDNORM;
return 0;
}
static int uinput_burn_device(struct uinput_device *udev)
{
if (test_bit(UIST_CREATED, &(udev->state)))
uinput_destroy_device(udev);
kfree(udev->dev);
kfree(udev);
return 0;
}
static int uinput_close(struct inode *inode, struct file *file)
{
return uinput_burn_device((struct uinput_device *)file->private_data);
}
static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int retval = 0;
struct uinput_device *udev;
udev = (struct uinput_device *)file->private_data;
/* device attributes can not be changed after the device is created */
if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
return -EINVAL;
switch (cmd) {
case UI_DEV_CREATE:
retval = uinput_create_device(udev);
break;
case UI_DEV_DESTROY:
retval = uinput_destroy_device(udev);
break;
case UI_SET_EVBIT:
if (arg > EV_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->evbit);
break;
case UI_SET_KEYBIT:
if (arg > KEY_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->keybit);
break;
case UI_SET_RELBIT:
if (arg > REL_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->relbit);
break;
case UI_SET_ABSBIT:
if (arg > ABS_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->absbit);
break;
case UI_SET_MSCBIT:
if (arg > MSC_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->mscbit);
break;
case UI_SET_LEDBIT:
if (arg > LED_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->ledbit);
break;
case UI_SET_SNDBIT:
if (arg > SND_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->sndbit);
break;
case UI_SET_FFBIT:
if (arg > FF_MAX) {
retval = -EINVAL;
break;
}
set_bit(arg, udev->dev->ffbit);
break;
default:
retval = -EFAULT;
}
return retval;
}
struct file_operations uinput_fops = {
.owner = THIS_MODULE,
.open = uinput_open,
.release = uinput_close,
.read = uinput_read,
.write = uinput_write,
.poll = uinput_poll,
.ioctl = uinput_ioctl,
};
static struct miscdevice uinput_misc = {
.fops = &uinput_fops,
.minor = UINPUT_MINOR,
.name = UINPUT_NAME,
};
static int __init uinput_init(void)
{
return misc_register(&uinput_misc);
}
static void __exit uinput_exit(void)
{
misc_deregister(&uinput_misc);
}
MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
MODULE_DESCRIPTION("User level driver support for input subsystem");
MODULE_LICENSE("GPL");
module_init(uinput_init);
module_exit(uinput_exit);

View File

@@ -0,0 +1 @@
cmd_drivers/input/mouse/built-in.o := ld -m elf_i386 -r -o drivers/input/mouse/built-in.o drivers/input/mouse/psmouse.o

View File

@@ -0,0 +1,246 @@
cmd_drivers/input/mouse/logips2pp.o := gcc -Wp,-MD,drivers/input/mouse/.logips2pp.o.d -nostdinc -iwithprefix include -D__KERNEL__ -Iinclude -Wall -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -fno-unit-at-a-time -march=pentium-mmx -Iinclude/asm-i386/mach-default -Wdeclaration-after-statement -DKBUILD_BASENAME=logips2pp -DKBUILD_MODNAME=psmouse -c -o drivers/input/mouse/logips2pp.o drivers/input/mouse/logips2pp.c
deps_drivers/input/mouse/logips2pp.o := \
drivers/input/mouse/logips2pp.c \
include/linux/input.h \
include/linux/time.h \
include/linux/types.h \
$(wildcard include/config/uid16.h) \
include/linux/config.h \
$(wildcard include/config/h.h) \
include/linux/posix_types.h \
include/linux/stddef.h \
include/linux/compiler.h \
include/linux/compiler-gcc3.h \
include/linux/compiler-gcc.h \
include/asm/posix_types.h \
include/asm/types.h \
$(wildcard include/config/highmem64g.h) \
$(wildcard include/config/lbd.h) \
include/linux/seqlock.h \
include/linux/spinlock.h \
$(wildcard include/config/smp.h) \
$(wildcard include/config/preempt.h) \
$(wildcard include/config/debug/spinlock.h) \
$(wildcard include/config/lockmeter.h) \
include/linux/preempt.h \
include/linux/linkage.h \
include/asm/linkage.h \
$(wildcard include/config/regparm.h) \
$(wildcard include/config/x86/alignment/16.h) \
include/linux/thread_info.h \
include/linux/bitops.h \
include/asm/bitops.h \
include/asm/thread_info.h \
$(wildcard include/config/4kstacks.h) \
$(wildcard include/config/debug/stack/usage.h) \
include/asm/page.h \
$(wildcard include/config/x86/use/3dnow.h) \
$(wildcard include/config/x86/pae.h) \
$(wildcard include/config/hugetlb/page.h) \
$(wildcard include/config/highmem4g.h) \
$(wildcard include/config/discontigmem.h) \
include/asm/processor.h \
$(wildcard include/config/mk8.h) \
$(wildcard include/config/mk7.h) \
include/asm/vm86.h \
include/asm/math_emu.h \
include/asm/sigcontext.h \
include/asm/segment.h \
include/asm/cpufeature.h \
include/asm/msr.h \
include/asm/system.h \
$(wildcard include/config/x86/cmpxchg.h) \
$(wildcard include/config/x86/oostore.h) \
include/linux/kernel.h \
$(wildcard include/config/debug/spinlock/sleep.h) \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.1/include/stdarg.h \
include/asm/byteorder.h \
$(wildcard include/config/x86/bswap.h) \
include/linux/byteorder/little_endian.h \
include/linux/byteorder/swab.h \
include/linux/byteorder/generic.h \
include/asm/bug.h \
include/asm-generic/bug.h \
include/linux/cache.h \
include/asm/cache.h \
$(wildcard include/config/x86/l1/cache/shift.h) \
include/linux/threads.h \
$(wildcard include/config/nr/cpus.h) \
include/asm/percpu.h \
include/asm-generic/percpu.h \
include/linux/stringify.h \
include/linux/list.h \
include/linux/prefetch.h \
include/linux/fs.h \
$(wildcard include/config/dnotify.h) \
$(wildcard include/config/quota.h) \
$(wildcard include/config/epoll.h) \
$(wildcard include/config/auditsyscall.h) \
$(wildcard include/config/security.h) \
include/linux/limits.h \
include/linux/wait.h \
include/asm/current.h \
include/linux/kdev_t.h \
include/linux/ioctl.h \
include/asm/ioctl.h \
include/linux/dcache.h \
include/asm/atomic.h \
$(wildcard include/config/m386.h) \
include/linux/rcupdate.h \
include/linux/percpu.h \
include/linux/slab.h \
$(wildcard include/config/.h) \
$(wildcard include/config/numa.h) \
include/linux/gfp.h \
include/linux/mmzone.h \
$(wildcard include/config/force/max/zoneorder.h) \
include/linux/numa.h \
include/linux/topology.h \
$(wildcard include/config/sched/smt.h) \
include/linux/cpumask.h \
$(wildcard include/config/hotplug/cpu.h) \
include/linux/bitmap.h \
include/linux/string.h \
include/asm/string.h \
include/linux/smp.h \
include/asm/topology.h \
include/asm-generic/topology.h \
include/linux/init.h \
$(wildcard include/config/modules.h) \
$(wildcard include/config/hotplug.h) \
include/linux/kmalloc_sizes.h \
$(wildcard include/config/mmu.h) \
$(wildcard include/config/large/allocs.h) \
include/linux/stat.h \
include/asm/stat.h \
include/linux/prio_tree.h \
include/linux/kobject.h \
include/linux/sysfs.h \
$(wildcard include/config/sysfs.h) \
include/linux/rwsem.h \
$(wildcard include/config/rwsem/generic/spinlock.h) \
include/asm/rwsem.h \
include/linux/kref.h \
include/linux/kobject_uevent.h \
$(wildcard include/config/kobject/uevent.h) \
include/linux/radix-tree.h \
include/linux/audit.h \
$(wildcard include/config/audit.h) \
include/asm/semaphore.h \
include/linux/quota.h \
include/linux/errno.h \
include/asm/errno.h \
include/asm-generic/errno.h \
include/asm-generic/errno-base.h \
include/linux/dqblk_xfs.h \
include/linux/dqblk_v1.h \
include/linux/dqblk_v2.h \
include/linux/nfs_fs_i.h \
include/linux/nfs.h \
include/linux/sunrpc/msg_prot.h \
include/linux/fcntl.h \
include/asm/fcntl.h \
include/linux/err.h \
include/linux/timer.h \
include/linux/serio.h \
include/linux/interrupt.h \
$(wildcard include/config/generic/hardirqs.h) \
$(wildcard include/config/generic/irq/probe.h) \
include/linux/hardirq.h \
include/linux/smp_lock.h \
$(wildcard include/config/lock/kernel.h) \
include/linux/sched.h \
$(wildcard include/config/keys.h) \
$(wildcard include/config/schedstats.h) \
$(wildcard include/config/magic/sysrq.h) \
include/asm/param.h \
include/linux/capability.h \
include/linux/timex.h \
$(wildcard include/config/time/interpolation.h) \
include/asm/timex.h \
$(wildcard include/config/x86/elan.h) \
$(wildcard include/config/x86/tsc.h) \
$(wildcard include/config/x86/generic.h) \
include/linux/jiffies.h \
include/asm/div64.h \
include/linux/rbtree.h \
include/asm/ptrace.h \
$(wildcard include/config/frame/pointer.h) \
include/asm/mmu.h \
include/linux/sem.h \
$(wildcard include/config/sysvipc.h) \
include/linux/ipc.h \
include/asm/ipcbuf.h \
include/asm/sembuf.h \
include/linux/signal.h \
include/asm/signal.h \
include/asm/siginfo.h \
include/asm-generic/siginfo.h \
include/linux/resource.h \
include/asm/resource.h \
include/linux/securebits.h \
include/linux/fs_struct.h \
include/linux/completion.h \
include/linux/pid.h \
include/linux/param.h \
include/linux/aio.h \
include/linux/workqueue.h \
include/linux/aio_abi.h \
include/asm/hardirq.h \
include/linux/irq.h \
$(wildcard include/config/arch/s390.h) \
include/asm/irq.h \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/irqbalance.h) \
include/asm-i386/mach-default/irq_vectors.h \
include/asm-i386/mach-default/irq_vectors_limits.h \
$(wildcard include/config/pci/msi.h) \
$(wildcard include/config/x86/io/apic.h) \
include/asm/hw_irq.h \
include/linux/profile.h \
$(wildcard include/config/proc/fs.h) \
$(wildcard include/config/profiling.h) \
include/asm/sections.h \
include/asm-generic/sections.h \
include/linux/irq_cpustat.h \
include/linux/device.h \
include/linux/ioport.h \
include/linux/module.h \
$(wildcard include/config/modversions.h) \
$(wildcard include/config/module/unload.h) \
$(wildcard include/config/kallsyms.h) \
include/linux/kmod.h \
$(wildcard include/config/kmod.h) \
include/linux/elf.h \
include/asm/elf.h \
include/asm/user.h \
include/linux/utsname.h \
include/linux/moduleparam.h \
include/asm/local.h \
include/asm/module.h \
$(wildcard include/config/m486.h) \
$(wildcard include/config/m586.h) \
$(wildcard include/config/m586tsc.h) \
$(wildcard include/config/m586mmx.h) \
$(wildcard include/config/m686.h) \
$(wildcard include/config/mpentiumii.h) \
$(wildcard include/config/mpentiumiii.h) \
$(wildcard include/config/mpentiumm.h) \
$(wildcard include/config/mpentium4.h) \
$(wildcard include/config/mk6.h) \
$(wildcard include/config/mcrusoe.h) \
$(wildcard include/config/mefficeon.h) \
$(wildcard include/config/mwinchipc6.h) \
$(wildcard include/config/mwinchip2.h) \
$(wildcard include/config/mwinchip3d.h) \
$(wildcard include/config/mcyrixiii.h) \
$(wildcard include/config/mviac3/2.h) \
include/linux/pm.h \
$(wildcard include/config/pm.h) \
drivers/input/mouse/psmouse.h \
drivers/input/mouse/logips2pp.h \
drivers/input/mouse/logips2pp.o: $(deps_drivers/input/mouse/logips2pp.o)
$(deps_drivers/input/mouse/logips2pp.o):

View File

@@ -0,0 +1,249 @@
cmd_drivers/input/mouse/psmouse-base.o := gcc -Wp,-MD,drivers/input/mouse/.psmouse-base.o.d -nostdinc -iwithprefix include -D__KERNEL__ -Iinclude -Wall -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -fno-unit-at-a-time -march=pentium-mmx -Iinclude/asm-i386/mach-default -Wdeclaration-after-statement -DKBUILD_BASENAME=psmouse_base -DKBUILD_MODNAME=psmouse -c -o drivers/input/mouse/psmouse-base.o drivers/input/mouse/psmouse-base.c
deps_drivers/input/mouse/psmouse-base.o := \
drivers/input/mouse/psmouse-base.c \
include/linux/delay.h \
include/asm/delay.h \
include/linux/module.h \
$(wildcard include/config/modules.h) \
$(wildcard include/config/modversions.h) \
$(wildcard include/config/module/unload.h) \
$(wildcard include/config/kallsyms.h) \
include/linux/config.h \
$(wildcard include/config/h.h) \
include/linux/sched.h \
$(wildcard include/config/keys.h) \
$(wildcard include/config/schedstats.h) \
$(wildcard include/config/smp.h) \
$(wildcard include/config/numa.h) \
$(wildcard include/config/security.h) \
$(wildcard include/config/preempt.h) \
$(wildcard include/config/magic/sysrq.h) \
include/asm/param.h \
include/linux/capability.h \
include/linux/types.h \
$(wildcard include/config/uid16.h) \
include/linux/posix_types.h \
include/linux/stddef.h \
include/linux/compiler.h \
include/linux/compiler-gcc3.h \
include/linux/compiler-gcc.h \
include/asm/posix_types.h \
include/asm/types.h \
$(wildcard include/config/highmem64g.h) \
$(wildcard include/config/lbd.h) \
include/linux/spinlock.h \
$(wildcard include/config/debug/spinlock.h) \
$(wildcard include/config/lockmeter.h) \
include/linux/preempt.h \
include/linux/linkage.h \
include/asm/linkage.h \
$(wildcard include/config/regparm.h) \
$(wildcard include/config/x86/alignment/16.h) \
include/linux/thread_info.h \
include/linux/bitops.h \
include/asm/bitops.h \
include/asm/thread_info.h \
$(wildcard include/config/4kstacks.h) \
$(wildcard include/config/debug/stack/usage.h) \
include/asm/page.h \
$(wildcard include/config/x86/use/3dnow.h) \
$(wildcard include/config/x86/pae.h) \
$(wildcard include/config/hugetlb/page.h) \
$(wildcard include/config/highmem4g.h) \
$(wildcard include/config/discontigmem.h) \
include/asm/processor.h \
$(wildcard include/config/mk8.h) \
$(wildcard include/config/mk7.h) \
include/asm/vm86.h \
include/asm/math_emu.h \
include/asm/sigcontext.h \
include/asm/segment.h \
include/asm/cpufeature.h \
include/asm/msr.h \
include/asm/system.h \
$(wildcard include/config/x86/cmpxchg.h) \
$(wildcard include/config/x86/oostore.h) \
include/linux/kernel.h \
$(wildcard include/config/debug/spinlock/sleep.h) \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.1/include/stdarg.h \
include/asm/byteorder.h \
$(wildcard include/config/x86/bswap.h) \
include/linux/byteorder/little_endian.h \
include/linux/byteorder/swab.h \
include/linux/byteorder/generic.h \
include/asm/bug.h \
include/asm-generic/bug.h \
include/linux/cache.h \
include/asm/cache.h \
$(wildcard include/config/x86/l1/cache/shift.h) \
include/linux/threads.h \
$(wildcard include/config/nr/cpus.h) \
include/asm/percpu.h \
include/asm-generic/percpu.h \
include/linux/stringify.h \
include/linux/timex.h \
$(wildcard include/config/time/interpolation.h) \
include/linux/time.h \
include/linux/seqlock.h \
include/asm/timex.h \
$(wildcard include/config/x86/elan.h) \
$(wildcard include/config/x86/tsc.h) \
$(wildcard include/config/x86/generic.h) \
include/linux/jiffies.h \
include/asm/div64.h \
include/linux/rbtree.h \
include/linux/cpumask.h \
$(wildcard include/config/hotplug/cpu.h) \
include/linux/bitmap.h \
include/linux/string.h \
include/asm/string.h \
include/asm/semaphore.h \
include/asm/atomic.h \
$(wildcard include/config/m386.h) \
include/linux/wait.h \
include/linux/list.h \
include/linux/prefetch.h \
include/asm/current.h \
include/linux/rwsem.h \
$(wildcard include/config/rwsem/generic/spinlock.h) \
include/asm/rwsem.h \
include/asm/ptrace.h \
$(wildcard include/config/frame/pointer.h) \
include/asm/mmu.h \
include/linux/smp.h \
include/linux/sem.h \
$(wildcard include/config/sysvipc.h) \
include/linux/ipc.h \
include/asm/ipcbuf.h \
include/asm/sembuf.h \
include/linux/signal.h \
include/asm/signal.h \
include/asm/siginfo.h \
include/asm-generic/siginfo.h \
include/linux/resource.h \
include/asm/resource.h \
include/linux/securebits.h \
include/linux/fs_struct.h \
include/linux/completion.h \
include/linux/pid.h \
include/linux/percpu.h \
include/linux/slab.h \
$(wildcard include/config/.h) \
include/linux/gfp.h \
include/linux/mmzone.h \
$(wildcard include/config/force/max/zoneorder.h) \
include/linux/numa.h \
include/linux/topology.h \
$(wildcard include/config/sched/smt.h) \
include/asm/topology.h \
include/asm-generic/topology.h \
include/linux/init.h \
$(wildcard include/config/hotplug.h) \
include/linux/kmalloc_sizes.h \
$(wildcard include/config/mmu.h) \
$(wildcard include/config/large/allocs.h) \
include/linux/param.h \
include/linux/timer.h \
include/linux/aio.h \
include/linux/workqueue.h \
include/linux/aio_abi.h \
include/linux/stat.h \
include/asm/stat.h \
include/linux/kmod.h \
$(wildcard include/config/kmod.h) \
include/linux/errno.h \
include/asm/errno.h \
include/asm-generic/errno.h \
include/asm-generic/errno-base.h \
include/linux/elf.h \
include/asm/elf.h \
include/asm/user.h \
include/linux/utsname.h \
include/linux/kobject.h \
include/linux/sysfs.h \
$(wildcard include/config/sysfs.h) \
include/linux/kref.h \
include/linux/kobject_uevent.h \
$(wildcard include/config/kobject/uevent.h) \
include/linux/moduleparam.h \
include/asm/local.h \
include/asm/module.h \
$(wildcard include/config/m486.h) \
$(wildcard include/config/m586.h) \
$(wildcard include/config/m586tsc.h) \
$(wildcard include/config/m586mmx.h) \
$(wildcard include/config/m686.h) \
$(wildcard include/config/mpentiumii.h) \
$(wildcard include/config/mpentiumiii.h) \
$(wildcard include/config/mpentiumm.h) \
$(wildcard include/config/mpentium4.h) \
$(wildcard include/config/mk6.h) \
$(wildcard include/config/mcrusoe.h) \
$(wildcard include/config/mefficeon.h) \
$(wildcard include/config/mwinchipc6.h) \
$(wildcard include/config/mwinchip2.h) \
$(wildcard include/config/mwinchip3d.h) \
$(wildcard include/config/mcyrixiii.h) \
$(wildcard include/config/mviac3/2.h) \
include/linux/interrupt.h \
$(wildcard include/config/generic/hardirqs.h) \
$(wildcard include/config/generic/irq/probe.h) \
include/linux/hardirq.h \
include/linux/smp_lock.h \
$(wildcard include/config/lock/kernel.h) \
include/asm/hardirq.h \
include/linux/irq.h \
$(wildcard include/config/arch/s390.h) \
include/asm/irq.h \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/irqbalance.h) \
include/asm-i386/mach-default/irq_vectors.h \
include/asm-i386/mach-default/irq_vectors_limits.h \
$(wildcard include/config/pci/msi.h) \
$(wildcard include/config/x86/io/apic.h) \
include/asm/hw_irq.h \
include/linux/profile.h \
$(wildcard include/config/proc/fs.h) \
$(wildcard include/config/profiling.h) \
include/asm/sections.h \
include/asm-generic/sections.h \
include/linux/irq_cpustat.h \
include/linux/input.h \
include/linux/fs.h \
$(wildcard include/config/dnotify.h) \
$(wildcard include/config/quota.h) \
$(wildcard include/config/epoll.h) \
$(wildcard include/config/auditsyscall.h) \
include/linux/limits.h \
include/linux/kdev_t.h \
include/linux/ioctl.h \
include/asm/ioctl.h \
include/linux/dcache.h \
include/linux/rcupdate.h \
include/linux/prio_tree.h \
include/linux/radix-tree.h \
include/linux/audit.h \
$(wildcard include/config/audit.h) \
include/linux/quota.h \
include/linux/dqblk_xfs.h \
include/linux/dqblk_v1.h \
include/linux/dqblk_v2.h \
include/linux/nfs_fs_i.h \
include/linux/nfs.h \
include/linux/sunrpc/msg_prot.h \
include/linux/fcntl.h \
include/asm/fcntl.h \
include/linux/err.h \
include/linux/serio.h \
include/linux/device.h \
include/linux/ioport.h \
include/linux/pm.h \
$(wildcard include/config/pm.h) \
drivers/input/mouse/psmouse.h \
drivers/input/mouse/synaptics.h \
drivers/input/mouse/logips2pp.h \
drivers/input/mouse/psmouse-base.o: $(deps_drivers/input/mouse/psmouse-base.o)
$(deps_drivers/input/mouse/psmouse-base.o):

View File

@@ -0,0 +1 @@
cmd_drivers/input/mouse/psmouse.o := ld -m elf_i386 -r -o drivers/input/mouse/psmouse.o drivers/input/mouse/psmouse-base.o drivers/input/mouse/logips2pp.o drivers/input/mouse/synaptics.o

View File

@@ -0,0 +1,246 @@
cmd_drivers/input/mouse/synaptics.o := gcc -Wp,-MD,drivers/input/mouse/.synaptics.o.d -nostdinc -iwithprefix include -D__KERNEL__ -Iinclude -Wall -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -fomit-frame-pointer -pipe -msoft-float -mpreferred-stack-boundary=2 -fno-unit-at-a-time -march=pentium-mmx -Iinclude/asm-i386/mach-default -Wdeclaration-after-statement -DKBUILD_BASENAME=synaptics -DKBUILD_MODNAME=psmouse -c -o drivers/input/mouse/synaptics.o drivers/input/mouse/synaptics.c
deps_drivers/input/mouse/synaptics.o := \
drivers/input/mouse/synaptics.c \
include/linux/module.h \
$(wildcard include/config/modules.h) \
$(wildcard include/config/modversions.h) \
$(wildcard include/config/module/unload.h) \
$(wildcard include/config/kallsyms.h) \
include/linux/config.h \
$(wildcard include/config/h.h) \
include/linux/sched.h \
$(wildcard include/config/keys.h) \
$(wildcard include/config/schedstats.h) \
$(wildcard include/config/smp.h) \
$(wildcard include/config/numa.h) \
$(wildcard include/config/security.h) \
$(wildcard include/config/preempt.h) \
$(wildcard include/config/magic/sysrq.h) \
include/asm/param.h \
include/linux/capability.h \
include/linux/types.h \
$(wildcard include/config/uid16.h) \
include/linux/posix_types.h \
include/linux/stddef.h \
include/linux/compiler.h \
include/linux/compiler-gcc3.h \
include/linux/compiler-gcc.h \
include/asm/posix_types.h \
include/asm/types.h \
$(wildcard include/config/highmem64g.h) \
$(wildcard include/config/lbd.h) \
include/linux/spinlock.h \
$(wildcard include/config/debug/spinlock.h) \
$(wildcard include/config/lockmeter.h) \
include/linux/preempt.h \
include/linux/linkage.h \
include/asm/linkage.h \
$(wildcard include/config/regparm.h) \
$(wildcard include/config/x86/alignment/16.h) \
include/linux/thread_info.h \
include/linux/bitops.h \
include/asm/bitops.h \
include/asm/thread_info.h \
$(wildcard include/config/4kstacks.h) \
$(wildcard include/config/debug/stack/usage.h) \
include/asm/page.h \
$(wildcard include/config/x86/use/3dnow.h) \
$(wildcard include/config/x86/pae.h) \
$(wildcard include/config/hugetlb/page.h) \
$(wildcard include/config/highmem4g.h) \
$(wildcard include/config/discontigmem.h) \
include/asm/processor.h \
$(wildcard include/config/mk8.h) \
$(wildcard include/config/mk7.h) \
include/asm/vm86.h \
include/asm/math_emu.h \
include/asm/sigcontext.h \
include/asm/segment.h \
include/asm/cpufeature.h \
include/asm/msr.h \
include/asm/system.h \
$(wildcard include/config/x86/cmpxchg.h) \
$(wildcard include/config/x86/oostore.h) \
include/linux/kernel.h \
$(wildcard include/config/debug/spinlock/sleep.h) \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.1/include/stdarg.h \
include/asm/byteorder.h \
$(wildcard include/config/x86/bswap.h) \
include/linux/byteorder/little_endian.h \
include/linux/byteorder/swab.h \
include/linux/byteorder/generic.h \
include/asm/bug.h \
include/asm-generic/bug.h \
include/linux/cache.h \
include/asm/cache.h \
$(wildcard include/config/x86/l1/cache/shift.h) \
include/linux/threads.h \
$(wildcard include/config/nr/cpus.h) \
include/asm/percpu.h \
include/asm-generic/percpu.h \
include/linux/stringify.h \
include/linux/timex.h \
$(wildcard include/config/time/interpolation.h) \
include/linux/time.h \
include/linux/seqlock.h \
include/asm/timex.h \
$(wildcard include/config/x86/elan.h) \
$(wildcard include/config/x86/tsc.h) \
$(wildcard include/config/x86/generic.h) \
include/linux/jiffies.h \
include/asm/div64.h \
include/linux/rbtree.h \
include/linux/cpumask.h \
$(wildcard include/config/hotplug/cpu.h) \
include/linux/bitmap.h \
include/linux/string.h \
include/asm/string.h \
include/asm/semaphore.h \
include/asm/atomic.h \
$(wildcard include/config/m386.h) \
include/linux/wait.h \
include/linux/list.h \
include/linux/prefetch.h \
include/asm/current.h \
include/linux/rwsem.h \
$(wildcard include/config/rwsem/generic/spinlock.h) \
include/asm/rwsem.h \
include/asm/ptrace.h \
$(wildcard include/config/frame/pointer.h) \
include/asm/mmu.h \
include/linux/smp.h \
include/linux/sem.h \
$(wildcard include/config/sysvipc.h) \
include/linux/ipc.h \
include/asm/ipcbuf.h \
include/asm/sembuf.h \
include/linux/signal.h \
include/asm/signal.h \
include/asm/siginfo.h \
include/asm-generic/siginfo.h \
include/linux/resource.h \
include/asm/resource.h \
include/linux/securebits.h \
include/linux/fs_struct.h \
include/linux/completion.h \
include/linux/pid.h \
include/linux/percpu.h \
include/linux/slab.h \
$(wildcard include/config/.h) \
include/linux/gfp.h \
include/linux/mmzone.h \
$(wildcard include/config/force/max/zoneorder.h) \
include/linux/numa.h \
include/linux/topology.h \
$(wildcard include/config/sched/smt.h) \
include/asm/topology.h \
include/asm-generic/topology.h \
include/linux/init.h \
$(wildcard include/config/hotplug.h) \
include/linux/kmalloc_sizes.h \
$(wildcard include/config/mmu.h) \
$(wildcard include/config/large/allocs.h) \
include/linux/param.h \
include/linux/timer.h \
include/linux/aio.h \
include/linux/workqueue.h \
include/linux/aio_abi.h \
include/linux/stat.h \
include/asm/stat.h \
include/linux/kmod.h \
$(wildcard include/config/kmod.h) \
include/linux/errno.h \
include/asm/errno.h \
include/asm-generic/errno.h \
include/asm-generic/errno-base.h \
include/linux/elf.h \
include/asm/elf.h \
include/asm/user.h \
include/linux/utsname.h \
include/linux/kobject.h \
include/linux/sysfs.h \
$(wildcard include/config/sysfs.h) \
include/linux/kref.h \
include/linux/kobject_uevent.h \
$(wildcard include/config/kobject/uevent.h) \
include/linux/moduleparam.h \
include/asm/local.h \
include/asm/module.h \
$(wildcard include/config/m486.h) \
$(wildcard include/config/m586.h) \
$(wildcard include/config/m586tsc.h) \
$(wildcard include/config/m586mmx.h) \
$(wildcard include/config/m686.h) \
$(wildcard include/config/mpentiumii.h) \
$(wildcard include/config/mpentiumiii.h) \
$(wildcard include/config/mpentiumm.h) \
$(wildcard include/config/mpentium4.h) \
$(wildcard include/config/mk6.h) \
$(wildcard include/config/mcrusoe.h) \
$(wildcard include/config/mefficeon.h) \
$(wildcard include/config/mwinchipc6.h) \
$(wildcard include/config/mwinchip2.h) \
$(wildcard include/config/mwinchip3d.h) \
$(wildcard include/config/mcyrixiii.h) \
$(wildcard include/config/mviac3/2.h) \
include/linux/input.h \
include/linux/fs.h \
$(wildcard include/config/dnotify.h) \
$(wildcard include/config/quota.h) \
$(wildcard include/config/epoll.h) \
$(wildcard include/config/auditsyscall.h) \
include/linux/limits.h \
include/linux/kdev_t.h \
include/linux/ioctl.h \
include/asm/ioctl.h \
include/linux/dcache.h \
include/linux/rcupdate.h \
include/linux/prio_tree.h \
include/linux/radix-tree.h \
include/linux/audit.h \
$(wildcard include/config/audit.h) \
include/linux/quota.h \
include/linux/dqblk_xfs.h \
include/linux/dqblk_v1.h \
include/linux/dqblk_v2.h \
include/linux/nfs_fs_i.h \
include/linux/nfs.h \
include/linux/sunrpc/msg_prot.h \
include/linux/fcntl.h \
include/asm/fcntl.h \
include/linux/err.h \
include/linux/serio.h \
include/linux/interrupt.h \
$(wildcard include/config/generic/hardirqs.h) \
$(wildcard include/config/generic/irq/probe.h) \
include/linux/hardirq.h \
include/linux/smp_lock.h \
$(wildcard include/config/lock/kernel.h) \
include/asm/hardirq.h \
include/linux/irq.h \
$(wildcard include/config/arch/s390.h) \
include/asm/irq.h \
$(wildcard include/config/x86/local/apic.h) \
$(wildcard include/config/irqbalance.h) \
include/asm-i386/mach-default/irq_vectors.h \
include/asm-i386/mach-default/irq_vectors_limits.h \
$(wildcard include/config/pci/msi.h) \
$(wildcard include/config/x86/io/apic.h) \
include/asm/hw_irq.h \
include/linux/profile.h \
$(wildcard include/config/proc/fs.h) \
$(wildcard include/config/profiling.h) \
include/asm/sections.h \
include/asm-generic/sections.h \
include/linux/irq_cpustat.h \
include/linux/device.h \
include/linux/ioport.h \
include/linux/pm.h \
$(wildcard include/config/pm.h) \
drivers/input/mouse/psmouse.h \
drivers/input/mouse/synaptics.h \
drivers/input/mouse/synaptics.o: $(deps_drivers/input/mouse/synaptics.o)
$(deps_drivers/input/mouse/synaptics.o):

View File

@@ -0,0 +1,130 @@
#
# Mouse driver configuration
#
config INPUT_MOUSE
bool "Mice"
default y
depends on INPUT
help
Say Y here, and a list of supported mice will be displayed.
This option doesn't affect the kernel.
If unsure, say Y.
config MOUSE_PS2
tristate "PS/2 mouse"
default y
depends on INPUT && INPUT_MOUSE
select SERIO
select SERIO_I8042 if PC
select SERIO_GSCPS2 if GSC
---help---
Say Y here if you have a PS/2 mouse connected to your system. This
includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
mice with wheels and extra buttons, Microsoft, Logitech or Genius
compatible.
Synaptics TouchPad users might be interested in a specialized
XFree86 driver at:
http://w1.894.telia.com/~u89404340/touchpad/index.html
and a new verion of GPM at:
http://www.geocities.com/dt_or/gpm/gpm.html
to take advantage of the advanced features of the touchpad.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called psmouse.
config MOUSE_SERIAL
tristate "Serial mouse"
depends on INPUT && INPUT_MOUSE
select SERIO
---help---
Say Y here if you have a serial (RS-232, COM port) mouse connected
to your system. This includes Sun, MouseSystems, Microsoft,
Logitech and all other compatible serial mice.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called sermouse.
config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on INPUT && INPUT_MOUSE && ISA
help
Say Y here if you have an InPort, Microsoft or ATI XL busmouse.
They are rather rare these days.
To compile this driver as a module, choose M here: the
module will be called inport.
config MOUSE_ATIXL
bool "ATI XL variant"
depends on MOUSE_INPORT
help
Say Y here if your mouse is of the ATI XL variety.
config MOUSE_LOGIBM
tristate "Logitech busmouse"
depends on INPUT && INPUT_MOUSE && ISA
help
Say Y here if you have a Logitech busmouse.
They are rather rare these days.
To compile this driver as a module, choose M here: the
module will be called logibm.
config MOUSE_PC110PAD
tristate "IBM PC110 touchpad"
depends on INPUT && INPUT_MOUSE && ISA
help
Say Y if you have the IBM PC-110 micro-notebook and want its
touchpad supported.
To compile this driver as a module, choose M here: the
module will be called pc110pad.
config MOUSE_MAPLE
tristate "Maple bus mouse"
depends on SH_DREAMCAST && INPUT && INPUT_MOUSE && MAPLE
help
Say Y if you have a DreamCast console and a mouse attached to
its Maple bus.
To compile this driver as a module, choose M here: the
module will be called maplemouse.
config MOUSE_AMIGA
tristate "Amiga mouse"
depends on AMIGA && INPUT && INPUT_MOUSE
help
Say Y here if you have an Amiga and want its native mouse
supported by the kernel.
To compile this driver as a module, choose M here: the
module will be called amimouse.
config MOUSE_RISCPC
tristate "Acorn RiscPC mouse"
depends on ARCH_ACORN && INPUT && INPUT_MOUSE
help
Say Y here if you have the Acorn RiscPC computer and want its
native mouse supported.
To compile this driver as a module, choose M here: the
module will be called rpcmouse.
config MOUSE_VSXXXAA
tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet"
depends on INPUT && INPUT_MOUSE
select SERIO
help
Say Y (or M) if you want to use a DEC VSXXX-AA (hockey
puck) or a VSXXX-GA (rectangular) mouse. Theses mice are
typically used on DECstations or VAXstations, but can also
be used on any box capable of RS232 (with some adaptor
described in the source file). This driver also works with the
digitizer (VSXXX-AB) DEC produced.

View File

@@ -0,0 +1,17 @@
#
# Makefile for the mouse drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
psmouse-objs := psmouse-base.o logips2pp.o synaptics.o

View File

@@ -0,0 +1,137 @@
/*
* Amiga mouse driver for Linux/m68k
*
* Copyright (c) 2000-2002 Vojtech Pavlik
*
* Based on the work of:
* Michael Rausch James Banks
* Matther Dillon David Giller
* Nathan Laredo Linus Torvalds
* Johan Myreen Jes Sorensen
* Russell King
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Amiga mouse driver");
MODULE_LICENSE("GPL");
static int amimouse_used = 0;
static int amimouse_lastx, amimouse_lasty;
static struct input_dev amimouse_dev;
static char *amimouse_name = "Amiga mouse";
static char *amimouse_phys = "amimouse/input0";
static irqreturn_t amimouse_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
unsigned short joy0dat, potgor;
int nx, ny, dx, dy;
joy0dat = custom.joy0dat;
nx = joy0dat & 0xff;
ny = joy0dat >> 8;
dx = nx - amimouse_lastx;
dy = ny - amimouse_lasty;
if (dx < -127) dx = (256 + nx) - amimouse_lastx;
if (dx > 127) dx = (nx - 256) - amimouse_lastx;
if (dy < -127) dy = (256 + ny) - amimouse_lasty;
if (dy > 127) dy = (ny - 256) - amimouse_lasty;
amimouse_lastx = nx;
amimouse_lasty = ny;
potgor = custom.potgor;
input_regs(&amimouse_dev, fp);
input_report_rel(&amimouse_dev, REL_X, dx);
input_report_rel(&amimouse_dev, REL_Y, dy);
input_report_key(&amimouse_dev, BTN_LEFT, ciaa.pra & 0x40);
input_report_key(&amimouse_dev, BTN_MIDDLE, potgor & 0x0100);
input_report_key(&amimouse_dev, BTN_RIGHT, potgor & 0x0400);
input_sync(&amimouse_dev);
return IRQ_HANDLED;
}
static int amimouse_open(struct input_dev *dev)
{
unsigned short joy0dat;
if (amimouse_used++)
return 0;
joy0dat = custom.joy0dat;
amimouse_lastx = joy0dat & 0xff;
amimouse_lasty = joy0dat >> 8;
if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) {
amimouse_used--;
printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
return -EBUSY;
}
return 0;
}
static void amimouse_close(struct input_dev *dev)
{
if (!--amimouse_used)
free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt);
}
static int __init amimouse_init(void)
{
if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
return -ENODEV;
amimouse_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
amimouse_dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
amimouse_dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
amimouse_dev.open = amimouse_open;
amimouse_dev.close = amimouse_close;
amimouse_dev.name = amimouse_name;
amimouse_dev.phys = amimouse_phys;
amimouse_dev.id.bustype = BUS_AMIGA;
amimouse_dev.id.vendor = 0x0001;
amimouse_dev.id.product = 0x0002;
amimouse_dev.id.version = 0x0100;
input_register_device(&amimouse_dev);
printk(KERN_INFO "input: %s at joy0dat\n", amimouse_name);
return 0;
}
static void __exit amimouse_exit(void)
{
input_unregister_device(&amimouse_dev);
}
module_init(amimouse_init);
module_exit(amimouse_exit);

Binary file not shown.

View File

@@ -0,0 +1,196 @@
/*
* $Id: inport.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* Teemu Rantanen Derrick Cole
* Peter Cervasio Christoph Niemann
* Philip Blundell Russell King
* Bob Harris
*/
/*
* Inport (ATI XL and Microsoft) busmouse driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/config.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
MODULE_LICENSE("GPL");
#define INPORT_BASE 0x23c
#define INPORT_EXTENT 4
#define INPORT_CONTROL_PORT INPORT_BASE + 0
#define INPORT_DATA_PORT INPORT_BASE + 1
#define INPORT_SIGNATURE_PORT INPORT_BASE + 2
#define INPORT_REG_BTNS 0x00
#define INPORT_REG_X 0x01
#define INPORT_REG_Y 0x02
#define INPORT_REG_MODE 0x07
#define INPORT_RESET 0x80
#ifdef CONFIG_INPUT_ATIXL
#define INPORT_NAME "ATI XL Mouse"
#define INPORT_VENDOR 0x0002
#define INPORT_SPEED_30HZ 0x01
#define INPORT_SPEED_50HZ 0x02
#define INPORT_SPEED_100HZ 0x03
#define INPORT_SPEED_200HZ 0x04
#define INPORT_MODE_BASE INPORT_SPEED_100HZ
#define INPORT_MODE_IRQ 0x08
#else
#define INPORT_NAME "Microsoft InPort Mouse"
#define INPORT_VENDOR 0x0001
#define INPORT_MODE_BASE 0x10
#define INPORT_MODE_IRQ 0x01
#endif
#define INPORT_MODE_HOLD 0x20
#define INPORT_IRQ 5
static int inport_irq = INPORT_IRQ;
module_param_named(irq, inport_irq, uint, 0);
MODULE_PARM_DESC(irq, "IRQ number (5=default)");
__obsolete_setup("inport_irq=");
static int inport_used;
static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int inport_open(struct input_dev *dev)
{
if (!inport_used++) {
if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
return -EBUSY;
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
}
return 0;
}
static void inport_close(struct input_dev *dev)
{
if (!--inport_used) {
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
free_irq(inport_irq, NULL);
}
}
static struct input_dev inport_dev = {
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
.open = inport_open,
.close = inport_close,
.name = INPORT_NAME,
.phys = "isa023c/input0",
.id = {
.bustype = BUS_ISA,
.vendor = INPORT_VENDOR,
.product = 0x0001,
.version = 0x0100,
},
};
static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned char buttons;
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
input_regs(&inport_dev, regs);
outb(INPORT_REG_X, INPORT_CONTROL_PORT);
input_report_rel(&inport_dev, REL_X, inb(INPORT_DATA_PORT));
outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
input_report_rel(&inport_dev, REL_Y, inb(INPORT_DATA_PORT));
outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
buttons = inb(INPORT_DATA_PORT);
input_report_key(&inport_dev, BTN_MIDDLE, buttons & 1);
input_report_key(&inport_dev, BTN_LEFT, buttons & 2);
input_report_key(&inport_dev, BTN_RIGHT, buttons & 4);
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
input_sync(&inport_dev);
return IRQ_HANDLED;
}
static int __init inport_init(void)
{
unsigned char a,b,c;
if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
return -EBUSY;
}
a = inb(INPORT_SIGNATURE_PORT);
b = inb(INPORT_SIGNATURE_PORT);
c = inb(INPORT_SIGNATURE_PORT);
if (( a == b ) || ( a != c )) {
release_region(INPORT_BASE, INPORT_EXTENT);
printk(KERN_ERR "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
return -ENODEV;
}
outb(INPORT_RESET, INPORT_CONTROL_PORT);
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
input_register_device(&inport_dev);
printk(KERN_INFO "input: " INPORT_NAME " at %#x irq %d\n", INPORT_BASE, inport_irq);
return 0;
}
static void __exit inport_exit(void)
{
input_unregister_device(&inport_dev);
release_region(INPORT_BASE, INPORT_EXTENT);
}
module_init(inport_init);
module_exit(inport_exit);

View File

@@ -0,0 +1,183 @@
/*
* $Id: logibm.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* James Banks Matthew Dillon
* David Giller Nathan Laredo
* Linus Torvalds Johan Myreen
* Cliff Matthews Philip Blundell
* Russell King
*/
/*
* Logitech Bus Mouse Driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Logitech busmouse driver");
MODULE_LICENSE("GPL");
#define LOGIBM_BASE 0x23c
#define LOGIBM_EXTENT 4
#define LOGIBM_DATA_PORT LOGIBM_BASE + 0
#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1
#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2
#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3
#define LOGIBM_ENABLE_IRQ 0x00
#define LOGIBM_DISABLE_IRQ 0x10
#define LOGIBM_READ_X_LOW 0x80
#define LOGIBM_READ_X_HIGH 0xa0
#define LOGIBM_READ_Y_LOW 0xc0
#define LOGIBM_READ_Y_HIGH 0xe0
#define LOGIBM_DEFAULT_MODE 0x90
#define LOGIBM_CONFIG_BYTE 0x91
#define LOGIBM_SIGNATURE_BYTE 0xa5
#define LOGIBM_IRQ 5
static int logibm_irq = LOGIBM_IRQ;
module_param_named(irq, logibm_irq, uint, 0);
MODULE_PARM_DESC(irq, "IRQ number (5=default)");
__obsolete_setup("logibm_irq=");
static int logibm_used = 0;
static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int logibm_open(struct input_dev *dev)
{
if (logibm_used++)
return 0;
if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) {
logibm_used--;
printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq);
return -EBUSY;
}
outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
return 0;
}
static void logibm_close(struct input_dev *dev)
{
if (--logibm_used)
return;
outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
free_irq(logibm_irq, NULL);
}
static struct input_dev logibm_dev = {
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
.open = logibm_open,
.close = logibm_close,
.name = "Logitech bus mouse",
.phys = "isa023c/input0",
.id = {
.bustype = BUS_ISA,
.vendor = 0x0003,
.product = 0x0001,
.version = 0x0100,
},
};
static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
char dx, dy;
unsigned char buttons;
outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT);
dx = (inb(LOGIBM_DATA_PORT) & 0xf);
outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT);
dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4;
outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT);
dy = (inb(LOGIBM_DATA_PORT) & 0xf);
outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT);
buttons = inb(LOGIBM_DATA_PORT);
dy |= (buttons & 0xf) << 4;
buttons = ~buttons >> 5;
input_regs(&logibm_dev, regs);
input_report_rel(&logibm_dev, REL_X, dx);
input_report_rel(&logibm_dev, REL_Y, dy);
input_report_key(&logibm_dev, BTN_RIGHT, buttons & 1);
input_report_key(&logibm_dev, BTN_MIDDLE, buttons & 2);
input_report_key(&logibm_dev, BTN_LEFT, buttons & 4);
input_sync(&logibm_dev);
outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
return IRQ_HANDLED;
}
static int __init logibm_init(void)
{
if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
return -EBUSY;
}
outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT);
outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT);
udelay(100);
if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
release_region(LOGIBM_BASE, LOGIBM_EXTENT);
printk(KERN_ERR "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
return -ENODEV;
}
outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
input_register_device(&logibm_dev);
printk(KERN_INFO "input: Logitech bus mouse at %#x irq %d\n", LOGIBM_BASE, logibm_irq);
return 0;
}
static void __exit logibm_exit(void)
{
input_unregister_device(&logibm_dev);
release_region(LOGIBM_BASE, LOGIBM_EXTENT);
}
module_init(logibm_init);
module_exit(logibm_exit);

View File

@@ -0,0 +1,310 @@
/*
* Logitech PS/2++ mouse driver
*
* Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2003 Eric Wong <eric@yhbt.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/input.h>
#include <linux/serio.h>
#include "psmouse.h"
#include "logips2pp.h"
/* Logitech mouse types */
#define PS2PP_KIND_WHEEL 1
#define PS2PP_KIND_MX 2
#define PS2PP_KIND_TP3 3
/* Logitech mouse features */
#define PS2PP_WHEEL 0x01
#define PS2PP_HWHEEL 0x02
#define PS2PP_SIDE_BTN 0x04
#define PS2PP_EXTRA_BTN 0x08
#define PS2PP_TASK_BTN 0x10
#define PS2PP_NAV_BTN 0x20
struct ps2pp_info {
const int model;
unsigned const int kind;
unsigned const int features;
};
/*
* Process a PS2++ or PS2T++ packet.
*/
void ps2pp_process_packet(struct psmouse *psmouse)
{
struct input_dev *dev = &psmouse->dev;
unsigned char *packet = psmouse->packet;
if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
case 0x0d: /* Mouse extra info */
input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
(int) (packet[2] & 8) - (int) (packet[2] & 7));
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
break;
case 0x0f: /* TouchPad extra info */
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
packet[0] = packet[2] | 0x08;
break;
#ifdef DEBUG
default:
printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
(packet[1] >> 4) | (packet[0] & 0x30));
#endif
}
packet[0] &= 0x0f;
packet[1] = 0;
packet[2] = 0;
}
}
/*
* ps2pp_cmd() sends a PS2++ command, sliced into two bit
* pieces through the SETRES command. This is needed to send extended
* commands to mice on notebooks that try to understand the PS/2 protocol
* Ugly.
*/
static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
{
if (psmouse_sliced_command(psmouse, command))
return -1;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL))
return -1;
return 0;
}
/*
* SmartScroll / CruiseControl for some newer Logitech mice Defaults to
* enabled if we do nothing to it. Of course I put this in because I want it
* disabled :P
* 1 - enabled (if previously disabled, also default)
* 0/2 - disabled
*/
static void ps2pp_set_smartscroll(struct psmouse *psmouse)
{
unsigned char param[4];
ps2pp_cmd(psmouse, param, 0x32);
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
if (psmouse_smartscroll < 2) {
/* 0 - disabled, 1 - enabled */
param[0] = psmouse_smartscroll;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
}
}
/*
* Support 800 dpi resolution _only_ if the user wants it (there are good
* reasons to not use it even if the mouse supports it, and of course there are
* also good reasons to use it, let the user decide).
*/
void ps2pp_set_800dpi(struct psmouse *psmouse)
{
unsigned char param = 3;
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, &param, PSMOUSE_CMD_SETRES);
}
static struct ps2pp_info *get_model_info(unsigned char model)
{
static struct ps2pp_info ps2pp_list[] = {
{ 12, 0, PS2PP_SIDE_BTN},
{ 13, 0, 0 },
{ 40, 0, PS2PP_SIDE_BTN },
{ 41, 0, PS2PP_SIDE_BTN },
{ 42, 0, PS2PP_SIDE_BTN },
{ 43, 0, PS2PP_SIDE_BTN },
{ 50, 0, 0 },
{ 51, 0, 0 },
{ 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 61, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, /* MX700 */
{ 73, 0, PS2PP_SIDE_BTN },
{ 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 96, 0, 0 },
{ 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL },
{ 100, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, /* MX510 */
{ 112, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, /* MX500 */
{ 114, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN |
PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }, /* M310 */
{ }
};
int i;
for (i = 0; ps2pp_list[i].model; i++)
if (model == ps2pp_list[i].model)
return &ps2pp_list[i];
return NULL;
}
/*
* Set up input device's properties based on the detected mouse model.
*/
static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info)
{
if (model_info->features & PS2PP_SIDE_BTN)
set_bit(BTN_SIDE, psmouse->dev.keybit);
if (model_info->features & PS2PP_EXTRA_BTN)
set_bit(BTN_EXTRA, psmouse->dev.keybit);
if (model_info->features & PS2PP_TASK_BTN)
set_bit(BTN_TASK, psmouse->dev.keybit);
if (model_info->features & PS2PP_NAV_BTN) {
set_bit(BTN_FORWARD, psmouse->dev.keybit);
set_bit(BTN_BACK, psmouse->dev.keybit);
}
if (model_info->features & PS2PP_WHEEL)
set_bit(REL_WHEEL, psmouse->dev.relbit);
if (model_info->features & PS2PP_HWHEEL)
set_bit(REL_HWHEEL, psmouse->dev.relbit);
switch (model_info->kind) {
case PS2PP_KIND_WHEEL:
psmouse->name = "Wheel Mouse";
break;
case PS2PP_KIND_MX:
psmouse->name = "MX Mouse";
break;
case PS2PP_KIND_TP3:
psmouse->name = "TouchPad 3";
break;
}
}
/*
* Logitech magic init. Detect whether the mouse is a Logitech one
* and its exact model and try turning on extended protocol for ones
* that support it.
*/
int ps2pp_init(struct psmouse *psmouse, int set_properties)
{
unsigned char param[4];
unsigned char protocol = PSMOUSE_PS2;
unsigned char model, buttons;
struct ps2pp_info *model_info;
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
param[1] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
if (param[1] != 0) {
model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
buttons = param[1];
model_info = get_model_info(model);
/*
* Do Logitech PS2++ / PS2T++ magic init.
*/
if (model == 97) { /* Touch Pad 3 */
/* Unprotect RAM */
param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
psmouse_command(psmouse, param, 0x30d1);
/* Enable features */
param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
psmouse_command(psmouse, param, 0x30d1);
/* Enable PS2++ */
param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
psmouse_command(psmouse, param, 0x30d1);
param[0] = 0;
if (!psmouse_command(psmouse, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
protocol = PSMOUSE_PS2TPP;
}
} else if (model_info != NULL) {
param[0] = param[1] = param[2] = 0;
ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
ps2pp_cmd(psmouse, param, 0xDB);
if ((param[0] & 0x78) == 0x48 &&
(param[1] & 0xf3) == 0xc2 &&
(param[2] & 0x03) == ((param[1] >> 2) & 3)) {
ps2pp_set_smartscroll(psmouse);
protocol = PSMOUSE_PS2PP;
}
}
if (set_properties) {
psmouse->vendor = "Logitech";
psmouse->model = model;
if (buttons < 3)
clear_bit(BTN_MIDDLE, psmouse->dev.keybit);
if (buttons < 2)
clear_bit(BTN_RIGHT, psmouse->dev.keybit);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info);
}
}
return protocol;
}

View File

@@ -0,0 +1,18 @@
/*
* Logitech PS/2++ mouse driver header
*
* Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _LOGIPS2PP_H
#define _LOGIPS2PP_H
void ps2pp_process_packet(struct psmouse *psmouse);
void ps2pp_set_800dpi(struct psmouse *psmouse);
int ps2pp_init(struct psmouse *psmouse, int set_properties);
#endif

Binary file not shown.

View File

@@ -0,0 +1,134 @@
/*
* $Id: maplemouse.c,v 1.2 2004/03/22 01:18:15 lethal Exp $
* SEGA Dreamcast mouse driver
* Based on drivers/usb/usbmouse.c
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/maple.h>
MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>");
MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
struct dc_mouse {
struct input_dev dev;
int open;
};
static void dc_mouse_callback(struct mapleq *mq)
{
int buttons, relx, rely, relz;
struct maple_device *mapledev = mq->dev;
struct dc_mouse *mouse = mapledev->private_data;
struct input_dev *dev = &mouse->dev;
unsigned char *res = mq->recvbuf;
buttons = ~res[8];
relx=*(unsigned short *)(res+12)-512;
rely=*(unsigned short *)(res+14)-512;
relz=*(unsigned short *)(res+16)-512;
input_report_key(dev, BTN_LEFT, buttons&4);
input_report_key(dev, BTN_MIDDLE, buttons&9);
input_report_key(dev, BTN_RIGHT, buttons&2);
input_report_rel(dev, REL_X, relx);
input_report_rel(dev, REL_Y, rely);
input_report_rel(dev, REL_WHEEL, relz);
input_sync(dev);
}
static int dc_mouse_open(struct input_dev *dev)
{
struct dc_mouse *mouse = dev->private;
mouse->open++;
return 0;
}
static void dc_mouse_close(struct input_dev *dev)
{
struct dc_mouse *mouse = dev->private;
mouse->open--;
}
static int dc_mouse_connect(struct maple_device *dev)
{
unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
struct dc_mouse *mouse;
if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL)))
return -1;
memset(mouse, 0, sizeof(struct dc_mouse));
dev->private_data = mouse;
mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
init_input_dev(&mouse->dev);
mouse->dev.private = mouse;
mouse->dev.open = dc_mouse_open;
mouse->dev.close = dc_mouse_close;
mouse->dev.event = NULL;
mouse->dev.name = dev->product_name;
mouse->dev.id.bustype = BUS_MAPLE;
input_register_device(&mouse->dev);
maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE);
printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, mouse->dev.name);
return 0;
}
static void dc_mouse_disconnect(struct maple_device *dev)
{
struct dc_mouse *mouse = dev->private_data;
input_unregister_device(&mouse->dev);
kfree(mouse);
}
static struct maple_driver dc_mouse_driver = {
.function = MAPLE_FUNC_MOUSE,
.name = "Dreamcast mouse",
.connect = dc_mouse_connect,
.disconnect = dc_mouse_disconnect,
};
static int __init dc_mouse_init(void)
{
maple_register_driver(&dc_mouse_driver);
return 0;
}
static void __exit dc_mouse_exit(void)
{
maple_unregister_driver(&dc_mouse_driver);
}
module_init(dc_mouse_init);
module_exit(dc_mouse_exit);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,178 @@
/*
* $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Alan Cox Robin O'Leary
*/
/*
* IBM PC110 touchpad driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("IBM PC110 touchpad driver");
MODULE_LICENSE("GPL");
#define PC110PAD_OFF 0x30
#define PC110PAD_ON 0x38
static int pc110pad_irq = 10;
static int pc110pad_io = 0x15e0;
static struct input_dev pc110pad_dev;
static int pc110pad_data[3];
static int pc110pad_count;
static int pc110pad_used;
static char *pc110pad_name = "IBM PC110 TouchPad";
static char *pc110pad_phys = "isa15e0/input0";
static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs)
{
int value = inb_p(pc110pad_io);
int handshake = inb_p(pc110pad_io + 2);
outb_p(handshake | 1, pc110pad_io + 2);
outb_p(handshake & ~1, pc110pad_io + 2);
inb_p(0x64);
pc110pad_data[pc110pad_count++] = value;
if (pc110pad_count < 3)
return IRQ_HANDLED;
input_regs(&pc110pad_dev, regs);
input_report_key(&pc110pad_dev, BTN_TOUCH,
pc110pad_data[0] & 0x01);
input_report_abs(&pc110pad_dev, ABS_X,
pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
input_report_abs(&pc110pad_dev, ABS_Y,
pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
input_sync(&pc110pad_dev);
pc110pad_count = 0;
return IRQ_HANDLED;
}
static void pc110pad_close(struct input_dev *dev)
{
if (!--pc110pad_used)
outb(PC110PAD_OFF, pc110pad_io + 2);
}
static int pc110pad_open(struct input_dev *dev)
{
if (pc110pad_used++)
return 0;
pc110pad_interrupt(0,NULL,NULL);
pc110pad_interrupt(0,NULL,NULL);
pc110pad_interrupt(0,NULL,NULL);
outb(PC110PAD_ON, pc110pad_io + 2);
pc110pad_count = 0;
return 0;
}
/*
* We try to avoid enabling the hardware if it's not
* there, but we don't know how to test. But we do know
* that the PC110 is not a PCI system. So if we find any
* PCI devices in the machine, we don't have a PC110.
*/
static int __init pc110pad_init(void)
{
struct pci_dev *dev;
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
if (dev) {
pci_dev_put(dev);
return -ENOENT;
}
if (!request_region(pc110pad_io, 4, "pc110pad")) {
printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
pc110pad_io, pc110pad_io + 4);
return -EBUSY;
}
outb(PC110PAD_OFF, pc110pad_io + 2);
if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL))
{
release_region(pc110pad_io, 4);
printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
return -EBUSY;
}
pc110pad_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
pc110pad_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
pc110pad_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
pc110pad_dev.absmax[ABS_X] = 0x1ff;
pc110pad_dev.absmax[ABS_Y] = 0x0ff;
pc110pad_dev.open = pc110pad_open;
pc110pad_dev.close = pc110pad_close;
pc110pad_dev.name = pc110pad_name;
pc110pad_dev.phys = pc110pad_phys;
pc110pad_dev.id.bustype = BUS_ISA;
pc110pad_dev.id.vendor = 0x0003;
pc110pad_dev.id.product = 0x0001;
pc110pad_dev.id.version = 0x0100;
input_register_device(&pc110pad_dev);
printk(KERN_INFO "input: %s at %#x irq %d\n",
pc110pad_name, pc110pad_io, pc110pad_irq);
return 0;
}
static void __exit pc110pad_exit(void)
{
input_unregister_device(&pc110pad_dev);
outb(PC110PAD_OFF, pc110pad_io + 2);
free_irq(pc110pad_irq, NULL);
release_region(pc110pad_io, 4);
}
module_init(pc110pad_init);
module_exit(pc110pad_exit);

View File

@@ -0,0 +1,916 @@
/*
* PS/2 mouse driver
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#include "psmouse.h"
#include "synaptics.h"
#include "logips2pp.h"
#define DRIVER_DESC "PS/2 mouse driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static char *psmouse_proto;
static unsigned int psmouse_max_proto = -1U;
module_param_named(proto, psmouse_proto, charp, 0);
MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps). Useful for KVM switches.");
int psmouse_resolution = 200;
module_param_named(resolution, psmouse_resolution, uint, 0);
MODULE_PARM_DESC(resolution, "Resolution, in dpi.");
unsigned int psmouse_rate = 100;
module_param_named(rate, psmouse_rate, uint, 0);
MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
int psmouse_smartscroll = 1;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
static unsigned int psmouse_resetafter;
module_param_named(resetafter, psmouse_resetafter, uint, 0);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
__obsolete_setup("psmouse_noext");
__obsolete_setup("psmouse_resolution=");
__obsolete_setup("psmouse_smartscroll=");
__obsolete_setup("psmouse_resetafter=");
__obsolete_setup("psmouse_rate=");
static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2"};
/*
* psmouse_process_byte() analyzes the PS/2 data stream and reports
* relevant events to the input module once full packet has arrived.
*/
static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
{
struct input_dev *dev = &psmouse->dev;
unsigned char *packet = psmouse->packet;
if (psmouse->pktcnt < 3 + (psmouse->type >= PSMOUSE_GENPS))
return PSMOUSE_GOOD_DATA;
/*
* Full packet accumulated, process it
*/
input_regs(dev, regs);
/*
* The PS2++ protocol is a little bit complex
*/
if (psmouse->type == PSMOUSE_PS2PP || psmouse->type == PSMOUSE_PS2TPP)
ps2pp_process_packet(psmouse);
/*
* Scroll wheel on IntelliMice, scroll buttons on NetMice
*/
if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
/*
* Scroll wheel and buttons on IntelliMouse Explorer
*/
if (psmouse->type == PSMOUSE_IMEX) {
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
}
/*
* Extra buttons on Genius NewNet 3D
*/
if (psmouse->type == PSMOUSE_GENPS) {
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
}
/*
* Generic PS/2 Mouse
*/
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
input_sync(dev);
return PSMOUSE_FULL_PACKET;
}
/*
* psmouse_interrupt() handles incoming characters, either gathering them into
* packets or passing them to the command routine as command output.
*/
static irqreturn_t psmouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct psmouse *psmouse = serio->private;
psmouse_ret_t rc;
if (psmouse->state == PSMOUSE_IGNORE)
goto out;
if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) {
if (psmouse->state == PSMOUSE_ACTIVATED)
printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
flags & SERIO_TIMEOUT ? " timeout" : "",
flags & SERIO_PARITY ? " bad parity" : "");
psmouse->nak = 1;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
goto out;
}
if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) {
switch (data) {
case PSMOUSE_RET_ACK:
psmouse->nak = 0;
break;
case PSMOUSE_RET_NAK:
psmouse->nak = 1;
break;
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (test_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags)) {
psmouse->nak = 0;
break;
}
/* Fall through */
default:
goto out;
}
if (!psmouse->nak && psmouse->cmdcnt) {
set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
}
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
if (data == PSMOUSE_RET_ACK || data == PSMOUSE_RET_NAK)
goto out;
}
if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) {
if (psmouse->cmdcnt)
psmouse->cmdbuf[--psmouse->cmdcnt] = data;
if (test_and_clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags) && psmouse->cmdcnt)
wake_up_interruptible(&psmouse->wait);
if (!psmouse->cmdcnt) {
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
}
goto out;
}
if (psmouse->state == PSMOUSE_INITIALIZING)
goto out;
if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
psmouse->name, psmouse->phys, psmouse->pktcnt);
psmouse->pktcnt = 0;
}
psmouse->last = jiffies;
psmouse->packet[psmouse->pktcnt++] = data;
if (psmouse->packet[0] == PSMOUSE_RET_BAT) {
if (psmouse->pktcnt == 1)
goto out;
if (psmouse->pktcnt == 2) {
if (psmouse->packet[1] == PSMOUSE_RET_ID) {
psmouse->state = PSMOUSE_IGNORE;
serio_reconnect(serio);
goto out;
}
if (psmouse->type == PSMOUSE_SYNAPTICS) {
/* neither 0xAA nor 0x00 are valid first bytes
* for a packet in absolute mode
*/
psmouse->pktcnt = 0;
goto out;
}
}
}
rc = psmouse->protocol_handler(psmouse, regs);
switch (rc) {
case PSMOUSE_BAD_DATA:
printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
psmouse->name, psmouse->phys, psmouse->pktcnt);
psmouse->pktcnt = 0;
if (++psmouse->out_of_sync == psmouse_resetafter) {
psmouse->state = PSMOUSE_IGNORE;
printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
serio_reconnect(psmouse->serio);
}
break;
case PSMOUSE_FULL_PACKET:
psmouse->pktcnt = 0;
if (psmouse->out_of_sync) {
psmouse->out_of_sync = 0;
printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
psmouse->name, psmouse->phys);
}
break;
case PSMOUSE_GOOD_DATA:
break;
}
out:
return IRQ_HANDLED;
}
/*
* psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge.
* It doesn't handle retransmission, though it could - because when there would
* be need for retransmissions, the mouse has to be replaced anyway.
*
* psmouse_sendbyte() can only be called from a process context
*/
static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
{
psmouse->nak = 1;
set_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
if (serio_write(psmouse->serio, byte) == 0)
wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags),
msecs_to_jiffies(200));
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
return -psmouse->nak;
}
/*
* psmouse_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array.
*
* psmouse_command() can only be called from a process context
*/
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command)
{
int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int rc = -1;
int i;
timeout = msecs_to_jiffies(command == PSMOUSE_CMD_RESET_BAT ? 4000 : 500);
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
if (command == PSMOUSE_CMD_GETID)
set_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
if (receive && param)
for (i = 0; i < receive; i++)
psmouse->cmdbuf[(receive - 1) - i] = param[i];
psmouse->cmdcnt = receive;
if (command & 0xff)
if (psmouse_sendbyte(psmouse, command & 0xff))
goto out;
for (i = 0; i < send; i++)
if (psmouse_sendbyte(psmouse, param[i]))
goto out;
timeout = wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags), timeout);
if (psmouse->cmdcnt && timeout > 0) {
if (command == PSMOUSE_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
timeout = msecs_to_jiffies(100);
if (command == PSMOUSE_CMD_GETID &&
psmouse->cmdbuf[receive - 1] != 0xab && psmouse->cmdbuf[receive - 1] != 0xac) {
/*
* Device behind the port is not a keyboard
* so we don't need to wait for the 2nd byte
* of ID response.
*/
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
psmouse->cmdcnt = 0;
}
wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags), timeout);
}
if (param)
for (i = 0; i < receive; i++)
param[i] = psmouse->cmdbuf[(receive - 1) - i];
if (psmouse->cmdcnt && (command != PSMOUSE_CMD_RESET_BAT || psmouse->cmdcnt != 1))
goto out;
rc = 0;
out:
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
return rc;
}
/*
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
* or Synaptics touchpads. The command is encoded as:
* 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
* is the command.
*/
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
{
int i;
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11))
return -1;
for (i = 6; i >= 0; i -= 2) {
unsigned char d = (command >> i) & 3;
if (psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES))
return -1;
}
return 0;
}
/*
* psmouse_reset() resets the mouse into power-on state.
*/
int psmouse_reset(struct psmouse *psmouse)
{
unsigned char param[2];
if (psmouse_command(psmouse, param, PSMOUSE_CMD_RESET_BAT))
return -1;
if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
return -1;
return 0;
}
/*
* Genius NetMouse magic init.
*/
static int genius_detect(struct psmouse *psmouse)
{
unsigned char param[4];
param[0] = 3;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
return param[0] == 0x00 && param[1] == 0x33 && param[2] == 0x55;
}
/*
* IntelliMouse magic init.
*/
static int intellimouse_detect(struct psmouse *psmouse)
{
unsigned char param[2];
param[0] = 200;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
param[0] = 100;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
param[0] = 80;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
psmouse_command(psmouse, param, PSMOUSE_CMD_GETID);
return param[0] == 3;
}
/*
* Try IntelliMouse/Explorer magic init.
*/
static int im_explorer_detect(struct psmouse *psmouse)
{
unsigned char param[2];
intellimouse_detect(psmouse);
param[0] = 200;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
param[0] = 200;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
param[0] = 80;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE);
psmouse_command(psmouse, param, PSMOUSE_CMD_GETID);
return param[0] == 4;
}
/*
* psmouse_extensions() probes for any extensions to the basic PS/2 protocol
* the mouse may have.
*/
static int psmouse_extensions(struct psmouse *psmouse,
unsigned int max_proto, int set_properties)
{
int synaptics_hardware = 0;
/*
* Try Synaptics TouchPad
*/
if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse)) {
synaptics_hardware = 1;
if (set_properties) {
psmouse->vendor = "Synaptics";
psmouse->name = "TouchPad";
}
if (max_proto > PSMOUSE_IMEX) {
if (!set_properties || synaptics_init(psmouse) == 0)
return PSMOUSE_SYNAPTICS;
/*
* Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
* Unfortunately Logitech/Genius probes confuse some firmware versions so
* we'll have to skip them.
*/
max_proto = PSMOUSE_IMEX;
}
/*
* Make sure that touchpad is in relative mode, gestures (taps) are enabled
*/
synaptics_reset(psmouse);
}
if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse)) {
if (set_properties) {
set_bit(BTN_EXTRA, psmouse->dev.keybit);
set_bit(BTN_SIDE, psmouse->dev.keybit);
set_bit(REL_WHEEL, psmouse->dev.relbit);
psmouse->vendor = "Genius";
psmouse->name = "Wheel Mouse";
}
return PSMOUSE_GENPS;
}
if (max_proto > PSMOUSE_IMEX) {
int type = ps2pp_init(psmouse, set_properties);
if (type > PSMOUSE_PS2)
return type;
}
if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse)) {
if (set_properties) {
set_bit(REL_WHEEL, psmouse->dev.relbit);
set_bit(BTN_SIDE, psmouse->dev.keybit);
set_bit(BTN_EXTRA, psmouse->dev.keybit);
if (!psmouse->name)
psmouse->name = "Explorer Mouse";
}
return PSMOUSE_IMEX;
}
if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse)) {
if (set_properties) {
set_bit(REL_WHEEL, psmouse->dev.relbit);
if (!psmouse->name)
psmouse->name = "Wheel Mouse";
}
return PSMOUSE_IMPS;
}
/*
* Okay, all failed, we have a standard mouse here. The number of the buttons
* is still a question, though. We assume 3.
*/
if (synaptics_hardware) {
/*
* We detected Synaptics hardware but it did not respond to IMPS/2 probes.
* We need to reset the touchpad because if there is a track point on the
* pass through port it could get disabled while probing for protocol
* extensions.
*/
psmouse_reset(psmouse);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS);
}
return PSMOUSE_PS2;
}
/*
* psmouse_probe() probes for a PS/2 mouse.
*/
static int psmouse_probe(struct psmouse *psmouse)
{
unsigned char param[2];
/*
* First, we check if it's a mouse. It should send 0x00 or 0x03
* in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
*/
param[0] = 0xa5;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETID))
return -1;
if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04)
return -1;
/*
* Then we reset and disable the mouse so that it doesn't generate events.
*/
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS))
printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", psmouse->serio->phys);
return 0;
}
/*
* Here we set the mouse resolution.
*/
static void psmouse_set_resolution(struct psmouse *psmouse)
{
unsigned char param[1];
if (psmouse->type == PSMOUSE_PS2PP && psmouse_resolution > 400) {
ps2pp_set_800dpi(psmouse);
return;
}
if (!psmouse_resolution || psmouse_resolution >= 200)
param[0] = 3;
else if (psmouse_resolution >= 100)
param[0] = 2;
else if (psmouse_resolution >= 50)
param[0] = 1;
else if (psmouse_resolution)
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
}
/*
* Here we set the mouse report rate.
*/
static void psmouse_set_rate(struct psmouse *psmouse)
{
unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
int i = 0;
while (rates[i] > psmouse_rate) i++;
psmouse_command(psmouse, rates + i, PSMOUSE_CMD_SETRATE);
}
/*
* psmouse_initialize() initializes the mouse to a sane state.
*/
static void psmouse_initialize(struct psmouse *psmouse)
{
unsigned char param[2];
/*
* We set the mouse report rate, resolution and scaling.
*/
if (psmouse_max_proto != PSMOUSE_PS2) {
psmouse_set_rate(psmouse);
psmouse_set_resolution(psmouse);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
}
/*
* We set the mouse into streaming mode.
*/
psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM);
}
/*
* psmouse_set_state() sets new psmouse state and resets all flags and
* counters while holding serio lock so fighting with interrupt handler
* is not a concern.
*/
static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
serio_pause_rx(psmouse->serio);
psmouse->state = new_state;
psmouse->pktcnt = psmouse->cmdcnt = psmouse->out_of_sync = 0;
psmouse->flags = 0;
serio_continue_rx(psmouse->serio);
}
/*
* psmouse_activate() enables the mouse so that we get motion reports from it.
*/
static void psmouse_activate(struct psmouse *psmouse)
{
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
}
/*
* psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
* reports from it unless we explicitely request it.
*/
static void psmouse_deactivate(struct psmouse *psmouse)
{
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE))
printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", psmouse->serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
}
/*
* psmouse_cleanup() resets the mouse into power-on state.
*/
static void psmouse_cleanup(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
psmouse_reset(psmouse);
}
/*
* psmouse_disconnect() closes and frees.
*/
static void psmouse_disconnect(struct serio *serio)
{
struct psmouse *psmouse, *parent;
psmouse = serio->private;
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
if (parent->pt_deactivate)
parent->pt_deactivate(parent);
}
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
input_unregister_device(&psmouse->dev);
serio_close(serio);
kfree(psmouse);
}
/*
* psmouse_connect() is a callback from the serio module when
* an unhandled serio port is found.
*/
static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
{
struct psmouse *psmouse, *parent = NULL;
if ((serio->type & SERIO_TYPE) != SERIO_8042 &&
(serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU)
return;
/*
* If this is a pass-through port deactivate parent so the device
* connected to this port can be successfully identified
*/
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
goto out;
memset(psmouse, 0, sizeof(struct psmouse));
init_waitqueue_head(&psmouse->wait);
init_input_dev(&psmouse->dev);
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
psmouse->serio = serio;
psmouse->dev.private = psmouse;
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
serio->private = psmouse;
if (serio_open(serio, drv)) {
kfree(psmouse);
serio->private = NULL;
goto out;
}
if (psmouse_probe(psmouse) < 0) {
serio_close(serio);
kfree(psmouse);
serio->private = NULL;
goto out;
}
psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1);
if (!psmouse->vendor)
psmouse->vendor = "Generic";
if (!psmouse->name)
psmouse->name = "Mouse";
if (!psmouse->protocol_handler)
psmouse->protocol_handler = psmouse_process_byte;
sprintf(psmouse->devname, "%s %s %s",
psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name);
sprintf(psmouse->phys, "%s/input0",
serio->phys);
psmouse->dev.name = psmouse->devname;
psmouse->dev.phys = psmouse->phys;
psmouse->dev.id.bustype = BUS_I8042;
psmouse->dev.id.vendor = 0x0002;
psmouse->dev.id.product = psmouse->type;
psmouse->dev.id.version = psmouse->model;
input_register_device(&psmouse->dev);
printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
if (serio->child) {
/*
* Nothing to be done here, serio core will detect that
* the driver set serio->child and will register it for us.
*/
printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys);
}
psmouse_activate(psmouse);
out:
/* If this is a pass-through port the parent awaits to be activated */
if (parent)
psmouse_activate(parent);
}
static int psmouse_reconnect(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
struct psmouse *parent = NULL;
struct serio_driver *drv = serio->drv;
int rc = -1;
if (!drv || !psmouse) {
printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
if (psmouse->reconnect) {
if (psmouse->reconnect(psmouse))
goto out;
} else if (psmouse_probe(psmouse) < 0 ||
psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
goto out;
/* ok, the device type (and capabilities) match the old one,
* we can continue using it, complete intialization
*/
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
psmouse_activate(psmouse);
rc = 0;
out:
/* If this is a pass-through port the parent waits to be activated */
if (parent)
psmouse_activate(parent);
return rc;
}
static struct serio_driver psmouse_drv = {
.driver = {
.name = "psmouse",
},
.description = DRIVER_DESC,
.interrupt = psmouse_interrupt,
.connect = psmouse_connect,
.reconnect = psmouse_reconnect,
.disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
};
static inline void psmouse_parse_proto(void)
{
if (psmouse_proto) {
if (!strcmp(psmouse_proto, "bare"))
psmouse_max_proto = PSMOUSE_PS2;
else if (!strcmp(psmouse_proto, "imps"))
psmouse_max_proto = PSMOUSE_IMPS;
else if (!strcmp(psmouse_proto, "exps"))
psmouse_max_proto = PSMOUSE_IMEX;
else
printk(KERN_ERR "psmouse: unknown protocol type '%s'\n", psmouse_proto);
}
}
int __init psmouse_init(void)
{
psmouse_parse_proto();
serio_register_driver(&psmouse_drv);
return 0;
}
void __exit psmouse_exit(void)
{
serio_unregister_driver(&psmouse_drv);
}
module_init(psmouse_init);
module_exit(psmouse_exit);

Binary file not shown.

View File

@@ -0,0 +1,87 @@
#ifndef _PSMOUSE_H
#define _PSMOUSE_H
#define PSMOUSE_CMD_SETSCALE11 0x00e6
#define PSMOUSE_CMD_SETRES 0x10e8
#define PSMOUSE_CMD_GETINFO 0x03e9
#define PSMOUSE_CMD_SETSTREAM 0x00ea
#define PSMOUSE_CMD_POLL 0x03eb
#define PSMOUSE_CMD_GETID 0x02f2
#define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_DISABLE 0x00f5
#define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
#define PSMOUSE_RET_BAT 0xaa
#define PSMOUSE_RET_ID 0x00
#define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe
#define PSMOUSE_FLAG_ACK 0 /* Waiting for ACK/NAK */
#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */
#define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */
#define PSMOUSE_FLAG_WAITID 3 /* Command execiting is GET ID */
enum psmouse_state {
PSMOUSE_IGNORE,
PSMOUSE_INITIALIZING,
PSMOUSE_CMD_MODE,
PSMOUSE_ACTIVATED,
};
/* psmouse protocol handler return codes */
typedef enum {
PSMOUSE_BAD_DATA,
PSMOUSE_GOOD_DATA,
PSMOUSE_FULL_PACKET
} psmouse_ret_t;
struct psmouse {
void *private;
struct input_dev dev;
struct serio *serio;
char *vendor;
char *name;
unsigned char cmdbuf[8];
unsigned char packet[8];
unsigned char cmdcnt;
unsigned char pktcnt;
unsigned char type;
unsigned char model;
unsigned long last;
unsigned long out_of_sync;
enum psmouse_state state;
unsigned char nak;
char error;
char devname[64];
char phys[32];
unsigned long flags;
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
void (*pt_activate)(struct psmouse *psmouse);
void (*pt_deactivate)(struct psmouse *psmouse);
};
#define PSMOUSE_PS2 1
#define PSMOUSE_PS2PP 2
#define PSMOUSE_PS2TPP 3
#define PSMOUSE_GENPS 4
#define PSMOUSE_IMPS 5
#define PSMOUSE_IMEX 6
#define PSMOUSE_SYNAPTICS 7
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
int psmouse_reset(struct psmouse *psmouse);
extern int psmouse_smartscroll;
extern unsigned int psmouse_rate;
#endif /* _PSMOUSE_H */

Binary file not shown.

View File

@@ -0,0 +1,107 @@
/*
* Acorn RiscPC mouse driver for Linux/ARM
*
* Copyright (c) 2000-2002 Vojtech Pavlik
* Copyright (C) 1996-2002 Russell King
*
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This handles the Acorn RiscPCs mouse. We basically have a couple of
* hardware registers that track the sensor count for the X-Y movement and
* another register holding the button state. On every VSYNC interrupt we read
* the complete state and then work out if something has changed.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware/iomd.h>
MODULE_AUTHOR("Vojtech Pavlik, Russell King");
MODULE_DESCRIPTION("Acorn RiscPC mouse driver");
MODULE_LICENSE("GPL");
static short rpcmouse_lastx, rpcmouse_lasty;
static struct input_dev rpcmouse_dev = {
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
.name = "Acorn RiscPC Mouse",
.phys = "rpcmouse/input0",
.id = {
.bustype = BUS_HOST,
.vendor = 0x0005,
.product = 0x0001,
.version = 0x0100,
},
};
static irqreturn_t rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct input_dev *dev = dev_id;
short x, y, dx, dy, b;
x = (short) iomd_readl(IOMD_MOUSEX);
y = (short) iomd_readl(IOMD_MOUSEY);
b = (short) (__raw_readl(0xe0310000) ^ 0x70);
dx = x - rpcmouse_lastx;
dy = y - rpcmouse_lasty;
rpcmouse_lastx = x;
rpcmouse_lasty = y;
input_regs(dev, regs);
input_report_rel(dev, REL_X, dx);
input_report_rel(dev, REL_Y, -dy);
input_report_key(dev, BTN_LEFT, b & 0x40);
input_report_key(dev, BTN_MIDDLE, b & 0x20);
input_report_key(dev, BTN_RIGHT, b & 0x10);
input_sync(dev);
return IRQ_HANDLED;
}
static int __init rpcmouse_init(void)
{
init_input_dev(&rpcmouse_dev);
rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX);
rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY);
if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, SA_SHIRQ, "rpcmouse", &rpcmouse_dev)) {
printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
return -1;
}
input_register_device(&rpcmouse_dev);
printk(KERN_INFO "input: Acorn RiscPC mouse\n");
return 0;
}
static void __exit rpcmouse_exit(void)
{
input_unregister_device(&rpcmouse_dev);
free_irq(IRQ_VSYNCPULSE, &rpcmouse_dev);
}
module_init(rpcmouse_init);
module_exit(rpcmouse_exit);

View File

@@ -0,0 +1,316 @@
/*
* $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Serial mouse driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/config.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Serial mouse driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
"Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
"Logitech MZ++ Mouse"};
struct sermouse {
struct input_dev dev;
signed char buf[8];
unsigned char count;
unsigned char type;
unsigned long last;
char phys[32];
};
/*
* sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and
* applies some prediction to the data, resulting in 96 updates per
* second, which is as good as a PS/2 or USB mouse.
*/
static void sermouse_process_msc(struct sermouse *sermouse, signed char data, struct pt_regs *regs)
{
struct input_dev *dev = &sermouse->dev;
signed char *buf = sermouse->buf;
input_regs(dev, regs);
switch (sermouse->count) {
case 0:
if ((data & 0xf8) != 0x80) return;
input_report_key(dev, BTN_LEFT, !(data & 4));
input_report_key(dev, BTN_RIGHT, !(data & 1));
input_report_key(dev, BTN_MIDDLE, !(data & 2));
break;
case 1:
case 3:
input_report_rel(dev, REL_X, data / 2);
input_report_rel(dev, REL_Y, -buf[1]);
buf[0] = data - data / 2;
break;
case 2:
case 4:
input_report_rel(dev, REL_X, buf[0]);
input_report_rel(dev, REL_Y, buf[1] - data);
buf[1] = data / 2;
break;
}
input_sync(dev);
if (++sermouse->count == (5 - ((sermouse->type == SERIO_SUN) << 1)))
sermouse->count = 0;
}
/*
* sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and
* generates events. With prediction it gets 80 updates/sec, assuming
* standard 3-byte packets and 1200 bps.
*/
static void sermouse_process_ms(struct sermouse *sermouse, signed char data, struct pt_regs *regs)
{
struct input_dev *dev = &sermouse->dev;
signed char *buf = sermouse->buf;
if (data & 0x40) sermouse->count = 0;
input_regs(dev, regs);
switch (sermouse->count) {
case 0:
buf[1] = data;
input_report_key(dev, BTN_LEFT, (data >> 5) & 1);
input_report_key(dev, BTN_RIGHT, (data >> 4) & 1);
break;
case 1:
buf[2] = data;
data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f));
input_report_rel(dev, REL_X, data / 2);
input_report_rel(dev, REL_Y, buf[4]);
buf[3] = data - data / 2;
break;
case 2:
/* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */
if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1]))
input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key));
buf[0] = buf[1];
data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f));
input_report_rel(dev, REL_X, buf[3]);
input_report_rel(dev, REL_Y, data - buf[4]);
buf[4] = data / 2;
break;
case 3:
switch (sermouse->type) {
case SERIO_MS:
sermouse->type = SERIO_MP;
case SERIO_MP:
if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */
input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1);
input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
break;
case SERIO_MZP:
case SERIO_MZPP:
input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
case SERIO_MZ:
input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
input_report_rel(dev, REL_WHEEL, (data & 8) - (data & 7));
break;
}
break;
case 4:
case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */
buf[1] = (data >> 2) & 0x0f;
break;
case 5:
case 7: /* Ignore anything besides MZ++ */
if (sermouse->type != SERIO_MZPP) break;
switch (buf[1]) {
case 1: /* Extra mouse info */
input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (data >> 5) & 1);
input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8));
break;
default: /* We don't decode anything else yet. */
printk(KERN_WARNING
"sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]);
break;
}
break;
}
input_sync(dev);
sermouse->count++;
}
/*
* sermouse_interrupt() handles incoming characters, either gathering them into
* packets or passing them to the command routine as command output.
*/
static irqreturn_t sermouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct sermouse *sermouse = serio->private;
if (time_after(jiffies, sermouse->last + HZ/10)) sermouse->count = 0;
sermouse->last = jiffies;
if (sermouse->type > SERIO_SUN)
sermouse_process_ms(sermouse, data, regs);
else
sermouse_process_msc(sermouse, data, regs);
return IRQ_HANDLED;
}
/*
* sermouse_disconnect() cleans up after we don't want talk
* to the mouse anymore.
*/
static void sermouse_disconnect(struct serio *serio)
{
struct sermouse *sermouse = serio->private;
input_unregister_device(&sermouse->dev);
serio_close(serio);
kfree(sermouse);
}
/*
* sermouse_connect() is a callback form the serio module when
* an unhandled serio port is found.
*/
static void sermouse_connect(struct serio *serio, struct serio_driver *drv)
{
struct sermouse *sermouse;
unsigned char c;
if ((serio->type & SERIO_TYPE) != SERIO_RS232)
return;
if (!(serio->type & SERIO_PROTO) || ((serio->type & SERIO_PROTO) > SERIO_MZPP))
return;
if (!(sermouse = kmalloc(sizeof(struct sermouse), GFP_KERNEL)))
return;
memset(sermouse, 0, sizeof(struct sermouse));
init_input_dev(&sermouse->dev);
sermouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
sermouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
sermouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
sermouse->dev.private = sermouse;
serio->private = sermouse;
sermouse->type = serio->type & SERIO_PROTO;
c = (serio->type & SERIO_EXTRA) >> 16;
if (c & 0x01) set_bit(BTN_MIDDLE, sermouse->dev.keybit);
if (c & 0x02) set_bit(BTN_SIDE, sermouse->dev.keybit);
if (c & 0x04) set_bit(BTN_EXTRA, sermouse->dev.keybit);
if (c & 0x10) set_bit(REL_WHEEL, sermouse->dev.relbit);
if (c & 0x20) set_bit(REL_HWHEEL, sermouse->dev.relbit);
sprintf(sermouse->phys, "%s/input0", serio->phys);
sermouse->dev.name = sermouse_protocols[sermouse->type];
sermouse->dev.phys = sermouse->phys;
sermouse->dev.id.bustype = BUS_RS232;
sermouse->dev.id.vendor = sermouse->type;
sermouse->dev.id.product = c;
sermouse->dev.id.version = 0x0100;
if (serio_open(serio, drv)) {
kfree(sermouse);
return;
}
input_register_device(&sermouse->dev);
printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys);
}
static struct serio_driver sermouse_drv = {
.driver = {
.name = "sermouse",
},
.description = DRIVER_DESC,
.interrupt = sermouse_interrupt,
.connect = sermouse_connect,
.disconnect = sermouse_disconnect,
};
int __init sermouse_init(void)
{
serio_register_driver(&sermouse_drv);
return 0;
}
void __exit sermouse_exit(void)
{
serio_unregister_driver(&sermouse_drv);
}
module_init(sermouse_init);
module_exit(sermouse_exit);

View File

@@ -0,0 +1,615 @@
/*
* Synaptics TouchPad PS/2 mouse driver
*
* 2003 Dmitry Torokhov <dtor@mail.ru>
* Added support for pass-through port. Special thanks to Peter Berg Larsen
* for explaining various Synaptics quirks.
*
* 2003 Peter Osterlund <petero2@telia.com>
* Ported to 2.5 input device infrastructure.
*
* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
* start merging tpconfig and gpm code to a xfree-input module
* adding some changes and extensions (ex. 3rd and 4th button)
*
* Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
* Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
* code for the special synaptics commands (from the tpconfig-source)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Trademarks are the property of their respective owners.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/serio.h>
#include "psmouse.h"
#include "synaptics.h"
/*
* The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
* section 2.3.2, which says that they should be valid regardless of the
* actual size of the sensor.
*/
#define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
/*****************************************************************************
* Synaptics communications functions
****************************************************************************/
/*
* Send a command to the synpatics touchpad by special commands
*/
static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
{
if (psmouse_sliced_command(psmouse, c))
return -1;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
return -1;
return 0;
}
/*
* Set the synaptics touchpad mode byte by special commands
*/
static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
{
unsigned char param[1];
if (psmouse_sliced_command(psmouse, mode))
return -1;
param[0] = SYN_PS_SET_MODE2;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
/*
* Read the model-id bytes from the touchpad
* see also SYN_MODEL_* macros
*/
static int synaptics_model_id(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char mi[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
return -1;
priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
return 0;
}
/*
* Read the capability-bits from the touchpad
* see also the SYN_CAP_* macros
*/
static int synaptics_capability(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char cap[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
return -1;
priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
priv->ext_cap = 0;
if (!SYN_CAP_VALID(priv->capabilities))
return -1;
/*
* Unless capExtended is set the rest of the flags should be ignored
*/
if (!SYN_CAP_EXTENDED(priv->capabilities))
priv->capabilities = 0;
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
printk(KERN_ERR "Synaptics claims to have extended capabilities,"
" but I'm not able to read them.");
} else {
priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
/*
* if nExtBtn is greater than 8 it should be considered
* invalid and treated as 0
*/
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
priv->ext_cap &= 0xff0fff;
}
}
return 0;
}
/*
* Identify Touchpad
* See also the SYN_ID_* macros
*/
static int synaptics_identify(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char id[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
return -1;
priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
if (SYN_ID_IS_SYNAPTICS(priv->identity))
return 0;
return -1;
}
static void print_ident(struct synaptics_data *priv)
{
printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity));
printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity),
SYN_ID_MINOR(priv->identity));
if (SYN_MODEL_ROT180(priv->model_id))
printk(KERN_INFO " 180 degree mounted touchpad\n");
if (SYN_MODEL_PORTRAIT(priv->model_id))
printk(KERN_INFO " portrait touchpad\n");
printk(KERN_INFO " Sensor: %ld\n", SYN_MODEL_SENSOR(priv->model_id));
if (SYN_MODEL_NEWABS(priv->model_id))
printk(KERN_INFO " new absolute packet format\n");
if (SYN_MODEL_PEN(priv->model_id))
printk(KERN_INFO " pen detection\n");
if (SYN_CAP_EXTENDED(priv->capabilities)) {
printk(KERN_INFO " Touchpad has extended capability bits\n");
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n",
(int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)));
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
printk(KERN_INFO " -> middle button\n");
if (SYN_CAP_FOUR_BUTTON(priv->capabilities))
printk(KERN_INFO " -> four buttons\n");
if (SYN_CAP_MULTIFINGER(priv->capabilities))
printk(KERN_INFO " -> multifinger detection\n");
if (SYN_CAP_PALMDETECT(priv->capabilities))
printk(KERN_INFO " -> palm detection\n");
if (SYN_CAP_PASS_THROUGH(priv->capabilities))
printk(KERN_INFO " -> pass-through port\n");
}
}
static int synaptics_query_hardware(struct psmouse *psmouse)
{
int retries = 0;
while ((retries++ < 3) && psmouse_reset(psmouse))
printk(KERN_ERR "synaptics reset failed\n");
if (synaptics_identify(psmouse))
return -1;
if (synaptics_model_id(psmouse))
return -1;
if (synaptics_capability(psmouse))
return -1;
return 0;
}
static int synaptics_set_mode(struct psmouse *psmouse, int mode)
{
struct synaptics_data *priv = psmouse->private;
mode |= SYN_BIT_ABSOLUTE_MODE;
if (psmouse_rate >= 80)
mode |= SYN_BIT_HIGH_RATE;
if (SYN_ID_MAJOR(priv->identity) >= 4)
mode |= SYN_BIT_DISABLE_GESTURE;
if (SYN_CAP_EXTENDED(priv->capabilities))
mode |= SYN_BIT_W_MODE;
if (synaptics_mode_cmd(psmouse, mode))
return -1;
return 0;
}
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
static int synaptics_pt_write(struct serio *serio, unsigned char c)
{
struct psmouse *parent = serio->parent->private;
char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
if (psmouse_sliced_command(parent, c))
return -1;
if (psmouse_command(parent, &rate_param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
static inline int synaptics_is_pt_packet(unsigned char *buf)
{
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
}
static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
{
struct psmouse *child = ptport->private;
if (child && child->state == PSMOUSE_ACTIVATED) {
serio_interrupt(ptport, packet[1], 0, NULL);
serio_interrupt(ptport, packet[4], 0, NULL);
serio_interrupt(ptport, packet[5], 0, NULL);
if (child->type >= PSMOUSE_GENPS)
serio_interrupt(ptport, packet[2], 0, NULL);
} else
serio_interrupt(ptport, packet[1], 0, NULL);
}
static void synaptics_pt_activate(struct psmouse *psmouse)
{
struct psmouse *child = psmouse->serio->child->private;
/* adjust the touchpad to child's choice of protocol */
if (child && child->type >= PSMOUSE_GENPS) {
if (synaptics_set_mode(psmouse, SYN_BIT_FOUR_BYTE_CLIENT))
printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n");
}
}
static void synaptics_pt_create(struct psmouse *psmouse)
{
struct serio *serio;
serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio) {
printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
return;
}
memset(serio, 0, sizeof(struct serio));
serio->type = SERIO_PS_PSTHRU;
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
serio->write = synaptics_pt_write;
serio->parent = psmouse->serio;
psmouse->pt_activate = synaptics_pt_activate;
psmouse->serio->child = serio;
}
/*****************************************************************************
* Functions to interpret the absolute mode packets
****************************************************************************/
static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
{
memset(hw, 0, sizeof(struct synaptics_hw_state));
if (SYN_MODEL_NEWABS(priv->model_id)) {
hw->x = (((buf[3] & 0x10) << 8) |
((buf[1] & 0x0f) << 8) |
buf[4]);
hw->y = (((buf[3] & 0x20) << 7) |
((buf[1] & 0xf0) << 4) |
buf[5]);
hw->z = buf[2];
hw->w = (((buf[0] & 0x30) >> 2) |
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
((buf[0] ^ buf[3]) & 0x02)) {
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
default:
/*
* if nExtBtn is greater than 8 it should be
* considered invalid and treated as 0
*/
break;
case 8:
hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
case 6:
hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
case 4:
hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
case 2:
hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
}
}
} else {
hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
}
}
/*
* called for each full received packet from the touchpad
*/
static void synaptics_process_packet(struct psmouse *psmouse)
{
struct input_dev *dev = &psmouse->dev;
struct synaptics_data *priv = psmouse->private;
struct synaptics_hw_state hw;
int num_fingers;
int finger_width;
int i;
synaptics_parse_hw_state(psmouse->packet, priv, &hw);
if (hw.z > 0) {
num_fingers = 1;
finger_width = 5;
if (SYN_CAP_EXTENDED(priv->capabilities)) {
switch (hw.w) {
case 0 ... 1:
if (SYN_CAP_MULTIFINGER(priv->capabilities))
num_fingers = hw.w + 2;
break;
case 2:
if (SYN_MODEL_PEN(priv->model_id))
; /* Nothing, treat a pen as a single finger */
break;
case 4 ... 15:
if (SYN_CAP_PALMDETECT(priv->capabilities))
finger_width = hw.w;
break;
}
}
} else {
num_fingers = 0;
finger_width = 0;
}
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
* absolute -> relative conversion
*/
if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
if (hw.z > 0) {
input_report_abs(dev, ABS_X, hw.x);
input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
}
input_report_abs(dev, ABS_PRESSURE, hw.z);
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
input_report_key(dev, BTN_LEFT, hw.left);
input_report_key(dev, BTN_RIGHT, hw.right);
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
input_report_key(dev, BTN_MIDDLE, hw.middle);
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
input_report_key(dev, BTN_FORWARD, hw.up);
input_report_key(dev, BTN_BACK, hw.down);
}
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
input_sync(dev);
}
static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
{
static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
static unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
if (idx < 0 || idx > 4)
return 0;
switch (pkt_type) {
case SYN_NEWABS:
case SYN_NEWABS_RELAXED:
return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
case SYN_NEWABS_STRICT:
return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
case SYN_OLDABS:
return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
default:
printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
return 0;
}
}
static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
{
int i;
for (i = 0; i < 5; i++)
if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
printk(KERN_INFO "synaptics: using relaxed packet validation\n");
return SYN_NEWABS_RELAXED;
}
return SYN_NEWABS_STRICT;
}
static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
{
struct input_dev *dev = &psmouse->dev;
struct synaptics_data *priv = psmouse->private;
input_regs(dev, regs);
if (psmouse->pktcnt >= 6) { /* Full packet received */
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
if (psmouse->serio->child)
synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet);
} else
synaptics_process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
}
return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
}
/*****************************************************************************
* Driver initialization/cleanup functions
****************************************************************************/
static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
int i;
set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
set_bit(ABS_TOOL_WIDTH, dev->absbit);
set_bit(EV_KEY, dev->evbit);
set_bit(BTN_TOUCH, dev->keybit);
set_bit(BTN_TOOL_FINGER, dev->keybit);
set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
set_bit(BTN_LEFT, dev->keybit);
set_bit(BTN_RIGHT, dev->keybit);
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
set_bit(BTN_MIDDLE, dev->keybit);
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
set_bit(BTN_FORWARD, dev->keybit);
set_bit(BTN_BACK, dev->keybit);
}
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
set_bit(BTN_0 + i, dev->keybit);
clear_bit(EV_REL, dev->evbit);
clear_bit(REL_X, dev->relbit);
clear_bit(REL_Y, dev->relbit);
}
void synaptics_reset(struct psmouse *psmouse)
{
/* reset touchpad back to relative mode, gestures enabled */
synaptics_mode_cmd(psmouse, 0);
}
static void synaptics_disconnect(struct psmouse *psmouse)
{
synaptics_reset(psmouse);
kfree(psmouse->private);
}
static int synaptics_reconnect(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
struct synaptics_data old_priv = *priv;
if (!synaptics_detect(psmouse))
return -1;
if (synaptics_query_hardware(psmouse)) {
printk(KERN_ERR "Unable to query Synaptics hardware.\n");
return -1;
}
if (old_priv.identity != priv->identity ||
old_priv.model_id != priv->model_id ||
old_priv.capabilities != priv->capabilities ||
old_priv.ext_cap != priv->ext_cap)
return -1;
if (synaptics_set_mode(psmouse, 0)) {
printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
return -1;
}
return 0;
}
int synaptics_detect(struct psmouse *psmouse)
{
unsigned char param[4];
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
return param[1] == 0x47;
}
int synaptics_init(struct psmouse *psmouse)
{
struct synaptics_data *priv;
psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
if (!priv)
return -1;
memset(priv, 0, sizeof(struct synaptics_data));
if (synaptics_query_hardware(psmouse)) {
printk(KERN_ERR "Unable to query Synaptics hardware.\n");
goto init_fail;
}
if (synaptics_set_mode(psmouse, 0)) {
printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
goto init_fail;
}
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
if (SYN_CAP_PASS_THROUGH(priv->capabilities))
synaptics_pt_create(psmouse);
print_ident(priv);
set_input_params(&psmouse->dev, priv);
psmouse->protocol_handler = synaptics_process_byte;
psmouse->disconnect = synaptics_disconnect;
psmouse->reconnect = synaptics_reconnect;
return 0;
init_fail:
kfree(priv);
return -1;
}

View File

@@ -0,0 +1,109 @@
/*
* Synaptics TouchPad PS/2 mouse driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _SYNAPTICS_H
#define _SYNAPTICS_H
extern int synaptics_detect(struct psmouse *psmouse);
extern int synaptics_init(struct psmouse *psmouse);
extern void synaptics_reset(struct psmouse *psmouse);
/* synaptics queries */
#define SYN_QUE_IDENTIFY 0x00
#define SYN_QUE_MODES 0x01
#define SYN_QUE_CAPABILITIES 0x02
#define SYN_QUE_MODEL 0x03
#define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06
#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
#define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09
/* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
#define SYN_BIT_HIGH_RATE (1 << 6)
#define SYN_BIT_SLEEP_MODE (1 << 3)
#define SYN_BIT_DISABLE_GESTURE (1 << 2)
#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1)
#define SYN_BIT_W_MODE (1 << 0)
/* synaptics model ID bits */
#define SYN_MODEL_ROT180(m) ((m) & (1 << 23))
#define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22))
#define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f)
#define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f)
#define SYN_MODEL_NEWABS(m) ((m) & (1 << 7))
#define SYN_MODEL_PEN(m) ((m) & (1 << 6))
#define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5))
#define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f)
/* synaptics capability bits */
#define SYN_CAP_EXTENDED(c) ((c) & (1 << 23))
#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18))
#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7))
#define SYN_CAP_SLEEP(c) ((c) & (1 << 4))
#define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3))
#define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1))
#define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0))
#define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47)
#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
#define SYN_MODE_RATE(m) ((m) & (1 << 6))
#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3))
#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2))
#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1))
#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
/* synaptics identify query bits */
#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
#define SYN_ID_MAJOR(i) ((i) & 0x0f)
#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
/* synaptics special commands */
#define SYN_PS_SET_MODE2 0x14
#define SYN_PS_CLIENT_CMD 0x28
/* synaptics packet types */
#define SYN_NEWABS 0
#define SYN_NEWABS_STRICT 1
#define SYN_NEWABS_RELAXED 2
#define SYN_OLDABS 3
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
struct synaptics_hw_state {
int x;
int y;
int z;
int w;
unsigned int left:1;
unsigned int right:1;
unsigned int middle:1;
unsigned int up:1;
unsigned int down:1;
unsigned char ext_buttons;
};
struct synaptics_data {
/* Data read from the touchpad */
unsigned long int model_id; /* Model-ID */
unsigned long int capabilities; /* Capabilities */
unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int identity; /* Identification */
/* Data for normal processing */
int old_w; /* Previous w value */
unsigned char pkt_type; /* packet type - old, new, etc */
};
#endif /* _SYNAPTICS_H */

Binary file not shown.

View File

@@ -0,0 +1,570 @@
/*
* DEC VSXXX-AA and VSXXX-GA mouse driver.
*
* Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
*
* The packet format was taken from a patch to GPM which is (C) 2001
* by Karsten Merker <merker@linuxtag.org>
* and Maciej W. Rozycki <macro@ds2.pg.gda.pl>
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Building an adaptor to DB9 / DB25 RS232
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for
* anything if you break your mouse, your computer or whatever!
*
* In theory, this mouse is a simple RS232 device. In practice, it has got
* a quite uncommon plug and the requirement to additionally get a power
* supply at +5V and -12V.
*
* If you look at the socket/jack (_not_ at the plug), we use this pin
* numbering:
* _______
* / 7 6 5 \
* | 4 --- 3 |
* \ 2 1 /
* -------
*
* DEC socket DB9 DB25 Note
* 1 (GND) 5 7 -
* 2 (RxD) 2 3 -
* 3 (TxD) 3 2 -
* 4 (-12V) - - Somewhere from the PSU. At ATX, it's
* the thin blue wire at pin 12 of the
* ATX power connector. Only required for
* VSXXX-AA/-GA mice.
* 5 (+5V) - - PSU (red wires of ATX power connector
* on pin 4, 6, 19 or 20) or HDD power
* connector (also red wire).
* 6 (+12V) - - HDD power connector, yellow wire. Only
* required for VSXXX-AB digitizer.
* 7 (dev. avail.) - - The mouse shorts this one to pin 1.
* This way, the host computer can detect
* the mouse. To use it with the adaptor,
* simply don't connect this pin.
*
* So to get a working adaptor, you need to connect the mouse with three
* wires to a RS232 port and two or three additional wires for +5V, +12V and
* -12V to the PSU.
*
* Flow specification for the link is 4800, 8o1.
*
* The mice and tablet are described in "VCB02 Video Subsystem - Technical
* Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine
* specific for DEC documentation. Try
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/config.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Serial DEC VSXXX-AA/GA mouse / DEC tablet driver"
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
#undef VSXXXAA_DEBUG
#ifdef VSXXXAA_DEBUG
#define DBG(x...) printk (x)
#else
#define DBG(x...) do {} while (0)
#endif
#define VSXXXAA_INTRO_MASK 0x80
#define VSXXXAA_INTRO_HEAD 0x80
#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \
== VSXXXAA_INTRO_HEAD)
#define VSXXXAA_PACKET_MASK 0xe0
#define VSXXXAA_PACKET_REL 0x80
#define VSXXXAA_PACKET_ABS 0xc0
#define VSXXXAA_PACKET_POR 0xa0
#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == type)
struct vsxxxaa {
struct input_dev dev;
struct serio *serio;
#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */
unsigned char buf[BUFLEN];
unsigned char count;
unsigned char version;
unsigned char country;
unsigned char type;
char name[64];
char phys[32];
};
static void
vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num)
{
if (num >= mouse->count)
mouse->count = 0;
else {
memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num);
mouse->count -= num;
}
}
static void
vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte)
{
if (mouse->count == BUFLEN) {
printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
mouse->name, mouse->phys);
vsxxxaa_drop_bytes (mouse, 1);
}
DBG (KERN_INFO "Queueing byte 0x%02x\n", byte);
mouse->buf[mouse->count++] = byte;
}
static void
vsxxxaa_detection_done (struct vsxxxaa *mouse)
{
switch (mouse->type) {
case 0x02:
sprintf (mouse->name, "DEC VSXXX-AA/GA mouse");
break;
case 0x04:
sprintf (mouse->name, "DEC VSXXX-AB digitizer");
break;
default:
sprintf (mouse->name, "unknown DEC pointer device");
break;
}
printk (KERN_INFO "Found %s version 0x%02x from country 0x%02x "
"on port %s\n", mouse->name, mouse->version,
mouse->country, mouse->phys);
}
/*
* Returns number of bytes to be dropped, 0 if packet is okay.
*/
static int
vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len)
{
int i;
/* First byte must be a header byte */
if (!IS_HDR_BYTE (mouse->buf[0])) {
DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
return 1;
}
/* Check all following bytes */
if (packet_len > 1) {
for (i = 1; i < packet_len; i++) {
if (IS_HDR_BYTE (mouse->buf[i])) {
printk (KERN_ERR "Need to drop %d bytes "
"of a broken packet.\n",
i - 1);
DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
packet_len, i, mouse->buf[i]);
return i - 1;
}
}
}
return 0;
}
static __inline__ int
vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len)
{
return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type);
}
static void
vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
{
struct input_dev *dev = &mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right;
int dx, dy;
/*
* Check for normal stream packets. This is three bytes,
* with the first byte's 3 MSB set to 100.
*
* [0]: 1 0 0 SignX SignY Left Middle Right
* [1]: 0 dx dx dx dx dx dx dx
* [2]: 0 dy dy dy dy dy dy dy
*/
/*
* Low 7 bit of byte 1 are abs(dx), bit 7 is
* 0, bit 4 of byte 0 is direction.
*/
dx = buf[1] & 0x7f;
dx *= ((buf[0] >> 4) & 0x01)? 1: -1;
/*
* Low 7 bit of byte 2 are abs(dy), bit 7 is
* 0, bit 3 of byte 0 is direction.
*/
dy = buf[2] & 0x7f;
dy *= ((buf[0] >> 3) & 0x01)? -1: 1;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0.
*/
left = (buf[0] & 0x04)? 1: 0;
middle = (buf[0] & 0x02)? 1: 0;
right = (buf[0] & 0x01)? 1: 0;
vsxxxaa_drop_bytes (mouse, 3);
DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
mouse->name, mouse->phys, dx, dy,
left? "L": "l", middle? "M": "m", right? "R": "r");
/*
* Report what we've found so far...
*/
input_regs (dev, regs);
input_report_key (dev, BTN_LEFT, left);
input_report_key (dev, BTN_MIDDLE, middle);
input_report_key (dev, BTN_RIGHT, right);
input_report_key (dev, BTN_TOUCH, 0);
input_report_rel (dev, REL_X, dx);
input_report_rel (dev, REL_Y, dy);
input_sync (dev);
}
static void
vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
{
struct input_dev *dev = &mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right, touch;
int x, y;
/*
* Tablet position / button packet
*
* [0]: 1 1 0 B4 B3 B2 B1 Pr
* [1]: 0 0 X5 X4 X3 X2 X1 X0
* [2]: 0 0 X11 X10 X9 X8 X7 X6
* [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0
* [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6
*/
/*
* Get X/Y position. Y axis needs to be inverted since VSXXX-AB
* counts down->top while monitor counts top->bottom.
*/
x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
y = 1023 - y;
/*
* Get button state. It's bits <4..1> of byte 0.
*/
left = (buf[0] & 0x02)? 1: 0;
middle = (buf[0] & 0x04)? 1: 0;
right = (buf[0] & 0x08)? 1: 0;
touch = (buf[0] & 0x10)? 1: 0;
vsxxxaa_drop_bytes (mouse, 5);
DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
mouse->name, mouse->phys, x, y,
left? "L": "l", middle? "M": "m",
right? "R": "r", touch? "T": "t");
/*
* Report what we've found so far...
*/
input_regs (dev, regs);
input_report_key (dev, BTN_LEFT, left);
input_report_key (dev, BTN_MIDDLE, middle);
input_report_key (dev, BTN_RIGHT, right);
input_report_key (dev, BTN_TOUCH, touch);
input_report_abs (dev, ABS_X, x);
input_report_abs (dev, ABS_Y, y);
input_sync (dev);
}
static void
vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
{
struct input_dev *dev = &mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right;
unsigned char error;
/*
* Check for Power-On-Reset packets. These are sent out
* after plugging the mouse in, or when explicitely
* requested by sending 'T'.
*
* [0]: 1 0 1 0 R3 R2 R1 R0
* [1]: 0 M2 M1 M0 D3 D2 D1 D0
* [2]: 0 E6 E5 E4 E3 E2 E1 E0
* [3]: 0 0 0 0 0 Left Middle Right
*
* M: manufacturer location code
* R: revision code
* E: Error code. I'm not sure about these, but gpm's sources,
* which support this mouse, too, tell about them:
* E = [0x00 .. 0x1f]: no error, byte #3 is button state
* E = 0x3d: button error, byte #3 tells which one.
* E = <else>: other error
* D: <0010> == mouse, <0100> == tablet
*
*/
mouse->version = buf[0] & 0x0f;
mouse->country = (buf[1] >> 4) & 0x07;
mouse->type = buf[1] & 0x0f;
error = buf[2] & 0x7f;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0. Maybe even the bit <3>
* has some meaning if a tablet is attached.
*/
left = (buf[0] & 0x04)? 1: 0;
middle = (buf[0] & 0x02)? 1: 0;
right = (buf[0] & 0x01)? 1: 0;
vsxxxaa_drop_bytes (mouse, 4);
vsxxxaa_detection_done (mouse);
if (error <= 0x1f) {
/* No error. Report buttons */
input_regs (dev, regs);
input_report_key (dev, BTN_LEFT, left);
input_report_key (dev, BTN_MIDDLE, middle);
input_report_key (dev, BTN_RIGHT, right);
input_report_key (dev, BTN_TOUCH, 0);
input_sync (dev);
} else {
printk (KERN_ERR "Your %s on %s reports an undefined error, "
"please check it...\n", mouse->name,
mouse->phys);
}
/*
* If the mouse was hot-plugged, we need to force differential mode
* now... However, give it a second to recover from it's reset.
*/
printk (KERN_NOTICE "%s on %s: Forceing standard packet format and "
"streaming mode\n", mouse->name, mouse->phys);
mouse->serio->write (mouse->serio, 'S');
mdelay (50);
mouse->serio->write (mouse->serio, 'R');
}
static void
vsxxxaa_parse_buffer (struct vsxxxaa *mouse, struct pt_regs *regs)
{
unsigned char *buf = mouse->buf;
int stray_bytes;
/*
* Parse buffer to death...
*/
do {
/*
* Out of sync? Throw away what we don't understand. Each
* packet starts with a byte whose bit 7 is set. Unhandled
* packets (ie. which we don't know about or simply b0rk3d
* data...) will get shifted out of the buffer after some
* activity on the mouse.
*/
while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
printk (KERN_ERR "%s on %s: Dropping a byte to regain "
"sync with mouse data stream...\n",
mouse->name, mouse->phys);
vsxxxaa_drop_bytes (mouse, 1);
}
/*
* Check for packets we know about.
*/
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet (mouse, 3);
if (stray_bytes > 0) {
printk (KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes (mouse, stray_bytes);
continue;
}
vsxxxaa_handle_REL_packet (mouse, regs);
continue; /* More to parse? */
}
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet (mouse, 5);
if (stray_bytes > 0) {
printk (KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes (mouse, stray_bytes);
continue;
}
vsxxxaa_handle_ABS_packet (mouse, regs);
continue; /* More to parse? */
}
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet (mouse, 4);
if (stray_bytes > 0) {
printk (KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes (mouse, stray_bytes);
continue;
}
vsxxxaa_handle_POR_packet (mouse, regs);
continue; /* More to parse? */
}
break; /* No REL, ABS or POR packet found */
} while (1);
}
static irqreturn_t
vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
struct pt_regs *regs)
{
struct vsxxxaa *mouse = serio->private;
vsxxxaa_queue_byte (mouse, data);
vsxxxaa_parse_buffer (mouse, regs);
return IRQ_HANDLED;
}
static void
vsxxxaa_disconnect (struct serio *serio)
{
struct vsxxxaa *mouse = serio->private;
input_unregister_device (&mouse->dev);
serio_close (serio);
kfree (mouse);
}
static void
vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
{
struct vsxxxaa *mouse;
if ((serio->type & SERIO_TYPE) != SERIO_RS232)
return;
if ((serio->type & SERIO_PROTO) != SERIO_VSXXXAA)
return;
if (!(mouse = kmalloc (sizeof (struct vsxxxaa), GFP_KERNEL)))
return;
memset (mouse, 0, sizeof (struct vsxxxaa));
init_input_dev (&mouse->dev);
set_bit (EV_KEY, mouse->dev.evbit); /* We have buttons */
set_bit (EV_REL, mouse->dev.evbit);
set_bit (EV_ABS, mouse->dev.evbit);
set_bit (BTN_LEFT, mouse->dev.keybit); /* We have 3 buttons */
set_bit (BTN_MIDDLE, mouse->dev.keybit);
set_bit (BTN_RIGHT, mouse->dev.keybit);
set_bit (BTN_TOUCH, mouse->dev.keybit); /* ...and Tablet */
set_bit (REL_X, mouse->dev.relbit);
set_bit (REL_Y, mouse->dev.relbit);
set_bit (ABS_X, mouse->dev.absbit);
set_bit (ABS_Y, mouse->dev.absbit);
mouse->dev.absmin[ABS_X] = 0;
mouse->dev.absmax[ABS_X] = 1023;
mouse->dev.absmin[ABS_Y] = 0;
mouse->dev.absmax[ABS_Y] = 1023;
mouse->dev.private = mouse;
serio->private = mouse;
sprintf (mouse->name, "DEC VSXXX-AA/GA mouse or VSXXX-AB digitizer");
sprintf (mouse->phys, "%s/input0", serio->phys);
mouse->dev.name = mouse->name;
mouse->dev.phys = mouse->phys;
mouse->dev.id.bustype = BUS_RS232;
mouse->serio = serio;
if (serio_open (serio, drv)) {
kfree (mouse);
return;
}
/*
* Request selftest. Standard packet format and differential
* mode will be requested after the device ID'ed successfully.
*/
mouse->serio->write (mouse->serio, 'T'); /* Test */
input_register_device (&mouse->dev);
printk (KERN_INFO "input: %s on %s\n", mouse->name, mouse->phys);
}
static struct serio_driver vsxxxaa_drv = {
.driver = {
.name = "vsxxxaa",
},
.description = DRIVER_DESC,
.connect = vsxxxaa_connect,
.interrupt = vsxxxaa_interrupt,
.disconnect = vsxxxaa_disconnect,
};
int __init
vsxxxaa_init (void)
{
serio_register_driver(&vsxxxaa_drv);
return 0;
}
void __exit
vsxxxaa_exit (void)
{
serio_unregister_driver(&vsxxxaa_drv);
}
module_init (vsxxxaa_init);
module_exit (vsxxxaa_exit);

View File

@@ -0,0 +1,739 @@
/*
* Input driver to ExplorerPS/2 device driver module.
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2004 Dmitry Torokhov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#define MOUSEDEV_MINOR_BASE 32
#define MOUSEDEV_MINORS 32
#define MOUSEDEV_MIX 31
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/config.h>
#include <linux/smp_lock.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/device.h>
#include <linux/devfs_fs_kernel.h>
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
#include <linux/miscdevice.h>
#endif
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
MODULE_LICENSE("GPL");
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
#endif
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
#endif
static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
module_param(xres, uint, 0);
MODULE_PARM_DESC(xres, "Horizontal screen resolution");
static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
module_param(yres, uint, 0);
MODULE_PARM_DESC(yres, "Vertical screen resolution");
static unsigned tap_time = 200;
module_param(tap_time, uint, 0);
MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
struct mousedev_hw_data {
int dx, dy, dz;
int x, y;
int abs_event;
unsigned long buttons;
};
struct mousedev {
int exist;
int open;
int minor;
char name[16];
wait_queue_head_t wait;
struct list_head list;
struct input_handle handle;
struct mousedev_hw_data packet;
unsigned int pkt_count;
int old_x[4], old_y[4];
unsigned long touch;
};
enum mousedev_emul {
MOUSEDEV_EMUL_PS2,
MOUSEDEV_EMUL_IMPS,
MOUSEDEV_EMUL_EXPS
};
struct mousedev_motion {
int dx, dy, dz;
unsigned long buttons;
};
#define PACKET_QUEUE_LEN 16
struct mousedev_list {
struct fasync_struct *fasync;
struct mousedev *mousedev;
struct list_head node;
struct mousedev_motion packets[PACKET_QUEUE_LEN];
unsigned int head, tail;
spinlock_t packet_lock;
int pos_x, pos_y;
signed char ps2[6];
unsigned char ready, buffer, bufsiz;
unsigned char imexseq, impsseq;
enum mousedev_emul mode;
};
#define MOUSEDEV_SEQ_LEN 6
static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
static struct input_handler mousedev_handler;
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
static struct mousedev mousedev_mix;
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
static void mousedev_touchpad_event(struct mousedev *mousedev, unsigned int code, int value)
{
if (mousedev->touch) {
switch (code) {
case ABS_X:
fx(0) = value;
if (mousedev->pkt_count >= 2)
mousedev->packet.dx = ((fx(0) - fx(1)) / 2 + (fx(1) - fx(2)) / 2) / 8;
break;
case ABS_Y:
fy(0) = value;
if (mousedev->pkt_count >= 2)
mousedev->packet.dy = -((fy(0) - fy(1)) / 2 + (fy(1) - fy(2)) / 2) / 8;
break;
}
}
}
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
{
int size;
switch (code) {
case ABS_X:
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0) size = xres;
if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X];
if (value < dev->absmin[ABS_X]) value = dev->absmin[ABS_X];
mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
mousedev->packet.abs_event = 1;
break;
case ABS_Y:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
if (size == 0) size = yres;
if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y];
if (value < dev->absmin[ABS_Y]) value = dev->absmin[ABS_Y];
mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
mousedev->packet.abs_event = 1;
break;
}
}
static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
{
switch (code) {
case REL_X: mousedev->packet.dx += value; break;
case REL_Y: mousedev->packet.dy -= value; break;
case REL_WHEEL: mousedev->packet.dz -= value; break;
}
}
static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value)
{
int index;
switch (code) {
case BTN_TOUCH:
case BTN_0:
case BTN_FORWARD:
case BTN_LEFT: index = 0; break;
case BTN_STYLUS:
case BTN_1:
case BTN_RIGHT: index = 1; break;
case BTN_2:
case BTN_STYLUS2:
case BTN_MIDDLE: index = 2; break;
case BTN_3:
case BTN_BACK:
case BTN_SIDE: index = 3; break;
case BTN_4:
case BTN_EXTRA: index = 4; break;
default: return;
}
if (value) {
set_bit(index, &mousedev->packet.buttons);
set_bit(index, &mousedev_mix.packet.buttons);
} else {
clear_bit(index, &mousedev->packet.buttons);
clear_bit(index, &mousedev_mix.packet.buttons);
}
}
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
{
struct mousedev_list *list;
struct mousedev_motion *p;
unsigned long flags;
list_for_each_entry(list, &mousedev->list, node) {
spin_lock_irqsave(&list->packet_lock, flags);
p = &list->packets[list->head];
if (list->ready && p->buttons != packet->buttons) {
unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN;
if (new_head != list->tail) {
p = &list->packets[list->head = new_head];
memset(p, 0, sizeof(struct mousedev_motion));
}
}
if (packet->abs_event) {
p->dx += packet->x - list->pos_x;
p->dy += packet->y - list->pos_y;
list->pos_x = packet->x;
list->pos_y = packet->y;
}
list->pos_x += packet->dx;
list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x);
list->pos_y += packet->dy;
list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y);
p->dx += packet->dx;
p->dy += packet->dy;
p->dz += packet->dz;
p->buttons = mousedev->packet.buttons;
list->ready = 1;
spin_unlock_irqrestore(&list->packet_lock, flags);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&mousedev->wait);
}
static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
{
if (!value) {
if (mousedev->touch &&
time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
/*
* Toggle left button to emulate tap.
* We rely on the fact that mousedev_mix always has 0
* motion packet so we won't mess current position.
*/
set_bit(0, &mousedev->packet.buttons);
set_bit(0, &mousedev_mix.packet.buttons);
mousedev_notify_readers(mousedev, &mousedev_mix.packet);
mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet);
clear_bit(0, &mousedev->packet.buttons);
clear_bit(0, &mousedev_mix.packet.buttons);
}
mousedev->touch = mousedev->pkt_count = 0;
}
else
if (!mousedev->touch)
mousedev->touch = jiffies;
}
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct mousedev *mousedev = handle->private;
switch (type) {
case EV_ABS:
/* Ignore joysticks */
if (test_bit(BTN_TRIGGER, handle->dev->keybit))
return;
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
mousedev_touchpad_event(mousedev, code, value);
else
mousedev_abs_event(handle->dev, mousedev, code, value);
break;
case EV_REL:
mousedev_rel_event(mousedev, code, value);
break;
case EV_KEY:
if (value != 2) {
if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
mousedev_touchpad_touch(mousedev, value);
else
mousedev_key_event(mousedev, code, value);
}
break;
case EV_SYN:
if (code == SYN_REPORT) {
if (mousedev->touch) {
mousedev->pkt_count++;
/* Input system eats duplicate events, but we need all of them
* to do correct averaging so apply present one forward
*/
fx(0) = fx(1);
fy(0) = fy(1);
}
mousedev_notify_readers(mousedev, &mousedev->packet);
mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
mousedev->packet.abs_event = 0;
}
break;
}
}
static int mousedev_fasync(int fd, struct file *file, int on)
{
int retval;
struct mousedev_list *list = file->private_data;
retval = fasync_helper(fd, file, on, &list->fasync);
return retval < 0 ? retval : 0;
}
static void mousedev_free(struct mousedev *mousedev)
{
mousedev_table[mousedev->minor] = NULL;
kfree(mousedev);
}
static int mixdev_release(void)
{
struct input_handle *handle;
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
struct mousedev *mousedev = handle->private;
if (!mousedev->open) {
if (mousedev->exist)
input_close_device(&mousedev->handle);
else
mousedev_free(mousedev);
}
}
return 0;
}
static int mousedev_release(struct inode * inode, struct file * file)
{
struct mousedev_list *list = file->private_data;
mousedev_fasync(-1, file, 0);
list_del(&list->node);
if (!--list->mousedev->open) {
if (list->mousedev->minor == MOUSEDEV_MIX)
return mixdev_release();
if (!mousedev_mix.open) {
if (list->mousedev->exist)
input_close_device(&list->mousedev->handle);
else
mousedev_free(list->mousedev);
}
}
kfree(list);
return 0;
}
static int mousedev_open(struct inode * inode, struct file * file)
{
struct mousedev_list *list;
struct input_handle *handle;
struct mousedev *mousedev;
int i;
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if (imajor(inode) == MISC_MAJOR)
i = MOUSEDEV_MIX;
else
#endif
i = iminor(inode) - MOUSEDEV_MINOR_BASE;
if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
return -ENODEV;
if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
return -ENOMEM;
memset(list, 0, sizeof(struct mousedev_list));
spin_lock_init(&list->packet_lock);
list->pos_x = xres / 2;
list->pos_y = yres / 2;
list->mousedev = mousedev_table[i];
list_add_tail(&list->node, &mousedev_table[i]->list);
file->private_data = list;
if (!list->mousedev->open++) {
if (list->mousedev->minor == MOUSEDEV_MIX) {
list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
mousedev = handle->private;
if (!mousedev->open && mousedev->exist)
input_open_device(handle);
}
} else
if (!mousedev_mix.open && list->mousedev->exist)
input_open_device(&list->mousedev->handle);
}
return 0;
}
static inline int mousedev_limit_delta(int delta, int limit)
{
return delta > limit ? limit : (delta < -limit ? -limit : delta);
}
static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
{
struct mousedev_motion *p;
unsigned long flags;
spin_lock_irqsave(&list->packet_lock, flags);
p = &list->packets[list->tail];
ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
ps2_data[1] = mousedev_limit_delta(p->dx, 127);
ps2_data[2] = mousedev_limit_delta(p->dy, 127);
p->dx -= ps2_data[1];
p->dy -= ps2_data[2];
switch (list->mode) {
case MOUSEDEV_EMUL_EXPS:
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
p->dz -= ps2_data[3];
ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
list->bufsiz = 4;
break;
case MOUSEDEV_EMUL_IMPS:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
p->dz -= ps2_data[3];
list->bufsiz = 4;
break;
case MOUSEDEV_EMUL_PS2:
default:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
p->dz = 0;
list->bufsiz = 3;
break;
}
if (!p->dx && !p->dy && !p->dz) {
if (list->tail != list->head)
list->tail = (list->tail + 1) % PACKET_QUEUE_LEN;
if (list->tail == list->head)
list->ready = 0;
}
spin_unlock_irqrestore(&list->packet_lock, flags);
}
static ssize_t mousedev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
{
struct mousedev_list *list = file->private_data;
unsigned char c;
unsigned int i;
for (i = 0; i < count; i++) {
if (get_user(c, buffer + i))
return -EFAULT;
if (c == mousedev_imex_seq[list->imexseq]) {
if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
list->imexseq = 0;
list->mode = MOUSEDEV_EMUL_EXPS;
}
} else list->imexseq = 0;
if (c == mousedev_imps_seq[list->impsseq]) {
if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
list->impsseq = 0;
list->mode = MOUSEDEV_EMUL_IMPS;
}
} else list->impsseq = 0;
list->ps2[0] = 0xfa;
switch (c) {
case 0xeb: /* Poll */
mousedev_packet(list, &list->ps2[1]);
list->bufsiz++; /* account for leading ACK */
break;
case 0xf2: /* Get ID */
switch (list->mode) {
case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break;
case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break;
case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break;
}
list->bufsiz = 2;
break;
case 0xe9: /* Get info */
list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
list->bufsiz = 4;
break;
case 0xff: /* Reset */
list->impsseq = list->imexseq = 0;
list->mode = MOUSEDEV_EMUL_PS2;
list->ps2[1] = 0xaa; list->ps2[2] = 0x00;
list->bufsiz = 3;
break;
default:
list->bufsiz = 1;
break;
}
list->buffer = list->bufsiz;
}
kill_fasync(&list->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&list->mousedev->wait);
return count;
}
static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
{
struct mousedev_list *list = file->private_data;
int retval = 0;
if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(list->mousedev->wait,
!list->mousedev->exist || list->ready || list->buffer);
if (retval)
return retval;
if (!list->mousedev->exist)
return -ENODEV;
if (!list->buffer && list->ready) {
mousedev_packet(list, list->ps2);
list->buffer = list->bufsiz;
}
if (count > list->buffer)
count = list->buffer;
list->buffer -= count;
if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count))
return -EFAULT;
return count;
}
/* No kernel lock - fine */
static unsigned int mousedev_poll(struct file *file, poll_table *wait)
{
struct mousedev_list *list = file->private_data;
poll_wait(file, &list->mousedev->wait, wait);
if (list->ready || list->buffer)
return POLLIN | POLLRDNORM;
return 0;
}
struct file_operations mousedev_fops = {
.owner = THIS_MODULE,
.read = mousedev_read,
.write = mousedev_write,
.poll = mousedev_poll,
.open = mousedev_open,
.release = mousedev_release,
.fasync = mousedev_fasync,
};
static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
struct mousedev *mousedev;
int minor = 0;
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
if (minor == MOUSEDEV_MINORS) {
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
return NULL;
}
if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
return NULL;
memset(mousedev, 0, sizeof(struct mousedev));
INIT_LIST_HEAD(&mousedev->list);
init_waitqueue_head(&mousedev->wait);
mousedev->minor = minor;
mousedev->exist = 1;
mousedev->handle.dev = dev;
mousedev->handle.name = mousedev->name;
mousedev->handle.handler = handler;
mousedev->handle.private = mousedev;
sprintf(mousedev->name, "mouse%d", minor);
if (mousedev_mix.open)
input_open_device(&mousedev->handle);
mousedev_table[minor] = mousedev;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
S_IFCHR|S_IRUGO|S_IWUSR, "input/mouse%d", minor);
class_simple_device_add(input_class,
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
dev->dev, "mouse%d", minor);
return &mousedev->handle;
}
static void mousedev_disconnect(struct input_handle *handle)
{
struct mousedev *mousedev = handle->private;
class_simple_device_remove(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor));
devfs_remove("input/mouse%d", mousedev->minor);
mousedev->exist = 0;
if (mousedev->open) {
input_close_device(handle);
} else {
if (mousedev_mix.open)
input_close_device(handle);
mousedev_free(mousedev);
}
}
static struct input_device_id mousedev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
}, /* A mouse like device, at least one button, two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.relbit = { BIT(REL_WHEEL) },
}, /* A separate scrollwheel */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
}, /* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
}, /* A touchpad */
{ }, /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, mousedev_ids);
static struct input_handler mousedev_handler = {
.event = mousedev_event,
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
static struct miscdevice psaux_mouse = {
PSMOUSE_MINOR, "psaux", &mousedev_fops
};
static int psaux_registered;
#endif
static int __init mousedev_init(void)
{
input_register_handler(&mousedev_handler);
memset(&mousedev_mix, 0, sizeof(struct mousedev));
INIT_LIST_HEAD(&mousedev_mix.list);
init_waitqueue_head(&mousedev_mix.wait);
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
mousedev_mix.exist = 1;
mousedev_mix.minor = MOUSEDEV_MIX;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");
class_simple_device_add(input_class, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
NULL, "mice");
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if (!(psaux_registered = !misc_register(&psaux_mouse)))
printk(KERN_WARNING "mice: could not misc_register the device\n");
#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
static void __exit mousedev_exit(void)
{
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if (psaux_registered)
misc_deregister(&psaux_mouse);
#endif
devfs_remove("input/mice");
class_simple_device_remove(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX));
input_unregister_handler(&mousedev_handler);
}
module_init(mousedev_init);
module_exit(mousedev_exit);

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More