diff options
| author | James Simmons <jsimmons@heisenberg.transvirtual.com> | 2002-07-07 22:56:15 -0700 |
|---|---|---|
| committer | James Simmons <jsimmons@heisenberg.transvirtual.com> | 2002-07-07 22:56:15 -0700 |
| commit | 8ef1bf6df837a5f92e2d8d50ca26c77998602ff4 (patch) | |
| tree | 4b08ab0d040f739937b98081fae5cc7aa1b39c64 | |
| parent | af5c826ce279f0cf9f87ce7543e94b0d3b83a644 (diff) | |
| parent | a321a55fcbb2c21eb7bc8b7d4b294eefaea9065c (diff) | |
Merge http://fbdev.bkbits.net/fbdev-2.5
into heisenberg.transvirtual.com:/tmp/fbdev-2.5
172 files changed, 11779 insertions, 3054 deletions
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 0502eeee672e..f5a24f4549f9 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -15,6 +15,10 @@ C-procfs-example = procfs_example.sgml $(TOPDIR)/scripts/docgen $(TOPDIR)/scripts/gen-all-syms \ $(TOPDIR)/scripts/kernel-doc $(TOPDIR)/scripts/docproc: doc-progs ; +dochelp: + @echo ' Linux kernel internal documentation in different formats:' + @echo ' sgmldocs (SGML), psdocs (Postscript), pdfdocs (PDF), htmldocs (HTML)' + .PHONY: doc-progs doc-progs: @$(MAKE) -C $(TOPDIR)/scripts doc-progs @@ -116,6 +120,7 @@ APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \ $(TOPDIR)/drivers/usb/core/urb.c \ $(TOPDIR)/drivers/usb/core/message.c \ $(TOPDIR)/drivers/usb/core/config.c \ + $(TOPDIR)/drivers/usb/core/file.c \ $(TOPDIR)/drivers/usb/core/usb.c \ $(TOPDIR)/drivers/video/fbmem.c \ $(TOPDIR)/drivers/video/fbcmap.c \ diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 52094d07bc19..9794214be48c 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -285,6 +285,7 @@ !Edrivers/usb/core/urb.c !Edrivers/usb/core/config.c !Edrivers/usb/core/message.c +!Edrivers/usb/core/file.c !Edrivers/usb/core/usb.c </sect1> diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index c894fcceb996..9ed995b75fbf 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -108,7 +108,7 @@ delete_inode: no clear_inode: no put_super: yes yes maybe (see below) write_super: no yes maybe (see below) -statfs: yes no no +statfs: no no no remount_fs: yes yes maybe (see below) umount_begin: yes no maybe (see below) diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt index 4827bc2cda6f..6d952dc7452e 100644 --- a/Documentation/filesystems/ntfs.txt +++ b/Documentation/filesystems/ntfs.txt @@ -247,6 +247,9 @@ ChangeLog Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. +2.0.14: + - Internal changes improving run list merging code and minor locking + change to not rely on BKL in ntfs_statfs(). 2.0.13: - Internal changes towards using iget5_locked() in preparation for fake inodes and small cleanups to ntfs_volume structure. diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 2dcccf3d8a3a..b5e03c88e291 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -83,7 +83,7 @@ can relax your locking. [mandatory] ->lookup(), ->truncate(), ->create(), ->unlink(), ->mknod(), ->mkdir(), -->rmdir(), ->link(), ->lseek(), ->symlink(), ->rename(), ->permission() +->rmdir(), ->link(), ->lseek(), ->symlink(), ->rename() and ->readdir() are called without BKL now. Grab it on entry, drop upon return - that will guarantee the same locking you used to have. If your method or its parts do not need BKL - better yet, now you can shift lock_kernel() and @@ -234,6 +234,22 @@ As soon as it gets fixed is_read_only() will die. --- [mandatory] +->permission() is called without BKL now. Grab it on entry, drop upon +return - that will guarantee the same locking you used to have. If +your method or its parts do not need BKL - better yet, now you can +shift lock_kernel() and unlock_kernel() so that they would protect +exactly what needs to be protected. + +--- +[mandatory] + +->statfs() is now called without BKL held. BKL should have been +shifted into individual fs sb_op functions where it's not clear that +it's safe to remove it. If you don't need it, remove it. + +--- +[mandatory] + is_read_only() is gone; use bdev_read_only() instead. --- @@ -1,8 +1,14 @@ VERSION = 2 PATCHLEVEL = 5 -SUBLEVEL = 24 +SUBLEVEL = 25 EXTRAVERSION = +# *DOCUMENTATION* +# Too see a list of typical targets execute "make help" +# More info can be located in ./Documentation/kbuild +# Comments in this file is targeted only to the developer, do not +# expect to learn how to build the kernel reading this file. + # We are using a recursive build, so we need to do a little thinking # to get the ordering right. # @@ -140,16 +146,23 @@ export VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \ CONFIG_SHELL TOPDIR HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS PERL -export CPPFLAGS EXPORT_FLAGS NOSTDINC_FLAGS +export CPPFLAGS EXPORT_FLAGS NOSTDINC_FLAGS OBJCOPYFLAGS export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE +src := . +obj := . +srctree := $(TOPDIR) +objtree := $(TOPDIR) + +export srctree objtree + SUBDIRS := init kernel mm fs ipc lib drivers sound net noconfig_targets := xconfig menuconfig config oldconfig randconfig \ defconfig allyesconfig allnoconfig allmodconfig \ clean mrproper distclean \ - tags TAGS sgmldocs psdocs pdfdocs htmldocs \ + help tags TAGS sgmldocs psdocs pdfdocs htmldocs \ checkconfig checkhelp checkincludes ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),) @@ -217,7 +230,7 @@ NETWORKS := net/network.o include arch/$(ARCH)/Makefile -export NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS +export NETWORKS DRIVERS LIBS HEAD LDFLAGS MAKEBOOT ASFLAGS # boot target # --------------------------------------------------------------------------- @@ -238,7 +251,7 @@ boot: vmlinux vmlinux-objs := $(HEAD) $(INIT) $(CORE_FILES) $(LIBS) $(DRIVERS) $(NETWORKS) quiet_cmd_link_vmlinux = LD $@ -cmd_link_vmlinux = $(LD) $(LINKFLAGS) $(HEAD) $(INIT) \ +cmd_link_vmlinux = $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $(HEAD) $(INIT) \ --start-group \ $(CORE_FILES) \ $(LIBS) \ @@ -255,7 +268,7 @@ define rule_link_vmlinux . scripts/mkversion > .tmpversion mv -f .tmpversion .version +$(MAKE) -C init - $(call cmd,cmd_link_vmlinux) + $(call cmd,link_vmlinux) $(cmd_link_vmlinux) echo 'cmd_$@ := $(cmd_link_vmlinux)' > $(@D)/.$(@F).cmd $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map @@ -679,6 +692,43 @@ tags: FORCE find $(SUBDIRS) init -name SCCS -prune -o -name BitKeeper -prune -o \ -name '*.[ch]' -print | xargs ctags $$CTAGSF -a +# Brief documentation of the typical targets used +# --------------------------------------------------------------------------- + +help: + @echo 'Cleaning targets:' + @echo ' clean - remove most generated files but keep the config' + @echo ' mrproper - remove all generated files including the config' + @echo ' distclean - mrproper + remove files generated by editors and patch' + @echo '' + @echo 'Configuration targets:' + @echo ' oldconfig - Update current config utilising a line-oriented program' + @echo ' menuconfig - Update current config utilising a menu based program' + @echo ' xconfig - Update current config utilising a X-based program' + @echo ' defconfig - New config with default answer to all options' + @echo ' allmodconfig - New config selecting modules when possible' + @echo ' allyesconfig - New config where all options are accepted with yes' + @echo ' allnoconfig - New minimal config' + @echo '' + @echo 'Other generic targets:' + @echo ' all - Build all targets marked with [*]' + @echo ' dep - Create module version information' + @echo '* vmlinux - Build the bare kernel' + @echo '* modules - Build all modules' + @echo ' dir/file.[ois]- Build specified target only' + @echo ' rpm - Build a kernel as an RPM package' + @echo ' tags/TAGS - Generate tags file for editors' + @echo '' + @echo 'Documentation targets:' + @$(MAKE) --no-print-directory -f Documentation/DocBook/Makefile dochelp + @echo '' + @echo 'Architecture specific targets ($(ARCH)):' + @$(MAKE) --no-print-directory -f arch/$(ARCH)/boot/Makefile archhelp + @echo '' + @echo 'Execute "make" or "make all" to build all targets marked with [*] ' + @echo 'For further info browse Documentation/kbuild/*' + + # Documentation targets # --------------------------------------------------------------------------- @@ -735,9 +785,9 @@ if_changed_rule = $(if $(strip $? \ $(filter-out $(cmd_$(@F)),$(cmd_$(1)))),\ @$(rule_$(1))) -# If quiet is set, only print short version of rule +# If quiet is set, only print short version of command -cmd = @$(if $($(quiet)$(1)),echo ' $($(quiet)$(1))' &&) $($(1)) +cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1)) define update-if-changed if [ -r $@ ] && cmp -s $@ $@.tmp; then \ diff --git a/Rules.make b/Rules.make index 961d801047ed..8df36f4b5cf6 100644 --- a/Rules.make +++ b/Rules.make @@ -34,7 +34,7 @@ endif # $(srctree)/include/linux/module.h : Some file relative to the source # dir root # -# Those can only be used in the section after +# $(obj) and $(src) can only be used in the section after # include $(TOPDIR)/Rules.make, i.e for generated files and the like. # Intentionally. # @@ -43,8 +43,10 @@ endif obj := . src := . -objtree := $(TOPDIR) -srctree := $(TOPDIR) + +# For use in the quiet output + +echo_target = $(RELDIR)/$@ # Figure out what we need to build from the various variables # =========================================================================== @@ -269,25 +271,25 @@ c_flags = -Wp,-MD,$(depfile) $(CFLAGS) $(NOSTDINC_FLAGS) \ -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) \ $(export_flags) -quiet_cmd_cc_s_c = CC $(RELDIR)/$@ +quiet_cmd_cc_s_c = CC $(echo_target) cmd_cc_s_c = $(CC) $(c_flags) -S -o $@ $< %.s: %.c FORCE $(call if_changed_dep,cc_s_c) -quiet_cmd_cc_i_c = CPP $(RELDIR)/$@ +quiet_cmd_cc_i_c = CPP $(echo_target) cmd_cc_i_c = $(CPP) $(c_flags) -o $@ $< %.i: %.c FORCE $(call if_changed_dep,cc_i_c) -quiet_cmd_cc_o_c = CC $(RELDIR)/$@ +quiet_cmd_cc_o_c = CC $(echo_target) cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< %.o: %.c FORCE $(call if_changed_dep,cc_o_c) -quiet_cmd_cc_lst_c = ' Generating $(RELDIR)/$@' +quiet_cmd_cc_lst_c = ' Generating $(echo_target)' cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && $(TOPDIR)/scripts/makelst $*.o $(TOPDIR)/System.map $(OBJDUMP) > $@ %.lst: %.c FORCE @@ -304,13 +306,13 @@ $(real-objs-m:.o=.s): modkern_aflags := $(AFLAGS_MODULE) a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(NOSTDINC_FLAGS) \ $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) -quiet_cmd_as_s_S = CPP $(RELDIR)/$@ +quiet_cmd_as_s_S = CPP $(echo_target) cmd_as_s_S = $(CPP) $(a_flags) -o $@ $< %.s: %.S FORCE $(call if_changed_dep,as_s_S) -quiet_cmd_as_o_S = AS $(RELDIR)/$@ +quiet_cmd_as_o_S = AS $(echo_target) cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< %.o: %.S FORCE @@ -328,10 +330,10 @@ $(sort $(subdir-obj-y)): sub_dirs ; # Rule to compile a set of .o files into one .o file # ifdef O_TARGET -quiet_cmd_link_o_target = LD $(RELDIR)/$@ +quiet_cmd_link_o_target = LD $(echo_target) # If the list of objects to link is empty, just create an empty O_TARGET cmd_link_o_target = $(if $(strip $(obj-y)),\ - $(LD) $(EXTRA_LDFLAGS) -r -o $@ $(filter $(obj-y), $^),\ + $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o $@ $(filter $(obj-y), $^),\ rm -f $@; $(AR) rcs $@) $(O_TARGET): $(obj-y) FORCE @@ -344,7 +346,7 @@ endif # O_TARGET # Rule to compile a set of .o files into one .a file # ifdef L_TARGET -quiet_cmd_link_l_target = AR $(RELDIR)/$@ +quiet_cmd_link_l_target = AR $(echo_target) cmd_link_l_target = rm -f $@; $(AR) $(EXTRA_ARFLAGS) rcs $@ $(obj-y) $(L_TARGET): $(obj-y) FORCE @@ -357,8 +359,8 @@ endif # Rule to link composite objects # -quiet_cmd_link_multi = LD $(RELDIR)/$@ -cmd_link_multi = $(LD) $(EXTRA_LDFLAGS) -r -o $@ $(filter $($(basename $@)-objs),$^) +quiet_cmd_link_multi = LD $(echo_target) +cmd_link_multi = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o $@ $(filter $($(basename $@)-objs),$^) # We would rather have a list of rules like # foo.o: $(foo-objs) @@ -379,22 +381,22 @@ host-progs-single := $(foreach m,$(host-progs),$(if $($(m)-objs),,$(m))) host-progs-multi := $(foreach m,$(host-progs),$(if $($(m)-objs),$(m))) host-progs-multi-objs := $(foreach m,$(host-progs-multi),$($(m)-objs)) -quiet_cmd_host_cc__c = HOSTCC $(RELDIR)/$@ -cmd_host_cc__c = $(HOSTCC) -Wp,-MD,.$(subst /,_,$@).d \ +quiet_cmd_host_cc__c = HOSTCC $(echo_target) +cmd_host_cc__c = $(HOSTCC) -Wp,-MD,$(depfile) \ $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) \ $(HOST_LOADLIBES) -o $@ $< $(host-progs-single): %: %.c FORCE $(call if_changed_dep,host_cc__c) -quiet_cmd_host_cc_o_c = HOSTCC $(RELDIR)/$@ -cmd_host_cc_o_c = $(HOSTCC) -Wp,-MD,.$(subst /,_,$@).d \ +quiet_cmd_host_cc_o_c = HOSTCC $(echo_target) +cmd_host_cc_o_c = $(HOSTCC) -Wp,-MD,$(depfile) \ $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) -c -o $@ $< $(host-progs-multi-objs): %.o: %.c FORCE $(call if_changed_dep,host_cc_o_c) -quiet_cmd_host_cc__o = HOSTLD $(RELDIR)/$@ +quiet_cmd_host_cc__o = HOSTLD $(echo_target) cmd_host_cc__o = $(HOSTCC) $(HOSTLDFLAGS) -o $@ $($@-objs) \ $(HOST_LOADLIBES) @@ -410,9 +412,39 @@ endif # ! fastdep # =========================================================================== %:: %_shipped - @echo ' CP $(RELDIR)/$@' + @echo ' CP $(echo_target)' @cp $< $@ +# Commands useful for building a boot image +# =========================================================================== +# +# Use as following: +# +# target: source(s) FORCE +# $(if_changed,ld/objcopy) +# +# and add target to EXTRA_TARGETS so that we know we have to +# read in the saved command line + +# Linking +# --------------------------------------------------------------------------- + +quiet_cmd_ld = LD $(echo_target) +cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$@) \ + $(filter-out FORCE,$^) -o $@ + +# Objcopy +# --------------------------------------------------------------------------- + +quiet_cmd_objcopy = OBJCPY $(echo_target) +cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ + +# Gzip +# --------------------------------------------------------------------------- + +quiet_cmd_gzip = GZIP $(echo_target) +cmd_gzip = gzip -f -9 < $< > $@ + # =========================================================================== # Generic stuff # =========================================================================== @@ -520,4 +552,4 @@ if_changed_rule = $(if $(strip $? \ # If quiet is set, only print short version of command -cmd = @$(if $($(quiet)$(1)),echo ' $($(quiet)$(1))' &&) $($(1)) +cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1)) diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile index 14aa0e55eb90..78dbb2696968 100644 --- a/arch/alpha/Makefile +++ b/arch/alpha/Makefile @@ -10,7 +10,7 @@ NM := $(NM) -B -LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N #-relax +LDFLAGS_vmlinux = -static -T arch/alpha/vmlinux.lds -N #-relax CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8 # Determine if we can use the BWX instructions with GAS. diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c511b3700519..9d147fe7a97b 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -7,7 +7,8 @@ # # Copyright (C) 1995-2001 by Russell King -LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds +LDFLAGS_vmlinux :=-p -X -T arch/arm/vmlinux.lds +OBJCOPYFLAGS :=-O binary -R .note -R .comment -S GZFLAGS :=-9 CFLAGS +=-pipe diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 4f452f835831..6d3e078a87fd 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -115,15 +115,15 @@ endif export SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS PARAMS_PHYS Image: $(SYSTEM) - $(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) $@ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ bzImage: zImage zImage: compressed/vmlinux - $(OBJCOPY) -O binary -R .note -R .comment -S compressed/vmlinux $@ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ bootpImage: bootp/bootp - $(OBJCOPY) -O binary -R .note -R .comment -S bootp/bootp $@ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ compressed/vmlinux: $(TOPDIR)/vmlinux @$(MAKE) -C compressed vmlinux diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 2f5c0eee43ea..67278fa673f1 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -78,7 +78,7 @@ $(HEAD): $(HEAD:.o=.S) $(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S) piggy.o: $(SYSTEM) - $(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) piggy + $(OBJCOPY) $(OBJCOPYFLAGS) $(SYSTEM) piggy gzip $(GZFLAGS) < piggy > piggy.gz $(LD) -r -o $@ -b binary piggy.gz rm -f piggy piggy.gz diff --git a/arch/cris/Makefile b/arch/cris/Makefile index 8cd602bf66dc..1b5c9af76fdd 100644 --- a/arch/cris/Makefile +++ b/arch/cris/Makefile @@ -31,11 +31,11 @@ LD = if [ ! -e $(LD_SCRIPT).tmp -o $(LD_SCRIPT) -nt $(LD_SCRIPT).tmp ]; then \ else true; \ fi && $(CROSS_COMPILE)ld -mcrislinux -LINKFLAGS = -T $(LD_SCRIPT).tmp +LDFLAGS_vmlinux = -T $(LD_SCRIPT).tmp # objcopy is used to make binary images from the resulting linked file -OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +OBJCOPYFLAGS := -O binary -R .note -R .comment -S # -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others @@ -64,7 +64,7 @@ LIBS := $(TOPDIR)/arch/cris/lib/lib.a $(LIBS) $(TOPDIR)/arch/cris/lib/lib.a $(LI MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot vmlinux.bin: vmlinux - $(OBJCOPY) vmlinux vmlinux.bin + $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin timage: vmlinux.bin cat vmlinux.bin cramfs.img >timage diff --git a/arch/cris/boot/compressed/Makefile b/arch/cris/boot/compressed/Makefile index ea58c7ad6a3f..5f71c2c819e6 100644 --- a/arch/cris/boot/compressed/Makefile +++ b/arch/cris/boot/compressed/Makefile @@ -8,7 +8,7 @@ CC = gcc-cris -melf -I $(TOPDIR)/include CFLAGS = -O2 LD = ld-cris OBJCOPY = objcopy-cris - +OBJCOPYFLAGS = -O binary --remove-section=.bss OBJECTS = head.o misc.o # files to compress @@ -18,7 +18,7 @@ all: vmlinuz decompress.bin: $(OBJECTS) $(LD) -T decompress.ld -o decompress.o $(OBJECTS) - $(OBJCOPY) -O binary --remove-section=.bss decompress.o decompress.bin + $(OBJCOPY) $(OBJCOPYFLAGS) decompress.o decompress.bin # save it for mkprod in the topdir. cp decompress.bin $(TOPDIR) diff --git a/arch/cris/boot/rescue/Makefile b/arch/cris/boot/rescue/Makefile index 43b1fa6b8e96..e9f2ba2ad02c 100644 --- a/arch/cris/boot/rescue/Makefile +++ b/arch/cris/boot/rescue/Makefile @@ -8,6 +8,7 @@ CC = gcc-cris -mlinux -I $(TOPDIR)/include CFLAGS = -O2 LD = gcc-cris -mlinux -nostdlib OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss all: rescue.bin testrescue.bin kimagerescue.bin @@ -16,11 +17,11 @@ rescue: rescue.bin rescue.bin: head.o $(LD) -T rescue.ld -o rescue.o head.o - $(OBJCOPY) -O binary --remove-section=.bss rescue.o rescue.bin + $(OBJCOPY) $(OBJCOPYFLAGS) rescue.o rescue.bin cp rescue.bin $(TOPDIR) testrescue.bin: testrescue.o - $(OBJCOPY) -O binary --remove-section=.bss testrescue.o tr.bin + $(OBJCOPY) $(OBJCOPYFLAGS) testrescue.o tr.bin # Pad it to 784 bytes dd if=/dev/zero of=tmp2423 bs=1 count=784 cat tr.bin tmp2423 >testrescue_tmp.bin @@ -28,7 +29,7 @@ testrescue.bin: testrescue.o rm tr.bin tmp2423 testrescue_tmp.bin kimagerescue.bin: kimagerescue.o - $(OBJCOPY) -O binary --remove-section=.bss kimagerescue.o ktr.bin + $(OBJCOPY) $(OBJCOPYFLAGS) kimagerescue.o ktr.bin # Pad it to 784 bytes, that's what the rescue loader expects dd if=/dev/zero of=tmp2423 bs=1 count=784 cat ktr.bin tmp2423 >kimagerescue_tmp.bin diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 7009d720d262..164f2ebc762b 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -16,10 +16,9 @@ # Added '-march' and '-mpreferred-stack-boundary' support # -LD=$(CROSS_COMPILE)ld -m elf_i386 -OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -LDFLAGS=-e stext -LINKFLAGS =-T $(TOPDIR)/arch/i386/vmlinux.lds $(LDFLAGS) +LDFLAGS := -m elf_i386 +OBJCOPYFLAGS := -O binary -R .note -R .comment -S +LDFLAGS_vmlinux := -T arch/i386/vmlinux.lds -e stext CFLAGS += -pipe @@ -103,7 +102,7 @@ SUBDIRS += arch/i386/pci DRIVERS += arch/i386/pci/pci.o endif -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +MAKEBOOT = +$(MAKE) -C arch/$(ARCH)/boot vmlinux: arch/i386/vmlinux.lds diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index 82a21d608f4f..4a3d7195b05e 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -25,25 +25,44 @@ SVGA_MODE := -DSVGA_MODE=NORMAL_VGA #RAMDISK := -DRAMDISK=512 +EXTRA_TARGETS := vmlinux.bin bvmlinux.bin bootsect bootsect.o \ + setup setup.o zImage bzImage + +host-progs := tools/build + +# Default + +boot: bzImage + +include $(TOPDIR)/Rules.make + # --------------------------------------------------------------------------- -BOOT_INCL = $(TOPDIR)/include/linux/config.h \ - $(TOPDIR)/include/linux/autoconf.h \ - $(TOPDIR)/include/asm/boot.h +zImage: IMAGE_OFFSET := 0x1000 +zImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK) +bzImage: IMAGE_OFFSET := 0x100000 +bzImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__ +bzImage: BUILDFLAGS := -b + +quiet_cmd_image = BUILD $(RELDIR)/$@ +cmd_image = tools/build $(BUILDFLAGS) bootsect setup vmlinux.bin \ + $(ROOT_DEV) > $@ -zImage: bootsect setup compressed/vmlinux tools/build - $(OBJCOPY) compressed/vmlinux compressed/vmlinux.out - tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage +zImage bzImage: bootsect setup vmlinux.bin tools/build FORCE + $(call if_changed,image) -bzImage: bbootsect bsetup compressed/bvmlinux tools/build - $(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out - tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage +vmlinux.bin: compressed/vmlinux FORCE + $(call if_changed,objcopy) -compressed/vmlinux: $(TOPDIR)/vmlinux - @$(MAKE) -C compressed vmlinux +LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary +LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext + +setup bootsect: %: %.o FORCE + $(call if_changed,ld) + +compressed/vmlinux: FORCE + @$(MAKE) IMAGE_OFFSET=$(IMAGE_OFFSET) -C compressed vmlinux -compressed/bvmlinux: $(TOPDIR)/vmlinux - @$(MAKE) -C compressed bvmlinux zdisk: $(BOOTIMAGE) dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0 @@ -58,48 +77,17 @@ zlilo: $(BOOTIMAGE) install: $(BOOTIMAGE) sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)" -tools/build: tools/build.c - $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include - -bootsect: bootsect.o - $(LD) -Ttext 0x0 -s --oformat binary -o $@ $< - -bootsect.o: bootsect.s - $(AS) -o $@ $< - -bootsect.s: bootsect.S Makefile $(BOOT_INCL) - $(CPP) $(CPPFLAGS) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -bbootsect: bbootsect.o - $(LD) -Ttext 0x0 -s --oformat binary $< -o $@ - -bbootsect.o: bbootsect.s - $(AS) -o $@ $< - -bbootsect.s: bootsect.S Makefile $(BOOT_INCL) - $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -setup: setup.o - $(LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< - -setup.o: setup.s - $(AS) -o $@ $< - -setup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h - $(CPP) $(CPPFLAGS) -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - -bsetup: bsetup.o - $(LD) -Ttext 0x0 -s --oformat binary -e begtext -o $@ $< - -bsetup.o: bsetup.s - $(AS) -o $@ $< - -bsetup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h - $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ - clean: @echo 'Cleaning up (boot)' - @rm -f tools/build + @rm -f tools/build vmlinux.bin bvmlinux.bin @rm -f setup bootsect zImage compressed/vmlinux.out @rm -f bsetup bbootsect bzImage compressed/bvmlinux.out @$(MAKE) -C compressed clean + +archhelp: + @echo '* bzImage - Compressed kernel image (arch/$(ARCH)/boot/bzImage)' + @echo ' install - Install kernel using' + @echo ' (your) ~/bin/installkernel or' + @echo ' (distribution) /sbin/installkernel or' + @echo ' install to $$(INSTALL_PATH) and run lilo' + diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index 70446cc3e5c7..df0f5f75e2e4 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -4,47 +4,26 @@ # create a compressed vmlinux image from the original vmlinux # -HEAD = head.o -SYSTEM = $(TOPDIR)/vmlinux +EXTRA_TARGETS := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o +EXTRA_AFLAGS := -traditional -OBJECTS = $(HEAD) misc.o +include $(TOPDIR)/Rules.make -ZLDFLAGS = -e startup_32 +LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32 -# -# ZIMAGE_OFFSET is the load offset of the compression loader -# BZIMAGE_OFFSET is the load offset of the high loaded compression loader -# -ZIMAGE_OFFSET = 0x1000 -BZIMAGE_OFFSET = 0x100000 - -ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS) -BZLINKFLAGS = -Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS) - -all: vmlinux - -vmlinux: piggy.o $(OBJECTS) - $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o - -bvmlinux: piggy.o $(OBJECTS) - $(LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) piggy.o +vmlinux: head.o misc.o piggy.o FORCE + $(call if_changed,ld) -head.o: head.S - $(CC) $(AFLAGS) -traditional -c head.S +vmlinux.bin: $(TOPDIR)/vmlinux FORCE + $(call if_changed,objcopy) -comma := , +vmlinux.bin.gz: vmlinux.bin FORCE + $(call if_changed,gzip) -misc.o: misc.c - $(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c misc.c +LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T -piggy.o: $(SYSTEM) - tmppiggy=_tmp_$$$$piggy; \ - rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ - $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ - gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ - echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ - $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ - rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk +piggy.o: vmlinux.scr vmlinux.bin.gz FORCE + $(call if_changed,ld) clean: - @rm -f vmlinux bvmlinux _tmp_* + @rm -f vmlinux vmlinux.bin vmlinux.bin.gz diff --git a/arch/i386/boot/compressed/vmlinux.scr b/arch/i386/boot/compressed/vmlinux.scr new file mode 100644 index 000000000000..1ed9d791f863 --- /dev/null +++ b/arch/i386/boot/compressed/vmlinux.scr @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index d8501b04a6c4..99f42387cb9c 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -13,7 +13,8 @@ AWK := awk export AWK -LINKFLAGS = -static -T arch/$(ARCH)/vmlinux.lds +OBJCOPYFLAGS := --strip-all +LDFLAGS_vmlinux := -static -T arch/$(ARCH)/vmlinux.lds AFLAGS_KERNEL := -mconstant-gp EXTRA = @@ -100,7 +101,7 @@ arch/$(ARCH)/vmlinux.lds: arch/$(ARCH)/vmlinux.lds.S FORCE -traditional arch/$(ARCH)/vmlinux.lds.S > $@ compressed: vmlinux - $(OBJCOPY) --strip-all vmlinux vmlinux-tmp + $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux-tmp gzip vmlinux-tmp mv vmlinux-tmp.gz vmlinux.gz diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index ccf90876c934..c2e695afc00a 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -18,16 +18,16 @@ COMPILE_ARCH = $(shell uname -m) # override top level makefile AS += -m68020 -LD += -m m68kelf +LDFLAGS := -m m68kelf ifneq ($(COMPILE_ARCH),$(ARCH)) # prefix for cross-compiling binaries CROSS_COMPILE = m68k-linux- endif ifndef CONFIG_SUN3 -LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux.lds +LDFLAGS_vmlinux = -T $(TOPDIR)/arch/m68k/vmlinux.lds else -LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux-sun3.lds -N +LDFLAGS_vmlinux = -T $(TOPDIR)/arch/m68k/vmlinux-sun3.lds -N endif # without -fno-strength-reduce the 53c7xx.c driver fails ;-( diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 7db284e77931..de853833c0d6 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -36,9 +36,9 @@ endif # crossformat linking we rely on the elf2ecoff tool for format conversion. # GCCFLAGS := -G 0 -mno-abicalls -fno-pic -LINKFLAGS += -static +LDFLAGS_vmlinux += -static MODFLAGS += -mlong-calls -LD := $(LD) -G 0 +LDFLAGS := -G 0 ifdef CONFIG_REMOTE_DEBUG CFLAGS := $(CFLAGS) -g @@ -270,7 +270,7 @@ vmlinux: arch/$(ARCH)/ld.script arch/$(ARCH)/ld.script: arch/$(ARCH)/ld.script.in arch/$(ARCH)/Makefile sed -e 's/@@LOADADDR@@/$(LOADADDR)/' <$< >$@ -LINKFLAGS += -T arch/$(ARCH)/ld.script +LDFLAGS_vmlinux += -T arch/$(ARCH)/ld.script HEAD := arch/mips/kernel/head.o arch/mips/kernel/init_task.o diff --git a/arch/mips/philips/nino/ramdisk/Makefile b/arch/mips/philips/nino/ramdisk/Makefile index 1e7658863b64..caaecadfad48 100644 --- a/arch/mips/philips/nino/ramdisk/Makefile +++ b/arch/mips/philips/nino/ramdisk/Makefile @@ -7,6 +7,6 @@ # ramdisk.o: ramdisk.gz ld.script - $(LD) -T ld.script -b binary -o $@ ramdisk.gz + $(LD) $(LDFLAGS) -T ld.script -b binary -o $@ ramdisk.gz include $(TOPDIR)/Rules.make diff --git a/arch/mips64/Makefile b/arch/mips64/Makefile index 57b5c2aa3862..0951e650926f 100644 --- a/arch/mips64/Makefile +++ b/arch/mips64/Makefile @@ -35,7 +35,7 @@ endif # CFLAGS += -I $(TOPDIR)/include/asm/gcc $(CFLAGS) CFLAGS += -mabi=64 -G 0 -mno-abicalls -fno-pic -Wa,--trap -pipe -LINKFLAGS += -G 0 -static # -N +LDFLAGS_vmlinux += -G 0 -static # -N MODFLAGS += -mlong-calls ifdef CONFIG_REMOTE_DEBUG @@ -118,7 +118,7 @@ endif # ifdef CONFIG_BOOT_ELF32 CFLAGS += -Wa,-32 -LINKFLAGS += -T arch/mips64/ld.script.elf32 +LDFLAGS_vmlinux += -T arch/mips64/ld.script.elf32 endif # # The 64-bit ELF tools are pretty broken so at this time we generate 64-bit @@ -126,13 +126,13 @@ endif # ifdef CONFIG_BOOT_ELF64 CFLAGS += -Wa,-32 -LINKFLAGS += -T arch/mips64/ld.script.elf32 +LDFLAGS_vmlinux += -T arch/mips64/ld.script.elf32 #AS += -64 -#LD += -m elf64bmip -#LINKFLAGS += -T arch/mips64/ld.script.elf64 +#LDFLAGS += -m elf64bmip +#LDFLAGS_vmlinux += -T arch/mips64/ld.script.elf64 endif -LINKFLAGS += -Ttext $(LOADADDR) +LDFLAGS_vmlinux += -Ttext $(LOADADDR) HEAD := arch/mips64/kernel/head.o arch/mips64/kernel/init_task.o diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile index 38bdaa29f31d..4beccadb8202 100644 --- a/arch/parisc/Makefile +++ b/arch/parisc/Makefile @@ -20,9 +20,8 @@ FINAL_LD=$(CROSS_COMPILE)ld --warn-common --warn-section-align CPP=$(CC) -E -OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -LDFLAGS = -LINKFLAGS =-T $(TOPDIR)/arch/parisc/vmlinux.lds $(LDFLAGS) +OBJCOPY_FLAGS =-O binary -R .note -R .comment -S +LDFLAGS_vmlinux =-T arch/parisc/vmlinux.lds CFLAGS_PIPE := -pipe CFLAGS_NSR := -fno-strength-reduce diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index 458476b851f1..9eed867abf8c 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -21,7 +21,7 @@ else KERNELLOAD =0xc0000000 endif -LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic +LDFLAGS_vmlinux = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic CPPFLAGS := $(CPPFLAGS) -I$(TOPDIR)/arch/$(ARCH) AFLAGS := $(AFLAGS) -I$(TOPDIR)/arch/$(ARCH) CFLAGS := $(CFLAGS) -I$(TOPDIR)/arch/$(ARCH) -fsigned-char \ diff --git a/arch/ppc64/Makefile b/arch/ppc64/Makefile index b281c06695f8..e7bbc9d4757d 100644 --- a/arch/ppc64/Makefile +++ b/arch/ppc64/Makefile @@ -15,7 +15,7 @@ KERNELLOAD =0xc000000000000000 -LINKFLAGS = -T arch/ppc64/vmlinux.lds -Bstatic \ +LDFLAGS_vmlinux = -T arch/ppc64/vmlinux.lds -Bstatic \ -e $(KERNELLOAD) -Ttext $(KERNELLOAD) CFLAGS := $(CFLAGS) -fsigned-char -msoft-float -pipe \ -Wno-uninitialized -mminimal-toc -mtraceback=full \ diff --git a/arch/ppc64/boot/Makefile b/arch/ppc64/boot/Makefile index 0dad54b44742..d96dc878093a 100644 --- a/arch/ppc64/boot/Makefile +++ b/arch/ppc64/boot/Makefile @@ -36,6 +36,7 @@ BOOTAFLAGS = -D__ASSEMBLY__ $(HOSTCFLAGS) CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS LD_ARGS = -Ttext 0x00400000 -e _start +OBJCOPYFLAGS := -S -O binary OBJS = crt0.o start.o main.o zlib.o image.o imagesize.o #LIBS = $(TOPDIR)/lib/lib.a @@ -109,9 +110,8 @@ zImage.initrd: $(OBJS) initrd.o addnote $(BOOTLD) $(LD_ARGS) -T zImage.lds -o $@ $(OBJS) initrd.o $(LIBS) ./addnote $@ - vmlinux.gz: $(TOPDIR)/vmlinux - $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux + $(OBJCOPY) $(OBJCOPYFLAGS) $(TOPDIR)/vmlinux vmlinux ls -l vmlinux | awk '{printf "/* generated -- do not edit! */\nint uncompressed_size = %d;\n", $$5}' > imagesize.c $(CROSS_COMPILE)nm -n $(TOPDIR)/vmlinux | tail -1 | awk '{printf "long vmlinux_end = 0x%s;\n", substr($$1,8)}' >> imagesize.c gzip -vf9 vmlinux diff --git a/arch/s390/Makefile b/arch/s390/Makefile index 681eb4f9a0fa..286017885528 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -13,15 +13,14 @@ # Copyright (C) 1994 by Linus Torvalds # -LD=$(CROSS_COMPILE)ld -m elf_s390 -OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -LDFLAGS=-e start +LDFLAGS := -m elf_s390 +OBJCOPYFLAGS := -O binary ifeq ($(CONFIG_SHARED_KERNEL),y) LINKSCRIPT := arch/s390/vmlinux-shared.lds else LINKSCRIPT := arch/s390/vmlinux.lds endif -LINKFLAGS =-T $(TOPDIR)/$(LINKSCRIPT) $(LDFLAGS) +LDFLAGS_vmlinux := -T $(LINKSCRIPT) -e start CFLAGS_PIPE := -pipe CFLAGS_NSR := -fno-strength-reduce diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 9bc623794c8f..47f55551632d 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -2,21 +2,19 @@ # arch/s390/boot/Makefile # -OBJCOPY = $(CROSS_COMPILE)objcopy - EXTRA_AFLAGS := -traditional include $(TOPDIR)/Rules.make %.lnk: %.o - $(LD) -Ttext 0x0 -o $@ $< + $(LD) $(LDFLAGS) -Ttext 0x0 -o $@ $< %.boot: %.lnk - $(OBJCOPY) -O binary $< $@ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ image: $(TOPDIR)/vmlinux \ iplfba.boot ipleckd.boot ipldump.boot - $(OBJCOPY) -O binary $(TOPDIR)/vmlinux image + $(OBJCOPY) $(OBJCOPYFLAGS) $(TOPDIR)/vmlinux image $(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aUw] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map listing: ../../../vmlinux diff --git a/arch/s390x/Makefile b/arch/s390x/Makefile index b46bf22ec984..ad6a4427b804 100644 --- a/arch/s390x/Makefile +++ b/arch/s390x/Makefile @@ -13,16 +13,15 @@ # Copyright (C) 1994 by Linus Torvalds # -LD=$(CROSS_COMPILE)ld -m elf64_s390 +LDFLAGS := -m elf64_s390 CPP=$(CC) -E -OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -LDFLAGS=-e start +OBJCOPYFLAGS := -O binary ifeq ($(CONFIG_SHARED_KERNEL),y) LINKSCRIPT := arch/s390x/vmlinux-shared.lds else LINKSCRIPT := arch/s390x/vmlinux.lds endif -LINKFLAGS =-T $(TOPDIR)/$(LINKSCRIPT) $(LDFLAGS) +LDFLAGS_vmlinux := -T $(LINKSCRIPT) -e start MODFLAGS += -fpic CFLAGS_PIPE := -pipe diff --git a/arch/s390x/boot/Makefile b/arch/s390x/boot/Makefile index 22d6f52c6942..3a514eedff72 100644 --- a/arch/s390x/boot/Makefile +++ b/arch/s390x/boot/Makefile @@ -2,8 +2,6 @@ # Makefile for the linux s390-specific parts of the memory manager. # -OBJCOPY = $(CROSS_COMPILE)objcopy - O_TARGET := EXTRA_AFLAGS := -traditional @@ -14,11 +12,11 @@ include $(TOPDIR)/Rules.make $(LD) -Ttext 0x0 -o $@ $< %.boot: %.lnk - $(OBJCOPY) -O binary $< $@ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ image: $(TOPDIR)/vmlinux \ iplfba.boot ipleckd.boot ipldump.boot - $(OBJCOPY) -O binary $(TOPDIR)/vmlinux image + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ $(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aUw] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map listing: ../../../vmlinux diff --git a/arch/sh/Makefile b/arch/sh/Makefile index b3c0bbe6c078..be546cb3f085 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -15,17 +15,16 @@ ifdef CONFIG_CPU_LITTLE_ENDIAN CFLAGS += -ml AFLAGS += -ml -# LINKFLAGS += -EL +# LDFLAGS_vmlinux += -EL LDFLAGS := -EL else CFLAGS += -mb AFLAGS += -mb -# LINKFLAGS += -EB +# LDFLAGS_vmlinux += -EB LDFLAGS := -EB endif -LD =$(CROSS_COMPILE)ld $(LDFLAGS) -OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -R .stab -R .stabstr -S +OBJCOPYFLAGS := -O binary -R .note -R .comment -R .stab -R .stabstr -S MODFLAGS += @@ -47,10 +46,10 @@ endif # none has been choosen above. # LINKSCRIPT = arch/sh/vmlinux.lds -LINKFLAGS += -T $(word 1,$(LINKSCRIPT)) -e _stext +LDFLAGS_vmlinux += -T $(word 1,$(LINKSCRIPT)) -e _stext ifdef LOADADDR -LINKFLAGS += -Ttext $(word 1,$(LOADADDR)) +LDFLAGS_vmlinux += -Ttext $(word 1,$(LOADADDR)) endif # diff --git a/arch/sh/boot/Makefile b/arch/sh/boot/Makefile index aeb304826d9a..4d3dd9419eb9 100644 --- a/arch/sh/boot/Makefile +++ b/arch/sh/boot/Makefile @@ -11,10 +11,10 @@ SYSTEM =$(TOPDIR)/vmlinux Image: $(SYSTEM) - $(OBJCOPY) $(SYSTEM) Image + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ zImage: compressed/vmlinux - $(OBJCOPY) compressed/vmlinux zImage + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ compressed/vmlinux: $(TOPDIR)/vmlinux $(MAKE) -C compressed vmlinux diff --git a/arch/sh/boot/compressed/Makefile b/arch/sh/boot/compressed/Makefile index 7e34e14dab2b..b2efc40bf86d 100644 --- a/arch/sh/boot/compressed/Makefile +++ b/arch/sh/boot/compressed/Makefile @@ -25,7 +25,7 @@ ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS) all: vmlinux vmlinux: piggy.o $(OBJECTS) - $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o + $(LD) $(LDFLAGS) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o head.o: head.S $(CC) $(AFLAGS) -traditional -c head.S @@ -33,10 +33,10 @@ head.o: head.S piggy.o: $(SYSTEM) tmppiggy=_tmp_$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ - $(OBJCOPY) -R .empty_zero_page $(SYSTEM) $$tmppiggy; \ + $(OBJCOPY) $(OBJCOPYFLAGS) -R .empty_zero_page $(SYSTEM) $$tmppiggy; \ gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ - $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-sh-linux -T $$tmppiggy.lnk; \ + $(LD) $(LDFLAGS) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-sh-linux -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk clean: diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile index ad328c3e5bca..04ca208da275 100644 --- a/arch/sparc/Makefile +++ b/arch/sparc/Makefile @@ -20,7 +20,7 @@ NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; the ifeq ($(NEW_GAS),y) AS := $(AS) -32 -LD := $(LD) -m elf32_sparc +LDFLAGS := -m elf32_sparc endif #CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7 @@ -30,8 +30,8 @@ else CFLAGS := $(CFLAGS) -m32 -pipe -mno-fpu -fcall-used-g5 -fcall-used-g7 endif -#LINKFLAGS = -N -Ttext 0xf0004000 -LINKFLAGS = -T arch/sparc/vmlinux.lds +#LDFLAGS_vmlinux = -N -Ttext 0xf0004000 +LDFLAGS_vmlinux = -T arch/sparc/vmlinux.lds HEAD := arch/sparc/kernel/head.o arch/sparc/kernel/init_task.o diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile index ea6c7052175d..3e2ceaf085fe 100644 --- a/arch/sparc/boot/Makefile +++ b/arch/sparc/boot/Makefile @@ -31,7 +31,7 @@ BTLIBS := $(CORE_FILES_NO_BTFIX) $(FILESYSTEMS) \ # properly as it will cause head.o to be built with the implicit # rules not the ones in kernel/Makefile. Someone please fix. --DaveM vmlinux.o: FORCE - $(LD) -r $(patsubst %,$(TOPDIR)/%,$(BTOBJS)) \ + $(LD) $(LDFLAGS) -r $(patsubst %,$(TOPDIR)/%,$(BTOBJS)) \ --start-group \ $(patsubst %,$(TOPDIR)/%,$(BTLIBS)) \ $(LIBS) \ diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index f1bee59d0b78..312431c6bb86 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -28,7 +28,7 @@ AR = sparc64-linux-ar RANLIB = sparc64-linux-ranlib else AS := $(AS) -64 -LD := $(LD) -m elf64_sparc +LDFLAGS := -m elf64_sparc endif ELFTOAOUT = elftoaout ifneq ($(UNDECLARED_REGS),y) @@ -53,7 +53,7 @@ ifeq ($(CONFIG_MCOUNT),y) CFLAGS := $(CFLAGS) -pg endif -LINKFLAGS = -T arch/sparc64/vmlinux.lds +LDFLAGS_vmlinux = -T arch/sparc64/vmlinux.lds HEAD := arch/sparc64/kernel/head.o arch/sparc64/kernel/init_task.o diff --git a/arch/x86_64/Makefile b/arch/x86_64/Makefile index b78fca37a641..fc75b3b338ee 100644 --- a/arch/x86_64/Makefile +++ b/arch/x86_64/Makefile @@ -34,9 +34,8 @@ export IA32_CC IA32_LD IA32_AS IA32_OBJCOPY IA32_CPP LD=$(CROSS_COMPILE)ld -m elf_x86_64 -OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -LDFLAGS=-e stext -LINKFLAGS =-T $(TOPDIR)/arch/x86_64/vmlinux.lds $(LDFLAGS) +OBJCOPYFLAGS := -O binary -R .note -R .comment -S +LDFLAGS_vmlinux := -T arch/x86_64/vmlinux.lds -e stext CFLAGS += -mno-red-zone CFLAGS += -mcmodel=kernel diff --git a/arch/x86_64/boot/Makefile b/arch/x86_64/boot/Makefile index 92aa42546bf4..68cb973787ac 100644 --- a/arch/x86_64/boot/Makefile +++ b/arch/x86_64/boot/Makefile @@ -28,11 +28,11 @@ BOOT_INCL = $(TOPDIR)/include/linux/config.h \ $(TOPDIR)/include/asm/boot.h zImage: bootsect setup compressed/vmlinux tools/build - $(OBJCOPY) compressed/vmlinux compressed/vmlinux.out + $(OBJCOPY) $(OBJCOPYFLAGS) compressed/vmlinux compressed/vmlinux.out tools/build bootsect setup compressed/vmlinux.out $(ROOT_DEV) > zImage bzImage: bbootsect bsetup compressed/bvmlinux tools/build - $(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out + $(OBJCOPY) $(OBJCOPYFLAGS) compressed/bvmlinux compressed/bvmlinux.out tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage bzImage-padded: bzImage diff --git a/arch/x86_64/boot/compressed/Makefile b/arch/x86_64/boot/compressed/Makefile index 0832d0231699..83b1b657b5e6 100644 --- a/arch/x86_64/boot/compressed/Makefile +++ b/arch/x86_64/boot/compressed/Makefile @@ -33,7 +33,7 @@ misc.o: misc.c piggy.o: $(SYSTEM) tmppiggy=_tmp_$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ - $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $$tmppiggy; \ gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ $(IA32_LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ diff --git a/drivers/acorn/char/Makefile b/drivers/acorn/char/Makefile index fc00f7902cb5..06acf0a7ea75 100644 --- a/drivers/acorn/char/Makefile +++ b/drivers/acorn/char/Makefile @@ -20,5 +20,16 @@ obj-y += $(obj-$(MACHINE)) include $(TOPDIR)/Rules.make -$(obj)/%.c: $(src)/%.map +$(obj)/defkeymap-acorn.o: $(obj)/defkeymap-acorn.c + +# Uncomment if you're changing the keymap and have an appropriate +# loadkeys version for the map. By default, we'll use the shipped +# versions. +# GENERATE_KEYMAP := 1 + +ifdef GENERATE_KEYMAP + +$(obj)/defkeymap-acorn.c: $(obj)/%.c: $(src)/%.map loadkeys --mktable $< > $@ + +endif diff --git a/drivers/acorn/char/defkeymap-acorn.c b/drivers/acorn/char/defkeymap-acorn.c_shipped index 63d366cc2658..63d366cc2658 100644 --- a/drivers/acorn/char/defkeymap-acorn.c +++ b/drivers/acorn/char/defkeymap-acorn.c_shipped diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 5a99b84a81c3..ed74453f7c64 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -219,8 +219,20 @@ include $(TOPDIR)/Rules.make $(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) $(obj)/conmakehash $(obj)/conmakehash $< > $@ -$(obj)/defkeymap.c: $(src)/defkeymap.map - set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ +$(obj)/defkeymap.o: $(obj)/defkeymap.c -$(obj)/qtronixmap.c: $(src)/qtronixmap.map - set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ +$(obj)/qtronixmap.o: $(obj)/qtronixmap.c + +# Uncomment if you're changing the keymap and have an appropriate +# loadkeys version for the map. By default, we'll use the shipped +# versions. +# GENERATE_KEYMAP := 1 + +ifdef GENERATE_KEYMAP + +$(obj)/defkeymap.c $(obj)/qtronixmap.c: $(obj)/%.c: $(src)/%.map + loadkeys --mktable $< > $@.tmp + sed -e 's/^static *//' $@.tmp > $@ + rm $@.tmp + +endif
\ No newline at end of file diff --git a/drivers/char/defkeymap.c_shipped b/drivers/char/defkeymap.c_shipped new file mode 100644 index 000000000000..d8ac176c251d --- /dev/null +++ b/drivers/char/defkeymap.c_shipped @@ -0,0 +1,262 @@ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include <linux/types.h> +#include <linux/keyboard.h> +#include <linux/kd.h> + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, + 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, + 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, + 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, + 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, + 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, + 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, + 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, + 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, + 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, + 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, + 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, + 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, + 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, + 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73, + 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76, + 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, + 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911, + 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, + 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, + 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, + 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, + 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short alt_map[NR_KEYS] = { + 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, + 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, + 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, + 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, + 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, + 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, + 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, + 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907, + 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, + 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, + 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813, + 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0 +}; + +unsigned int keymap_count = 7; + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', '\300'}, {'`', 'a', '\340'}, + {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, + {'^', 'A', '\302'}, {'^', 'a', '\342'}, + {'~', 'A', '\303'}, {'~', 'a', '\343'}, + {'"', 'A', '\304'}, {'"', 'a', '\344'}, + {'O', 'A', '\305'}, {'o', 'a', '\345'}, + {'0', 'A', '\305'}, {'0', 'a', '\345'}, + {'A', 'A', '\305'}, {'a', 'a', '\345'}, + {'A', 'E', '\306'}, {'a', 'e', '\346'}, + {',', 'C', '\307'}, {',', 'c', '\347'}, + {'`', 'E', '\310'}, {'`', 'e', '\350'}, + {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, + {'^', 'E', '\312'}, {'^', 'e', '\352'}, + {'"', 'E', '\313'}, {'"', 'e', '\353'}, + {'`', 'I', '\314'}, {'`', 'i', '\354'}, + {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, + {'^', 'I', '\316'}, {'^', 'i', '\356'}, + {'"', 'I', '\317'}, {'"', 'i', '\357'}, + {'-', 'D', '\320'}, {'-', 'd', '\360'}, + {'~', 'N', '\321'}, {'~', 'n', '\361'}, + {'`', 'O', '\322'}, {'`', 'o', '\362'}, + {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, + {'^', 'O', '\324'}, {'^', 'o', '\364'}, + {'~', 'O', '\325'}, {'~', 'o', '\365'}, + {'"', 'O', '\326'}, {'"', 'o', '\366'}, + {'/', 'O', '\330'}, {'/', 'o', '\370'}, + {'`', 'U', '\331'}, {'`', 'u', '\371'}, + {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, + {'^', 'U', '\333'}, {'^', 'u', '\373'}, + {'"', 'U', '\334'}, {'"', 'u', '\374'}, + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, + {'T', 'H', '\336'}, {'t', 'h', '\376'}, + {'s', 's', '\337'}, {'"', 'y', '\377'}, + {'s', 'z', '\337'}, {'i', 'j', '\377'}, +}; + +unsigned int accent_table_size = 68; diff --git a/drivers/char/qtronixmap.c_shipped b/drivers/char/qtronixmap.c_shipped new file mode 100644 index 000000000000..1e2b92b7d57a --- /dev/null +++ b/drivers/char/qtronixmap.c_shipped @@ -0,0 +1,265 @@ + +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include <linux/types.h> +#include <linux/keyboard.h> +#include <linux/kd.h> + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, + 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf200, 0xf07f, + 0xf009, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, + 0xfb69, 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf05c, 0xf207, 0xfb61, + 0xfb73, 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, + 0xf03b, 0xf027, 0xf060, 0xf201, 0xf700, 0xf200, 0xfb7a, 0xfb78, + 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf020, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf118, 0xf119, 0xf200, + 0xf200, 0xf602, 0xf208, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf01b, 0xf200, + 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, + 0xf108, 0xf109, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +u_short shift_map[NR_KEYS] = { + 0xf200, 0xf07e, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, + 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf200, 0xf07f, + 0xf009, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, + 0xfb49, 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf07c, 0xf207, 0xfb41, + 0xfb53, 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, + 0xf03a, 0xf022, 0xf07e, 0xf201, 0xf700, 0xf200, 0xfb5a, 0xfb58, + 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf020, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf20b, 0xf20a, 0xf200, + 0xf200, 0xf602, 0xf213, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf01b, 0xf200, + 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 0xf111, + 0xf112, 0xf113, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, + 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, + 0xf200, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, + 0xfb69, 0xfb6f, 0xfb70, 0xf200, 0xf200, 0xf200, 0xf207, 0xfb61, + 0xfb73, 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, + 0xf200, 0xf200, 0xf200, 0xf201, 0xf700, 0xf200, 0xfb7a, 0xfb78, + 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf200, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf118, 0xf119, 0xf200, + 0xf200, 0xf602, 0xf208, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf200, 0xf200, + 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 0xf511, 0xf512, 0xf513, + 0xf514, 0xf515, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, + 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf008, + 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, + 0xf009, 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf01c, 0xf207, 0xf001, + 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, + 0xf007, 0xf000, 0xf200, 0xf201, 0xf700, 0xf200, 0xf01a, 0xf018, + 0xf003, 0xf016, 0xf002, 0xf00e, 0xf20e, 0xf07f, 0xf200, 0xf200, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf000, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf118, 0xf119, 0xf200, + 0xf200, 0xf602, 0xf208, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf200, 0xf200, + 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, + 0xf108, 0xf109, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, + 0xf009, 0xf00f, 0xf010, 0xf200, 0xf200, 0xf200, 0xf207, 0xf001, + 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, + 0xf200, 0xf200, 0xf200, 0xf201, 0xf700, 0xf200, 0xf01a, 0xf018, + 0xf003, 0xf016, 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf200, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf118, 0xf119, 0xf200, + 0xf200, 0xf602, 0xf208, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +u_short alt_map[NR_KEYS] = { + 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, + 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf200, 0xf87f, + 0xf809, 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, + 0xf869, 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf85c, 0xf207, 0xf861, + 0xf873, 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf83b, + 0xf827, 0xf860, 0xf200, 0xf80d, 0xf700, 0xf200, 0xf87a, 0xf878, + 0xf863, 0xf876, 0xf862, 0xf82c, 0xf82e, 0xf82f, 0xf200, 0xf200, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf820, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf210, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf118, 0xf119, 0xf200, + 0xf200, 0xf211, 0xf208, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf200, 0xf200, + 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, 0xf507, + 0xf508, 0xf509, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, + 0xf809, 0xf80f, 0xf810, 0xf200, 0xf200, 0xf200, 0xf207, 0xf801, + 0xf813, 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, + 0xf200, 0xf200, 0xf200, 0xf201, 0xf700, 0xf200, 0xf81a, 0xf818, + 0xf803, 0xf816, 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf700, 0xf702, 0xf200, 0xf703, 0xf200, 0xf703, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf601, + 0xf200, 0xf200, 0xf200, 0xf603, 0xf600, 0xf118, 0xf119, 0xf200, + 0xf200, 0xf602, 0xf208, 0xf02d, 0xf02b, 0xf30c, 0xf02e, 0xf30d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf117, 0xf600, 0xf200, 0xf200, 0xf200, + 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, 0xf507, + 0xf508, 0xf509, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0 +}; + +unsigned int keymap_count = 7; + + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', 'À'}, {'`', 'a', 'à'}, + {'\'', 'A', 'Á'}, {'\'', 'a', 'á'}, + {'^', 'A', 'Â'}, {'^', 'a', 'â'}, + {'~', 'A', 'Ã'}, {'~', 'a', 'ã'}, + {'"', 'A', 'Ä'}, {'"', 'a', 'ä'}, + {'O', 'A', 'Å'}, {'o', 'a', 'å'}, + {'0', 'A', 'Å'}, {'0', 'a', 'å'}, + {'A', 'A', 'Å'}, {'a', 'a', 'å'}, + {'A', 'E', 'Æ'}, {'a', 'e', 'æ'}, + {',', 'C', 'Ç'}, {',', 'c', 'ç'}, + {'`', 'E', 'È'}, {'`', 'e', 'è'}, + {'\'', 'E', 'É'}, {'\'', 'e', 'é'}, + {'^', 'E', 'Ê'}, {'^', 'e', 'ê'}, + {'"', 'E', 'Ë'}, {'"', 'e', 'ë'}, + {'`', 'I', 'Ì'}, {'`', 'i', 'ì'}, + {'\'', 'I', 'Í'}, {'\'', 'i', 'í'}, + {'^', 'I', 'Î'}, {'^', 'i', 'î'}, + {'"', 'I', 'Ï'}, {'"', 'i', 'ï'}, + {'-', 'D', 'Ð'}, {'-', 'd', 'ð'}, + {'~', 'N', 'Ñ'}, {'~', 'n', 'ñ'}, + {'`', 'O', 'Ò'}, {'`', 'o', 'ò'}, + {'\'', 'O', 'Ó'}, {'\'', 'o', 'ó'}, + {'^', 'O', 'Ô'}, {'^', 'o', 'ô'}, + {'~', 'O', 'Õ'}, {'~', 'o', 'õ'}, + {'"', 'O', 'Ö'}, {'"', 'o', 'ö'}, + {'/', 'O', 'Ø'}, {'/', 'o', 'ø'}, + {'`', 'U', 'Ù'}, {'`', 'u', 'ù'}, + {'\'', 'U', 'Ú'}, {'\'', 'u', 'ú'}, + {'^', 'U', 'Û'}, {'^', 'u', 'û'}, + {'"', 'U', 'Ü'}, {'"', 'u', 'ü'}, + {'\'', 'Y', 'Ý'}, {'\'', 'y', 'ý'}, + {'T', 'H', 'Þ'}, {'t', 'h', 'þ'}, + {'s', 's', 'ß'}, {'"', 'y', 'ÿ'}, + {'s', 'z', 'ß'}, {'i', 'j', 'ÿ'}, +}; + +unsigned int accent_table_size = 68; diff --git a/drivers/ide/probe.c b/drivers/ide/probe.c index b1b026f76cfc..ab7df11e44a7 100644 --- a/drivers/ide/probe.c +++ b/drivers/ide/probe.c @@ -1173,18 +1173,6 @@ static void channel_init(struct ata_channel *ch) gd->next = NULL; /* linked list of major devs */ gd->fops = ide_fops; /* file operations */ - gd->de_arr = kmalloc(sizeof(*gd->de_arr) * MAX_DRIVES, GFP_KERNEL); - if (gd->de_arr) - memset(gd->de_arr, 0, sizeof(*gd->de_arr) * MAX_DRIVES); - else - goto err_kmalloc_gd_de_arr; - - gd->flags = kmalloc(sizeof(*gd->flags) * MAX_DRIVES, GFP_KERNEL); - if (gd->flags) - memset(gd->flags, 0, sizeof(*gd->flags) * MAX_DRIVES); - else - goto err_kmalloc_gd_flags; - ch->gd = gd; add_gendisk(gd); diff --git a/drivers/input/Config.help b/drivers/input/Config.help index 15bc1aab59bd..e75624d0917d 100644 --- a/drivers/input/Config.help +++ b/drivers/input/Config.help @@ -64,6 +64,18 @@ CONFIG_INPUT_JOYDEV The module will be called joydev.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +CONFIG_INPUT_TSDEV + 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. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tsdev.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + CONFIG_INPUT_EVDEV Say Y here if you want your input device events be accessible under char device 13:64+ - /dev/input/eventX in a generic way. @@ -72,3 +84,17 @@ CONFIG_INPUT_EVDEV inserted in and removed from the running kernel whenever you want). The module will be called evdev.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_INPUT_EVBUG + 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. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called joydev.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. diff --git a/drivers/input/Config.in b/drivers/input/Config.in index 66b2aa72897e..7105ceb2f7a6 100644 --- a/drivers/input/Config.in +++ b/drivers/input/Config.in @@ -6,20 +6,34 @@ mainmenu_option next_comment comment 'Input device support' tristate 'Input core support' CONFIG_INPUT + +comment 'Userland interfaces' dep_tristate ' Keyboard interface' CONFIG_INPUT_KEYBDEV $CONFIG_INPUT dep_tristate ' Mouse interface' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT +dep_mbool ' Provide legacy /dev/psaux device' CONFIG_INPUT_MOUSEDEV_PSAUX $CONFIG_INPUT if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 fi dep_tristate ' Joystick interface' CONFIG_INPUT_JOYDEV $CONFIG_INPUT +dep_tristate ' Touchscreen interface' CONFIG_INPUT_TSDEV $CONFIG_INPUT +if [ "$CONFIG_INPUT_TSDEV" != "n" ]; then + int ' Horizontal screen resolution' CONFIG_INPUT_TSDEV_SCREEN_X 240 + int ' Vertical screen resolution' CONFIG_INPUT_TSDEV_SCREEN_Y 320 +fi dep_tristate ' Event interface' CONFIG_INPUT_EVDEV $CONFIG_INPUT +dep_tristate ' Event debugging' CONFIG_INPUT_EVBUG $CONFIG_INPUT +comment 'Input I/O drivers' source drivers/input/gameport/Config.in source drivers/input/serio/Config.in +comment 'Input Device Drivers' if [ "$CONFIG_INPUT" != "n" ]; then + source drivers/input/keyboard/Config.in + source drivers/input/mouse/Config.in source drivers/input/joystick/Config.in + source drivers/input/touchscreen/Config.in fi endmenu diff --git a/drivers/input/Makefile b/drivers/input/Makefile index bdb70aa4a996..d6a647876456 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -13,8 +13,14 @@ obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.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/ # The global Rules.make. diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c new file mode 100644 index 000000000000..1b91f246cd08 --- /dev/null +++ b/drivers/input/evbug.c @@ -0,0 +1,99 @@ +/* + * $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> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Input driver event debug module"); +MODULE_LICENSE("GPL"); + +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; + + 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); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 413002ffa574..0a2f930cf585 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -1,5 +1,5 @@ /* - * $Id: evdev.c,v 1.42 2002/01/02 11:59:56 vojtech Exp $ + * $Id: evdev.c,v 1.48 2002/05/26 14:28:26 jdeneux Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * @@ -40,7 +40,6 @@ struct evdev { int exist; int open; - int open_for_write; int minor; char name[16]; struct input_handle handle; @@ -91,7 +90,9 @@ static int evdev_fasync(int fd, struct file *file, int on) static int evdev_flush(struct file * file) { - return input_flush_device(&((struct evdev_list*)file->private_data)->evdev->handle, file); + struct evdev_list *list = (struct evdev_list*)file->private_data; + if (!list->evdev->exist) return -ENODEV; + return input_flush_device(&list->evdev->handle, file); } static int evdev_release(struct inode * inode, struct file * file) @@ -158,6 +159,8 @@ static ssize_t evdev_write(struct file * file, const char * buffer, size_t count 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))) @@ -232,6 +235,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct input_dev *dev = evdev->handle.dev; int retval, t, u; + if (!evdev->exist) return -ENODEV; + switch (cmd) { case EVIOCGVERSION: @@ -284,11 +289,11 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, int err; if (copy_from_user((void*)(&effect), (void*)arg, sizeof(effect))) { - return -EINVAL; + return -EFAULT; } err = dev->upload_effect(dev, &effect); if (put_user(effect.id, &(((struct ff_effect*)arg)->id))) { - return -EINVAL; + return -EFAULT; } return err; } @@ -301,7 +306,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, else return -ENOSYS; case EVIOCGEFFECTS: - put_user(dev->ff_effects_max, (int*) arg); + if ((retval = put_user(dev->ff_effects_max, (int*) arg))) + return retval; return 0; default: diff --git a/drivers/input/gameport/Config.help b/drivers/input/gameport/Config.help index 54cac2482adf..10b849c461c3 100644 --- a/drivers/input/gameport/Config.help +++ b/drivers/input/gameport/Config.help @@ -34,7 +34,7 @@ CONFIG_GAMEPORT_L4 The module will be called lightning.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. -CONFIG_GAMEPORT_EMU10K1 +CONFIG_INPUT_EMU10K1 Say Y here if you have a SoundBlaster Live! or SoundBlaster Audigy card and want to use its gameport. @@ -43,16 +43,16 @@ CONFIG_GAMEPORT_EMU10K1 The module will be called emu10k1-gp.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. -CONFIG_GAMEPORT_PCIGAME - Say Y here if you have an Aureal Vortex 1 or 2 or a Trident - 4DWave NX or DX card and want to use its gameport. +CONFIG_GAMEPORT_VORTEX + Say Y here if you have an Aureal Vortex 1 or 2 card and want + to use its gameport. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called pcigame.o. If you want to compile it as a + The module will be called vortex.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. -CONFIG_GAMEPORT_CS461X +CONFIG_GAMEPORT_CS461x Say Y here if you have a Cirrus CS461x aka "Crystal SoundFusion" PCI audio accelerator and want to use its gameport. diff --git a/drivers/input/gameport/Config.in b/drivers/input/gameport/Config.in index df89f6a6f189..eeb983ec42a5 100644 --- a/drivers/input/gameport/Config.in +++ b/drivers/input/gameport/Config.in @@ -14,6 +14,6 @@ fi dep_tristate ' Classic ISA and PnP gameport support' CONFIG_GAMEPORT_NS558 $CONFIG_GAMEPORT dep_tristate ' PDPI Lightning 4 gamecard support' CONFIG_GAMEPORT_L4 $CONFIG_GAMEPORT dep_tristate ' SB Live and Audigy gameport support' CONFIG_INPUT_EMU10K1 $CONFIG_GAMEPORT -dep_tristate ' Aureal Vortex, Vortex 2 and Trident 4DWave NX/DX gameport support' CONFIG_GAMEPORT_PCIGAME $CONFIG_GAMEPORT +dep_tristate ' Aureal Vortex, Vortex 2 gameport support' CONFIG_GAMEPORT_VORTEX $CONFIG_GAMEPORT dep_tristate ' ForteMedia FM801 gameport support' CONFIG_GAMEPORT_FM801 $CONFIG_GAMEPORT dep_tristate ' Crystal SoundFusion gameport support' CONFIG_GAMEPORT_CS461x $CONFIG_GAMEPORT diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile index 60509253a031..ac1991701f6a 100644 --- a/drivers/input/gameport/Makefile +++ b/drivers/input/gameport/Makefile @@ -11,9 +11,10 @@ export-objs := gameport.o 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_PCIGAME) += pcigame.o +obj-$(CONFIG_GAMEPORT_VORTEX) += vortex.o # The global Rules.make. diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c new file mode 100644 index 000000000000..7cd454338f83 --- /dev/null +++ b/drivers/input/gameport/fm801-gp.c @@ -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) { + kfree(gp); + printk("unable to grab region 0x%x-0x%x\n", gp->gameport.io, gp->gameport.io + 0x0f); + return -1; + } + + gp->gameport.phys = gp->phys; + gp->gameport.name = gp->name; + gp->gameport.idbus = BUS_PCI; + gp->gameport.idvendor = pci->vendor; + gp->gameport.idproduct = 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: %s at pci%s speed %d kHz\n", + pci->name, pci->slot_name, 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[] __devinitdata = { + { 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 GP", + id_table: fm801_gp_id_table, + probe: fm801_gp_probe, + remove: 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"); diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 01542530e6e4..4ade83a2db99 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -54,16 +54,36 @@ EXPORT_SYMBOL(gameport_cooked_read); static struct gameport *gameport_list; static struct gameport_dev *gameport_dev; + +#ifdef __i386__ + +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180/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) { -#if defined(__i386__) || defined(__x86_64__) - -#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) -#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) +#ifdef __i386__ unsigned int i, t, t1, t2, t3, tx; unsigned long flags; diff --git a/drivers/input/gameport/pcigame.c b/drivers/input/gameport/pcigame.c deleted file mode 100644 index 194814ca64fc..000000000000 --- a/drivers/input/gameport/pcigame.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * $Id: pcigame.c,v 1.10 2001/04/26 10:24:46 vojtech Exp $ - * - * Copyright (c) 2000-2001 Vojtech Pavlik - * - * Based on the work of: - * Raymond Ingles - * - * Sponsored by SuSE - */ - -/* - * 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@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, 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/gameport.h> - -#define PCI_VENDOR_ID_AUREAL 0x12eb - -#define PCIGAME_DATA_WAIT 20 /* 20 ms */ - -#define PCIGAME_4DWAVE 0 -#define PCIGAME_VORTEX 1 -#define PCIGAME_VORTEX2 2 - -struct pcigame_data { - int gcr; /* Gameport control register */ - int legacy; /* Legacy port location */ - int axes; /* Axes start */ - int axsize; /* Axis field size */ - int axmax; /* Axis field max value */ - int adcmode; /* Value to enable ADC mode in GCR */ -}; - -static struct pcigame_data pcigame_data[] __devinitdata = -{{ 0x00030, 0x00031, 0x00034, 2, 0xffff, 0x80 }, - { 0x1100c, 0x11008, 0x11010, 4, 0x1fff, 0x40 }, - { 0x2880c, 0x28808, 0x28810, 4, 0x1fff, 0x40 }, - { 0 }}; - -struct pcigame { - struct gameport gameport; - struct pci_dev *dev; - unsigned char *base; - struct pcigame_data *data; -}; - -static unsigned char pcigame_read(struct gameport *gameport) -{ - struct pcigame *pcigame = gameport->private; - return readb(pcigame->base + pcigame->data->legacy); -} - -static void pcigame_trigger(struct gameport *gameport) -{ - struct pcigame *pcigame = gameport->private; - writeb(0xff, pcigame->base + pcigame->data->legacy); -} - -static int pcigame_cooked_read(struct gameport *gameport, int *axes, int *buttons) -{ - struct pcigame *pcigame = gameport->private; - int i; - - *buttons = (~readb(pcigame->base + pcigame->data->legacy) >> 4) & 0xf; - - for (i = 0; i < 4; i++) { - axes[i] = readw(pcigame->base + pcigame->data->axes + i * pcigame->data->axsize); - if (axes[i] == pcigame->data->axmax) axes[i] = -1; - } - - return 0; -} - -static int pcigame_open(struct gameport *gameport, int mode) -{ - struct pcigame *pcigame = gameport->private; - - switch (mode) { - case GAMEPORT_MODE_COOKED: - writeb(pcigame->data->adcmode, pcigame->base + pcigame->data->gcr); - wait_ms(PCIGAME_DATA_WAIT); - return 0; - case GAMEPORT_MODE_RAW: - writeb(0, pcigame->base + pcigame->data->gcr); - return 0; - default: - return -1; - } - - return 0; -} - -static int __devinit pcigame_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - struct pcigame *pcigame; - int i; - - if (!(pcigame = kmalloc(sizeof(struct pcigame), GFP_KERNEL))) - return -1; - memset(pcigame, 0, sizeof(struct pcigame)); - - - pcigame->data = pcigame_data + id->driver_data; - - pcigame->dev = dev; - pci_set_drvdata(dev, pcigame); - - pcigame->gameport.private = pcigame; - pcigame->gameport.fuzz = 64; - - pcigame->gameport.read = pcigame_read; - pcigame->gameport.trigger = pcigame_trigger; - pcigame->gameport.cooked_read = pcigame_cooked_read; - pcigame->gameport.open = pcigame_open; - - for (i = 0; i < 6; i++) - if (~pci_resource_flags(dev, i) & IORESOURCE_IO) - break; - - pci_enable_device(dev); - - pcigame->base = ioremap(pci_resource_start(pcigame->dev, i), - pci_resource_len(pcigame->dev, i)); - - gameport_register_port(&pcigame->gameport); - - printk(KERN_INFO "gameport%d: %s at pci%02x:%02x.%x speed %d kHz\n", - pcigame->gameport.number, dev->name, dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), pcigame->gameport.speed); - - return 0; -} - -static void __devexit pcigame_remove(struct pci_dev *dev) -{ - struct pcigame *pcigame = pci_get_drvdata(dev); - gameport_unregister_port(&pcigame->gameport); - iounmap(pcigame->base); - kfree(pcigame); -} - -static struct pci_device_id pcigame_id_table[] __devinitdata = -{{ PCI_VENDOR_ID_TRIDENT, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE }, - { PCI_VENDOR_ID_TRIDENT, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE }, - { PCI_VENDOR_ID_AUREAL, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX }, - { PCI_VENDOR_ID_AUREAL, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX2 }, - { 0 }}; - -static struct pci_driver pcigame_driver = { - name: "pcigame", - id_table: pcigame_id_table, - probe: pcigame_probe, - remove: __devexit_p(pcigame_remove), -}; - -int __init pcigame_init(void) -{ - return pci_module_init(&pcigame_driver); -} - -void __exit pcigame_exit(void) -{ - pci_unregister_driver(&pcigame_driver); -} - -module_init(pcigame_init); -module_exit(pcigame_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/input/gameport/vortex.c b/drivers/input/gameport/vortex.c new file mode 100644 index 000000000000..c2c12e7d4a5b --- /dev/null +++ b/drivers/input/gameport/vortex.c @@ -0,0 +1,185 @@ +/* + * $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/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 *base; + unsigned char *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); + wait_ms(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", dev->slot_name); + + 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 = dev->name; + vortex->gameport.phys = vortex->phys; + vortex->gameport.idbus = BUS_PCI; + vortex->gameport.idvendor = dev->vendor; + vortex->gameport.idproduct = 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: %s at pci%s speed %d kHz\n", + dev->name, dev->slot_name, 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[] __devinitdata = +{{ 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", + id_table: vortex_id_table, + probe: vortex_probe, + remove: 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); diff --git a/drivers/input/input.c b/drivers/input/input.c index e80f4a4c8732..93241c7d577e 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -61,8 +61,6 @@ static struct input_dev *input_dev; static struct input_handler *input_handler; static struct input_handler *input_table[8]; static devfs_handle_t input_devfs_handle; -static int input_number; -static long input_devices[NBITS(INPUT_DEVICES)]; #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_bus_input_dir; @@ -454,17 +452,8 @@ void input_register_device(struct input_dev *dev) * Add the device. */ - if (input_number >= INPUT_DEVICES) { - printk(KERN_WARNING "input: ran out of input device numbers!\n"); - dev->number = input_number; - } else { - dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES); - set_bit(dev->number, input_devices); - } - dev->next = input_dev; input_dev = dev; - input_number++; /* * Notify handlers. @@ -493,7 +482,6 @@ void input_register_device(struct input_dev *dev) input_devices_state++; wake_up(&input_devices_poll_wait); #endif - } void input_unregister_device(struct input_dev *dev) @@ -509,7 +497,6 @@ void input_unregister_device(struct input_dev *dev) if (dev->pm_dev) pm_unregister(dev->pm_dev); - /* * Kill any pending repeat timers. */ @@ -540,7 +527,6 @@ void input_unregister_device(struct input_dev *dev) */ input_find_and_remove(struct input_dev, input_dev, dev, next); - input_number--; /* * Notify /proc. */ diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index e5b845e29468..416aace2a2a1 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -1,5 +1,5 @@ /* - * $Id: joydev.c,v 1.38 2001/12/27 10:37:41 vojtech Exp $ + * $Id: joydev.c,v 1.43 2002/04/09 23:59:01 jsimmons Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * Copyright (c) 1999 Colin Van Dyke @@ -49,7 +49,7 @@ MODULE_SUPPORTED_DEVICE("input/js"); MODULE_LICENSE("GPL"); #define JOYDEV_MINOR_BASE 0 -#define JOYDEV_MINORS 32 +#define JOYDEV_MINORS 16 #define JOYDEV_BUFFER_SIZE 64 #define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ) @@ -254,6 +254,10 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p while (list->head == list->tail) { + if (!joydev->exist) { + retval = -ENODEV; + break; + } if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; @@ -325,6 +329,8 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd struct input_dev *dev = joydev->handle.dev; int i; + if (!joydev->exist) return -ENODEV; + switch (cmd) { case JS_SET_CAL: diff --git a/drivers/input/joystick/Config.help b/drivers/input/joystick/Config.help index 1b4187bbb8a3..155f0b83064a 100644 --- a/drivers/input/joystick/Config.help +++ b/drivers/input/joystick/Config.help @@ -1,4 +1,4 @@ -CONFIG_JOYSTICK +CONFIG_INPUT_JOYSTICK 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 @@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP The module will be called grip.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +CONFIG_JOYSTICK_GUILLEMOT + Say Y here if you have a Guillemot joystick using a digital + protocol over the PC gameport. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called guillemot.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + CONFIG_JOYSTICK_INTERACT Say Y here if you have an InterAct gameport or joystick communicating digitally over the gameport. @@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER The module will be called stinger.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +CONFIG_JOYSTICK_TWIDDLER + Say Y here if you have a Handykey Twiddler connected to your + computer's serial port and want to use it as a joystick. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called twidjoy.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + CONFIG_JOYSTICK_DB9 Say Y here if you have a Sega Master System gamepad, Sega Genesis gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, @@ -203,3 +221,12 @@ CONFIG_JOYSTICK_AMIJOY The module will be called joy-amiga.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +CONFIG_INPUT_JOYDUMP + 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. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called joydump.o. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt>. diff --git a/drivers/input/joystick/Config.in b/drivers/input/joystick/Config.in index afd07b89dff3..bb69101b9b9e 100644 --- a/drivers/input/joystick/Config.in +++ b/drivers/input/joystick/Config.in @@ -10,6 +10,7 @@ dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_JOYSTICK_ADI dep_tristate ' Creative Labs Blaster Cobra gamepad' CONFIG_JOYSTICK_COBRA $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' Genius Flight2000 Digital joysticks and gamepads' CONFIG_JOYSTICK_GF2K $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOYSTICK_GRIP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT +dep_tristate ' Guillemot joysticks and gamepads' CONFIG_JOYSTICK_GUILLEMOT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' InterAct digital joysticks and gamepads' CONFIG_JOYSTICK_INTERACT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' Microsoft SideWinder digital joysticks and gamepads' CONFIG_JOYSTICK_SIDEWINDER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOYSTICK_TMDC $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT @@ -21,6 +22,7 @@ dep_tristate ' LogiCad3d Magellan/SpaceMouse 6dof controllers' CONFIG_JOYSTICK_ dep_tristate ' SpaceTec SpaceOrb/Avenger 6dof controllers' CONFIG_JOYSTICK_SPACEORB $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO dep_tristate ' SpaceTec SpaceBall 6dof controllers' CONFIG_JOYSTICK_SPACEBALL $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO dep_tristate ' Gravis Stinger gamepad' CONFIG_JOYSTICK_STINGER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO +dep_tristate ' Twiddler as a joystick' CONFIG_JOYSTICK_TWIDDLER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO dep_tristate ' Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_JOYSTICK_DB9 $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_JOYSTICK_GAMECON $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT @@ -29,3 +31,5 @@ dep_tristate ' Multisystem joysticks via TurboGraFX device' CONFIG_JOYSTICK_TUR if [ "$CONFIG_AMIGA" = "y" ]; then dep_tristate ' Amiga joysticks' CONFIG_JOYSTICK_AMIJOY $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK fi + +dep_tristate ' Gameport data dumper' CONFIG_INPUT_JOYDUMP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index c70244633703..16ab6110e13c 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -31,8 +31,9 @@ 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_IFORCE) += iforce.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 @@ -40,8 +41,11 @@ 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/ + # The global Rules.make. include $(TOPDIR)/Rules.make diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c index 264fe6fc4924..66ed7834a213 100644 --- a/drivers/input/joystick/adi.c +++ b/drivers/input/joystick/adi.c @@ -426,10 +426,10 @@ static void adi_init_input(struct adi *adi, struct adi_port *port, int half) 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); + set_bit(adi->abs[i], adi->dev.absbit); for (i = 0; i < adi->buttons; i++) - set_bit(adi->key[i], &adi->dev.keybit); + set_bit(adi->key[i], adi->dev.keybit); } static void adi_init_center(struct adi *adi) diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 0ce114353b8e..24d75e44d904 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -1,5 +1,5 @@ /* - * $Id: db9.c,v 1.12 2002/01/22 20:27:05 vojtech Exp $ + * $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * @@ -199,7 +199,7 @@ static void db9_timer(unsigned long private) 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_START, ~data & DB9_FIRE2); parport_write_control(port, DB9_NOSELECT); /* 2 */ udelay(DB9_GENESIS6_DELAY); @@ -209,10 +209,10 @@ static void db9_timer(unsigned long private) udelay(DB9_GENESIS6_DELAY); data=parport_read_data(port); - input_report_key(dev, BTN_Y, ~data & DB9_LEFT); - input_report_key(dev, BTN_Z, ~data & DB9_DOWN); - input_report_key(dev, BTN_MODE, ~data & DB9_UP); - input_report_key(dev, BTN_START, ~data & DB9_RIGHT); + 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); diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 77a1036e32d4..23ebebe3adb4 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -1,5 +1,5 @@ /* - * $Id: gamecon.c,v 1.21 2002/01/22 20:27:27 vojtech Exp $ + * $Id: gamecon.c,v 1.22 2002/07/01 15:42:25 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * @@ -398,8 +398,8 @@ static void gc_timer(unsigned long private) case GC_PSX_RUMBLE: - input_report_key(dev + i, BTN_THUMB, ~data[0] & 0x04); - input_report_key(dev + i, BTN_THUMB2, ~data[0] & 0x02); + input_report_key(dev + i, BTN_THUMBL, ~data[0] & 0x04); + input_report_key(dev + i, BTN_THUMBR, ~data[0] & 0x02); case GC_PSX_NEGCON: case GC_PSX_ANALOG: diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c new file mode 100644 index 000000000000..53c46728a2bf --- /dev/null +++ b/drivers/input/joystick/guillemot.c @@ -0,0 +1,285 @@ +/* + * $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); + + __save_flags(flags); + __cli(); + 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; + } + } + + __restore_flags(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); + } + + 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.idbus = BUS_GAMEPORT; + guillemot->dev.idvendor = GAMEPORT_ID_VENDOR_GUILLEMOT; + guillemot->dev.idproduct = guillemot_type[i].id; + guillemot->dev.idversion = (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); diff --git a/drivers/input/joystick/iforce.c b/drivers/input/joystick/iforce.c deleted file mode 100644 index 172c5d99a954..000000000000 --- a/drivers/input/joystick/iforce.c +++ /dev/null @@ -1,1224 +0,0 @@ -/* - * $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $ - * - * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com> - * - * USB/RS232 I-Force joysticks and wheels. - * - * Sponsored by SuSE - */ - -/* - * 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@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, 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> - -/* FF: 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> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>, Johann Deneux <deneux@ifrance.com>"); -MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); -MODULE_LICENSE("GPL"); - -#define IFORCE_MAX_LENGTH 16 - -#if defined(CONFIG_JOYSTICK_IFORCE_232) || defined(CONFIG_JOYSTICK_IFORCE_232_MODULE) -#define IFORCE_232 1 -#endif -#if defined(CONFIG_JOYSTICK_IFORCE_USB) || defined(CONFIG_JOYSTICK_IFORCE_USB_MODULE) -#define IFORCE_USB 2 -#endif - -#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 -#define FF_MODCORE_MAX 3 - -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)]; -}; - -#define FF_CMD_EFFECT 0x010e -#define FF_CMD_SHAPE 0x0208 -#define FF_CMD_MAGNITUDE 0x0303 -#define FF_CMD_PERIOD 0x0407 -#define FF_CMD_INTERACT 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 - -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, BTN_DEAD, -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 abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -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_FRICTION, - FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, FF_AUTOCENTER, -1 }; - -static struct iforce_device { - u16 idvendor; - u16 idproduct; - char *name; - signed short *btn; - signed short *abs; - signed short *ff; -} iforce_device[] = { - { 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_joystick, abs_joystick, ff_iforce }, - { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce }, - { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, - { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } -}; - -struct iforce { - struct input_dev dev; /* Input device interface */ - struct iforce_device *type; - char name[64]; - int open; - int bus; - - unsigned char data[IFORCE_MAX_LENGTH]; - unsigned char edata[IFORCE_MAX_LENGTH]; - u16 ecmd; - u16 expect_packet; - -#ifdef IFORCE_232 - struct serio *serio; /* RS232 transfer */ - int idx, pkt, len, id; - unsigned char csum; -#endif -#ifdef IFORCE_USB - struct usb_device *usbdev; /* USB transfer */ - struct urb *irq, *out, *ctrl; - struct usb_ctrlrequest dr; -#endif - /* Force Feedback */ - wait_queue_head_t wait; - struct resource device_memory; - struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; -}; - -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}}; - -/* Get hi and low bytes of a 16-bits int */ -#define HI(a) ((unsigned char)((a) >> 8)) -#define LO(a) ((unsigned char)((a) & 0xff)) - -/* Encode a time value */ -#define TIME_SCALE(a) ((a) == 0xffff ? 0xffff : (a) * 1000 / 256) - -static void 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 - */ -static void send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) -{ - switch (iforce->bus) { - -#ifdef IFORCE_232 - case IFORCE_232: { - - int i; - unsigned char csum = 0x2b ^ HI(cmd) ^ LO(cmd); - - serio_write(iforce->serio, 0x2b); - serio_write(iforce->serio, HI(cmd)); - serio_write(iforce->serio, LO(cmd)); - - for (i = 0; i < LO(cmd); i++) { - serio_write(iforce->serio, data[i]); - csum = csum ^ data[i]; - } - - serio_write(iforce->serio, csum); - return; - } -#endif -#ifdef IFORCE_USB - case IFORCE_USB: { - - DECLARE_WAITQUEUE(wait, current); - int timeout = HZ; /* 1 second */ - - memcpy(iforce->out->transfer_buffer + 1, data, LO(cmd)); - ((char*)iforce->out->transfer_buffer)[0] = HI(cmd); - iforce->out->transfer_buffer_length = LO(cmd) + 2; - iforce->out->dev = iforce->usbdev; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&iforce->wait, &wait); - - if (usb_submit_urb(iforce->out, GFP_ATOMIC)) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - return; - } - - while (timeout && iforce->out->status == -EINPROGRESS) - timeout = schedule_timeout(timeout); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - - if (!timeout) - usb_unlink_urb(iforce->out); - - return; - } -#endif - } -} - -static void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) -{ - struct input_dev *dev = &iforce->dev; - int i; - -#ifdef IFORCE_232 - if (HI(iforce->expect_packet) == HI(cmd)) { - iforce->expect_packet = 0; - iforce->ecmd = cmd; - memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); - } -#endif - - if (!iforce->type) - return; - - switch (HI(cmd)) { - - case 0x01: /* joystick position data */ - case 0x03: /* wheel position data */ - - 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]); - } 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))); - - break; - - case 0x02: /* status report */ - - input_report_key(dev, BTN_DEAD, data[0] & 0x02); - break; - } -} - -static int get_id_packet(struct iforce *iforce, char *packet) -{ - DECLARE_WAITQUEUE(wait, current); - int timeout = HZ; /* 1 second */ - - switch (iforce->bus) { - -#ifdef IFORCE_USB - case IFORCE_USB: - - iforce->dr.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; - } - - break; -#endif -#ifdef IFORCE_232 - case IFORCE_232: - - iforce->expect_packet = FF_CMD_QUERY; - 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; - } - - break; -#endif - } - - return -(iforce->edata[0] != packet[0]); -} - -static int iforce_open(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - - switch (iforce->bus) { -#ifdef IFORCE_USB - case IFORCE_USB: - if (iforce->open++) - break; - iforce->irq->dev = iforce->usbdev; - if (usb_submit_urb(iforce->irq, GFP_KERNEL)) - return -EIO; - break; -#endif - } - return 0; -} - -static void iforce_close(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - - switch (iforce->bus) { -#ifdef IFORCE_USB - case IFORCE_USB: - if (!--iforce->open) - usb_unlink_urb(iforce->irq); - break; -#endif - } -} - -/* - * Start or stop playing an effect - */ - -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]; - - printk(KERN_DEBUG "iforce.c: input_event(type = %d, code = %d, value = %d)\n", type, code, value); - - if (type != EV_FF) - return -1; - - switch (code) { - - case FF_GAIN: - - data[0] = value >> 9; - send_packet(iforce, FF_CMD_GAIN, data); - - return 0; - - case FF_AUTOCENTER: - - data[0] = 0x03; - data[1] = value >> 9; - send_packet(iforce, FF_CMD_AUTOCENTER, data); - - data[0] = 0x04; - data[1] = 0x01; - send_packet(iforce, FF_CMD_AUTOCENTER, data); - - return 0; - - default: /* Play an effect */ - - if (code >= iforce->dev.ff_effects_max) - return -1; - - data[0] = LO(code); - data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0; - data[2] = LO(value); - send_packet(iforce, FF_CMD_PLAY, data); - - return 0; - } - - return -1; -} - -/* - * 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, __s16 level) -{ - unsigned char data[3]; - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - data[2] = HI(level); - - send_packet(iforce, 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, - __s16 magnitude, __s16 offset, u16 period, u16 phase) -{ - unsigned char data[7]; - - period = TIME_SCALE(period); - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - - data[2] = HI(magnitude); - data[3] = HI(offset); - data[4] = HI(phase); - - data[5] = LO(period); - data[6] = HI(period); - - send_packet(iforce, FF_CMD_PERIOD, data); - - return 0; -} - -/* - * Uploads the part of an effect setting the shape of the force - */ - -static int make_shape_modifier(struct iforce* iforce, struct resource* mod_chunk, - 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 (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - 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); - - send_packet(iforce, FF_CMD_SHAPE, data); - - return 0; -} - -/* - * Component of spring, friction, inertia... effects - */ - -static int make_interactive_modifier(struct iforce* iforce, - struct resource* mod_chunk, - __s16 rsat, __s16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center) -{ - unsigned char data[10]; - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - - data[2] = HI(rk); - data[3] = HI(lk); - - data[4] = LO(center); - data[5] = HI(center); - - data[6] = LO(db); - data[7] = HI(db); - - data[8] = HI(rsat); - data[9] = HI(lsat); - - send_packet(iforce, FF_CMD_INTERACT, 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; -} - -/* - * 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); - - send_packet(iforce, FF_CMD_EFFECT, data); - - return 0; -} - -/* - * Upload a periodic effect to the device - */ - -static int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect) -{ - 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 err = 0; - - err = make_period_modifier(iforce, mod1_chunk, - effect->u.periodic.magnitude, effect->u.periodic.offset, - effect->u.periodic.period, effect->u.periodic.phase); - if (err) return err; - set_bit(FF_MOD1_IS_USED, core_effect->flags); - - err = make_shape_modifier(iforce, mod2_chunk, - effect->u.periodic.shape.attack_length, - effect->u.periodic.shape.attack_level, - effect->u.periodic.shape.fade_length, - effect->u.periodic.shape.fade_level); - if (err) return 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; - } - - 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); - - return err; -} - -/* - * Upload a constant force effect - */ -static int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect) -{ - 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 err = 0; - - printk(KERN_DEBUG "iforce.c: make constant effect\n"); - - err = make_magnitude_modifier(iforce, mod1_chunk, effect->u.constant.level); - if (err) return err; - set_bit(FF_MOD1_IS_USED, core_effect->flags); - - err = make_shape_modifier(iforce, mod2_chunk, - effect->u.constant.shape.attack_length, - effect->u.constant.shape.attack_level, - effect->u.constant.shape.fade_length, - effect->u.constant.shape.fade_level); - if (err) return err; - set_bit(FF_MOD2_IS_USED, core_effect->flags); - - 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); - - return err; -} - -/* - * Upload an interactive effect. Those are for example friction, inertia, springs... - */ -static int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect) -{ - int core_id = effect->id; - struct iforce_core_effect* core_effect = iforce->core_effects + core_id; - struct resource* mod_chunk = &(core_effect->mod1_chunk); - u8 type, axes; - u16 mod1, mod2, direction; - int err = 0; - - printk(KERN_DEBUG "iforce.c: make interactive effect\n"); - - switch (effect->type) { - case FF_SPRING: type = 0x40; break; - case FF_FRICTION: type = 0x41; break; - default: return -1; - } - - err = make_interactive_modifier(iforce, mod_chunk, - effect->u.interactive.right_saturation, - effect->u.interactive.left_saturation, - effect->u.interactive.right_coeff, - effect->u.interactive.left_coeff, - effect->u.interactive.deadband, - effect->u.interactive.center); - if (err) return err; - set_bit(FF_MOD1_IS_USED, core_effect->flags); - - switch ((test_bit(ABS_X, &effect->u.interactive.axis) || - test_bit(ABS_WHEEL, &effect->u.interactive.axis)) | - (!!test_bit(ABS_Y, &effect->u.interactive.axis) << 1)) { - - case 0: /* Only one axis, choose orientation */ - mod1 = mod_chunk->start; - mod2 = 0xffff; - direction = effect->direction; - axes = 0x20; - break; - - case 1: /* Only X axis */ - mod1 = mod_chunk->start; - mod2 = 0xffff; - direction = 0x5a00; - axes = 0x40; - break; - - case 2: /* Only Y axis */ - mod1 = 0xffff; - mod2 = mod_chunk->start; - direction = 0xb400; - axes = 0x80; - break; - - case 3: /* Both X and Y axes */ - /* TODO: same setting for both axes is not mandatory */ - mod1 = mod_chunk->start; - mod2 = mod_chunk->start; - direction = 0x6000; - axes = 0xc0; - break; - - default: - return -1; - } - - err = make_core(iforce, effect->id, - mod1, mod2, - type, axes, - effect->replay.length, effect->replay.delay, - effect->trigger.button, effect->trigger.interval, - direction); - - return err; -} - -/* - * 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; - - printk(KERN_DEBUG "iforce.c: upload effect\n"); - -/* - * Get a free id - */ - - for (id=0; id < FF_EFFECTS_MAX; ++id) - if (!test_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; - set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags); - -/* - * Upload the effect - */ - - switch (effect->type) { - - case FF_PERIODIC: - return iforce_upload_periodic(iforce, effect); - - case FF_CONSTANT: - return iforce_upload_constant(iforce, effect); - - case FF_SPRING: - case FF_FRICTION: - return iforce_upload_interactive(iforce, effect); - - default: - return -1; - } -} - -/* - * 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; - - printk(KERN_DEBUG "iforce.c: erase effect %d\n", effect_id); - - 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_init_device(struct iforce *iforce) -{ - unsigned char c[] = "CEOV"; - int i; - - init_waitqueue_head(&iforce->wait); - iforce->dev.ff_effects_max = 10; - -/* - * Input device fields. - */ - - iforce->dev.idbus = BUS_USB; - iforce->dev.private = iforce; - iforce->dev.name = iforce->name; - iforce->dev.open = iforce_open; - iforce->dev.close = iforce_close; - 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 (!get_id_packet(iforce, "O")) - break; - - if (i == 20) { /* 5 seconds */ - printk(KERN_ERR "iforce.c: Timeout waiting for response from device.\n"); - iforce_close(&iforce->dev); - return -1; - } - -/* - * Get device info. - */ - - if (!get_id_packet(iforce, "M")) - iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1]; - if (!get_id_packet(iforce, "P")) - iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1]; - if (!get_id_packet(iforce, "B")) - iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; - if (!get_id_packet(iforce, "N")) - iforce->dev.ff_effects_max = iforce->edata[1]; - -/* - * Display additional info. - */ - - for (i = 0; c[i]; i++) - if (!get_id_packet(iforce, c + i)) - dump_packet("info", iforce->ecmd, iforce->edata); - -/* - * Disable spring, enable force feedback. - * FIXME: We should use iforce_set_autocenter() et al here. - */ - - send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000"); - send_packet(iforce, FF_CMD_ENABLE, "\004"); - -/* - * Find appropriate device entry - */ - - for (i = 0; iforce_device[i].idvendor; i++) - if (iforce_device[i].idvendor == iforce->dev.idvendor && - iforce_device[i].idproduct == iforce->dev.idproduct) - break; - - iforce->type = iforce_device + i; - - sprintf(iforce->name, iforce->type->name, - iforce->dev.idproduct, iforce->dev.idvendor); - -/* - * Set input device bitfields and ranges. - */ - - iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF); - - for (i = 0; iforce->type->btn[i] >= 0; i++) { - signed short t = iforce->type->btn[i]; - set_bit(t, iforce->dev.keybit); - if (t != BTN_DEAD) - set_bit(FF_BTN(t), iforce->dev.ffbit); - } - - 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(FF_ABS(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_HAT0X: - case ABS_HAT0Y: - 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); - - return 0; -} - -#ifdef IFORCE_USB - -static void iforce_usb_irq(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - iforce_process_packet(iforce, - (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1); -} - -static void iforce_usb_out(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); -} - -static void iforce_usb_ctrl(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - iforce->ecmd = 0xff00 | urb->actual_length; - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); -} - -static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id) -{ - struct usb_endpoint_descriptor *epirq, *epout; - struct iforce *iforce; - - epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1; - - if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL; - memset(iforce, 0, sizeof(struct iforce)); - - iforce->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!iforce->irq) { - kfree(iforce); - return NULL; - } - iforce->out = usb_alloc_urb(0, GFP_KERNEL); - if (!iforce->out) { - usb_free_urb(iforce->irq); - kfree(iforce); - return NULL; - } - iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL); - if (!iforce->ctrl) { - usb_free_urb(iforce->out); - usb_free_urb(iforce->irq); - kfree(iforce); - return NULL; - } - - iforce->bus = IFORCE_USB; - iforce->usbdev = dev; - - iforce->dr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE; - iforce->dr.wIndex = 0; - iforce->dr.wLength = 16; - - FILL_INT_URB(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress), - iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval); - - FILL_BULK_URB(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress), - iforce + 1, 32, iforce_usb_out, iforce); - - FILL_CONTROL_URB(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0), - (void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce); - - if (iforce_init_device(iforce)) { - usb_free_urb(iforce->ctrl); - usb_free_urb(iforce->out); - usb_free_urb(iforce->irq); - kfree(iforce); - return NULL; - } - - printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on usb%d:%d.%d\n", - iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max, - iforce->device_memory.end, dev->bus->busnum, dev->devnum, ifnum); - - return iforce; -} - -static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) -{ - struct iforce *iforce = ptr; - usb_unlink_urb(iforce->irq); - input_unregister_device(&iforce->dev); - usb_free_urb(iforce->ctrl); - usb_free_urb(iforce->out); - usb_free_urb(iforce->irq); - kfree(iforce); -} - -static struct usb_device_id iforce_usb_ids [] = { - { 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(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, iforce_usb_ids); - -static struct usb_driver iforce_usb_driver = { - name: "iforce", - probe: iforce_usb_probe, - disconnect: iforce_usb_disconnect, - id_table: iforce_usb_ids, -}; - -#endif - -#ifdef IFORCE_232 - -static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) -{ - struct iforce* iforce = serio->private; - - if (!iforce->pkt) { - if (data != 0x2b) { - return; - } - iforce->pkt = 1; - return; - } - - if (!iforce->id) { - if (data > 3 && data != 0xff) { - iforce->pkt = 0; - return; - } - iforce->id = data; - return; - } - - if (!iforce->len) { - if (data > IFORCE_MAX_LENGTH) { - iforce->pkt = 0; - iforce->id = 0; - return; - } - iforce->len = data; - return; - } - - if (iforce->idx < iforce->len) { - iforce->csum += iforce->data[iforce->idx++] = data; - return; - } - - if (iforce->idx == iforce->len) { - iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); - iforce->pkt = 0; - iforce->id = 0; - iforce->len = 0; - iforce->idx = 0; - iforce->csum = 0; - } -} - -static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) -{ - 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, dev)) { - kfree(iforce); - return; - } - - if (iforce_init_device(iforce)) { - serio_close(serio); - kfree(iforce); - return; - } - - printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on serio%d\n", - iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max, - iforce->device_memory.end, serio->number); -} - -static void iforce_serio_disconnect(struct serio *serio) -{ - struct iforce* iforce = serio->private; - - input_unregister_device(&iforce->dev); - serio_close(serio); - kfree(iforce); -} - -static struct serio_dev iforce_serio_dev = { - interrupt: iforce_serio_irq, - connect: iforce_serio_connect, - disconnect: iforce_serio_disconnect, -}; - -#endif - -static int __init iforce_init(void) -{ -#ifdef IFORCE_USB - usb_register(&iforce_usb_driver); -#endif -#ifdef IFORCE_232 - serio_register_device(&iforce_serio_dev); -#endif - return 0; -} - -static void __exit iforce_exit(void) -{ -#ifdef IFORCE_USB - usb_deregister(&iforce_usb_driver); -#endif -#ifdef IFORCE_232 - serio_unregister_device(&iforce_serio_dev); -#endif -} - -module_init(iforce_init); -module_exit(iforce_exit); diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile new file mode 100644 index 000000000000..4eb258e273cd --- /dev/null +++ b/drivers/input/joystick/iforce/Makefile @@ -0,0 +1,42 @@ +# +# Makefile for the I-Force driver +# + +# I-Force may need both USB and RS-232 + +CONFIG_JOYSTICK_IFORCE := n + +ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y) + ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y) + CONFIG_JOYSTICK_IFORCE := y + endif +endif + +ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m) + CONFIG_JOYSTICK_IFORCE := m +endif + +ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m) + CONFIG_JOYSTICK_IFORCE := m +endif + +obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o + +# The global Rules.make. + +include $(TOPDIR)/Rules.make + +# iforce.o is a multipart module. + +IFORCEOBJS = iforce-ff.o iforce-main.o iforce-packets.o + +ifneq ($(CONFIG_JOYSTICK_IFORCE_232),) + IFORCEOBJS += iforce-serio.o +endif + +ifneq ($(CONFIG_JOYSTICK_IFORCE_USB),) + IFORCEOBJS += iforce-usb.o +endif + +iforce.o: $(IFORCEOBJS) + $(LD) -i $(IFORCEOBJS) -o $@ diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c new file mode 100644 index 000000000000..267f92f0aa2f --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -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; +} diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c new file mode 100644 index 000000000000..45b7c001e066 --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -0,0 +1,543 @@ +/* + * $Id: iforce-main.c,v 1.18 2002/06/09 11:03:03 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 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 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 IFORCE_USB + case IFORCE_USB: + iforce_usb_delete(iforce); + break; +#endif +#ifdef 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.idbus = 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.idvendor = (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.idproduct = (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.idvendor && + iforce_device[i].idproduct == iforce->dev.idproduct) + 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 IFORCE_USB + usb_register(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_register_device(&iforce_serio_dev); +#endif + return 0; +} + +static void __exit iforce_exit(void) +{ +#ifdef IFORCE_USB + usb_deregister(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_unregister_device(&iforce_serio_dev); +#endif +} + +module_init(iforce_init); +module_exit(iforce_exit); diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c new file mode 100644 index 000000000000..922894bb13ea --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -0,0 +1,314 @@ +/* + * $Id: iforce-packets.c,v 1.15 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" + +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 IFORCE_232 + case IFORCE_232: + if (empty) + iforce_serial_xmit(iforce); + break; +#endif +#ifdef 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 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 IFORCE_232 + if (HI(iforce->expect_packet) == HI(cmd)) { + iforce->expect_packet = 0; + iforce->ecmd = cmd; + memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); + if (waitqueue_active(&iforce->wait)) + wake_up(&iforce->wait); + } +#endif + + if (!iforce->type) { + being_used--; + return; + } + + switch (HI(cmd)) { + + case 0x01: /* joystick position data */ + case 0x03: /* wheel position data */ + + 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); + } + } + + break; + + case 0x02: /* status report */ + input_report_key(dev, BTN_DEAD, data[0] & 0x02); + + /* 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 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_KERNEL)) { + 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 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]); +} + diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c new file mode 100644 index 000000000000..31e21fb431f6 --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -0,0 +1,166 @@ +/* + * $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 void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct iforce* iforce = serio->private; + + if (!iforce->pkt) { + if (data != 0x2b) { + return; + } + iforce->pkt = 1; + return; + } + + if (!iforce->id) { + if (data > 3 && data != 0xff) { + iforce->pkt = 0; + return; + } + iforce->id = data; + return; + } + + if (!iforce->len) { + if (data > IFORCE_MAX_LENGTH) { + iforce->pkt = 0; + iforce->id = 0; + return; + } + iforce->len = data; + return; + } + + if (iforce->idx < iforce->len) { + iforce->csum += iforce->data[iforce->idx++] = data; + return; + } + + if (iforce->idx == iforce->len) { + iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); + iforce->pkt = 0; + iforce->id = 0; + iforce->len = 0; + iforce->idx = 0; + iforce->csum = 0; + } +} + +static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) +{ + 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, dev)) { + 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_dev iforce_serio_dev = { + write_wakeup: iforce_serio_write_wakeup, + interrupt: iforce_serio_irq, + connect: iforce_serio_connect, + disconnect: iforce_serio_disconnect, +}; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c new file mode 100644 index 000000000000..760534a89f72 --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -0,0 +1,214 @@ + /* + * $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 iforce *iforce = urb->context; + if (urb->status) return; + iforce_process_packet(iforce, + (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1); +} + +static void iforce_usb_out(struct urb *urb) +{ + 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); + + if (waitqueue_active(&iforce->wait)) + wake_up(&iforce->wait); +} + +static void iforce_usb_ctrl(struct urb *urb) +{ + struct iforce *iforce = urb->context; + if (urb->status) return; + iforce->ecmd = 0xff00 | urb->actual_length; + if (waitqueue_active(&iforce->wait)) + wake_up(&iforce->wait); +} + +static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *epirq, *epout; + struct iforce *iforce; + + epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1; + + 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; + + return iforce; + +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 NULL; +} + +/* 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_device *dev, void *ptr) +{ + struct iforce *iforce = ptr; + int open = iforce->dev.handle->open; + + 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, +}; diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h new file mode 100644 index 000000000000..fec1b258225c --- /dev/null +++ b/drivers/input/joystick/iforce/iforce.h @@ -0,0 +1,194 @@ +/* + * $Id: iforce.h,v 1.12 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 <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> + +/* FF: 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 + +#if defined(CONFIG_JOYSTICK_IFORCE_232) +#define IFORCE_232 1 +#endif +#if defined(CONFIG_JOYSTICK_IFORCE_USB) +#define IFORCE_USB 2 +#endif + +#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 IFORCE_232 + struct serio *serio; /* RS232 transfer */ + int idx, pkt, len, id; + unsigned char csum; +#endif +#ifdef 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); +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_dev iforce_serio_dev; +extern struct usb_driver iforce_usb_driver; diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c new file mode 100644 index 000000000000..d64493c0cc04 --- /dev/null +++ b/drivers/input/joystick/joydump.c @@ -0,0 +1,152 @@ +/* + * $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; + + save_flags(flags); + cli(); + + 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++; + } + + restore_flags(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); diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c index 3e6139a62979..2930248b443a 100644 --- a/drivers/input/joystick/magellan.c +++ b/drivers/input/joystick/magellan.c @@ -156,7 +156,7 @@ static void magellan_connect(struct serio *serio, struct serio_dev *dev) magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); for (i = 0; i < 9; i++) - set_bit(magellan_buttons[i], &magellan->dev.keybit); + set_bit(magellan_buttons[i], magellan->dev.keybit); for (i = 0; i < 6; i++) { t = magellan_axes[i]; diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c new file mode 100644 index 000000000000..70da80e89404 --- /dev/null +++ b/drivers/input/joystick/twidjoy.c @@ -0,0 +1,264 @@ +/* + * $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> + +/* + * 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) +{ + 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); + + 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); + } + + 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 void twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + 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; /* 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); + twidjoy->idx = 0; + } + + return; +} + +/* + * 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_dev *dev) +{ + 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); + + twidjoy->dev.name = twidjoy_name; + twidjoy->dev.phys = twidjoy->phys; + twidjoy->dev.idbus = BUS_RS232; + twidjoy->dev.idvendor = SERIO_TWIDJOY; + twidjoy->dev.idproduct = 0x0001; + twidjoy->dev.idversion = 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, dev)) { + 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_dev twidjoy_dev = { + 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_device(&twidjoy_dev); + return 0; +} + +void __exit twidjoy_exit(void) +{ + serio_unregister_device(&twidjoy_dev); +} + +module_init(twidjoy_init); +module_exit(twidjoy_exit); diff --git a/drivers/input/keybdev.c b/drivers/input/keybdev.c index 6731dd2542c2..8a5a93b57f7a 100644 --- a/drivers/input/keybdev.c +++ b/drivers/input/keybdev.c @@ -1,5 +1,5 @@ /* - * $Id: keybdev.c,v 1.16 2002/01/09 04:21:41 lethal Exp $ + * $Id: keybdev.c,v 1.19 2002/03/13 10:09:20 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * @@ -179,7 +179,7 @@ void panic_blink(void) static unsigned long last_jiffie; static char led; /* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is different. */ - if (jiffies - last_jiffie > HZ/2) { + if (time_after(jiffies, last_jiffie + HZ/2)) { led ^= 0x01 | 0x04; keybdev_ledfunc(led); last_jiffie = jiffies; diff --git a/drivers/input/keyboard/Config.help b/drivers/input/keyboard/Config.help new file mode 100644 index 000000000000..3d7df5a61a23 --- /dev/null +++ b/drivers/input/keyboard/Config.help @@ -0,0 +1,66 @@ +CONFIG_INPUT_KEYBOARD + 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 + Say Y here if you want to use the standard AT keyboard. Usually + you'll need this, unless you have a different type keyboard (USB, + ADB or other). + + If unsure, say Y. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called atkbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_KEYBOARD_SUNKBD + 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. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called sunkbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_KEYBOARD_PS2SERKBD + Say Y here if you want to use a PS/2 to Serial converter with a + keyboard attached to it. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called ps2serkbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_KEYBOARD_XTKBD + 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. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xtkbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_KEYBOARD_MAPLE + Say Y here if you have a DreamCast console running Linux and have + a keyboard attached to its Maple bus. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called maple_keyb.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_KEYBOARD_AMIGA + Say Y here if you are running Linux on any AMIGA and have a keyboard + attached. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called amikbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + diff --git a/drivers/input/keyboard/Config.in b/drivers/input/keyboard/Config.in new file mode 100644 index 000000000000..f7f7128ee405 --- /dev/null +++ b/drivers/input/keyboard/Config.in @@ -0,0 +1,18 @@ +# +# Input core configuration +# + +bool 'Keyboards' CONFIG_INPUT_KEYBOARD + +dep_tristate ' AT keyboard support' CONFIG_KEYBOARD_ATKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO +dep_tristate ' Sun Type 4 and Type 5 keyboard support' CONFIG_KEYBOARD_SUNKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO +dep_tristate ' PS/2 to Serial converter support' CONFIG_KEYBOARD_PS2SERKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO +dep_tristate ' XT Keyboard support' CONFIG_KEYBOARD_XTKBD $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_SERIO + +if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then + dep_tristate ' Maple bus keyboard support' CONFIG_KEYBOARD_MAPLE $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD $CONFIG_MAPLE +fi + +if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga keyboard' CONFIG_KEYBOARD_AMIGA $CONFIG_INPUT $CONFIG_INPUT_KEYBOARD +fi diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile new file mode 100644 index 000000000000..d51417534b60 --- /dev/null +++ b/drivers/input/keyboard/Makefile @@ -0,0 +1,16 @@ +# +# 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_PS2SERKBD) += ps2serkbd.o +obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o +obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o + +# The global Rules.make. + +include $(TOPDIR)/Rules.make diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c new file mode 100644 index 000000000000..330b33615165 --- /dev/null +++ b/drivers/input/keyboard/amikbd.c @@ -0,0 +1,144 @@ +/* + * $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 <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] = { + 41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 43, 0, 82, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0, 79, 80, 81, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 75, 76, 77, + 0, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 0, 83, 71, 72, 73, + 57, 14, 15, 96, 28, 1,111, 0, 0, 0, 74, 0,103,108,106,105, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 98, 55, 78, 87, + 42, 54, 58, 29, 56,100 +} + +static char *amikbd_messages[] = { + KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n", + KERN_WARNING "amikbd: keyboard lost sync\n", + KERN_WARNING "amikbd: keyboard buffer overflow\n", + KERN_WARNING "amikbd: keyboard controller failure\n", + KERN_ERR "amikbd: keyboard selftest failure\n", + KERN_INFO "amikbd: initiate power-up key stream\n", + KERN_INFO "amikbd: terminate power-up key stream\n", + 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 void 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 = scancode >> 1; + + if (scancode < 0x78) { /* scancodes < 0x78 are keys */ + + scancode = amikbd_keycode[scancode]; + + if (scancode == KEY_CAPS) { /* CapsLock is a toggle switch key on Amiga */ + input_report_key(&amikbd_dev, scancode, 1); + input_report_key(&amikbd_dev, scancode, 0); + return; + } + + input_report_key(&amikbd_dev, scancode, down); + + return; + } + + printk(amikbd_messages[scancode - 0x78]); /* scancodes >= 0x78 are error codes */ +} + +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; + + amikbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + amikbd_dev.keycode = 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", NULL); + + amikbd_dev.name = amikbd_name; + amikbd_dev.phys = amikbd_phys; + amikbd_dev.idbus = BUS_AMIGA; + amikbd_dev.idvendor = 0x0001; + amikbd_dev.idproduct = 0x0001; + amikbd_dev.idversion = 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); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c new file mode 100644 index 000000000000..e14f581a4d67 --- /dev/null +++ b/drivers/input/keyboard/atkbd.c @@ -0,0 +1,565 @@ +/* + * $Id: atkbd.c,v 1.33 2002/02/12 09:34:34 vojtech Exp $ + * + * Copyright (c) 1999-2001 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 + * + * 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/init.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/tqueue.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("AT and PS/2 keyboard driver"); +MODULE_PARM(atkbd_set, "1i"); +MODULE_LICENSE("GPL"); + +static int atkbd_set = 2; + +/* + * Scancode to keycode tables. These are just the default setting, and + * are loadable via an userland utility. + */ + +static unsigned char atkbd_set2_keycode[512] = { + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41, 85, + 0, 56, 42, 0, 29, 16, 2, 89, 0, 0, 44, 31, 30, 17, 3, 90, + 0, 46, 45, 32, 18, 5, 4, 91, 0, 57, 47, 33, 20, 19, 6, 0, + 0, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 122, 89, 40,120, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 0, + 85, 86, 90, 91, 92, 93, 14, 94, 95, 79, 0, 75, 71,121, 0,123, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + 252, 0, 0, 65, 99, 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,251, 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, + 252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, + 0, 0, 92, 90, 85, 0,137, 0, 0, 0, 0, 91, 89,144,115, 0, + 136,100,255, 0, 97,149,164, 0,156, 0, 0,140,115, 0, 0,125, + 0,150, 0,154,152,163,151,126,112,166, 0,140, 0,147, 0,127, + 159,167,139,160,163, 0, 0,116,158, 0,150,165, 0, 0, 0,142, + 157, 0,114,166,168, 0, 0, 0,155, 0, 98,113, 0,148, 0,138, + 0, 0, 0, 0, 0, 0,153,140, 0,255, 96, 0, 0, 0,143, 0, + 133, 0,116, 0,143, 0,174,133, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119 +}; + +static unsigned char atkbd_set3_keycode[512] = { + 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, + 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, + 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64, + 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66, + 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68, + 113,114, 40, 84, 26, 13, 87, 99,100, 54, 28, 27, 43, 84, 88, 70, + 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104, + 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55, 85, + 89, 90, 91, 92, 74, 0, 0, 0, 0, 0, 0,125,126,127,112, 0, + 0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168, + 148,149,147,140, 0, 0, 0, 0, 0, 0,251, 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, + 252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255 +}; + +#define ATKBD_CMD_SETLEDS 0x10ed +#define ATKBD_CMD_GSCANSET 0x11f0 +#define ATKBD_CMD_SSCANSET 0x10f0 +#define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_ENABLE 0x00f4 +#define ATKBD_CMD_RESET_DIS 0x00f5 +#define ATKBD_CMD_SETALL_MB 0x00f8 +#define ATKBD_CMD_EX_ENABLE 0x10ea +#define ATKBD_CMD_EX_SETLEDS 0x20eb + +#define ATKBD_RET_ACK 0xfa +#define ATKBD_RET_NAK 0xfe + +#define ATKBD_KEY_UNKNOWN 0 +#define ATKBD_KEY_BAT 251 +#define ATKBD_KEY_EMUL0 252 +#define ATKBD_KEY_EMUL1 253 +#define ATKBD_KEY_RELEASE 254 +#define ATKBD_KEY_NULL 255 + +/* + * The atkbd control structure + */ + +struct atkbd { + unsigned char keycode[512]; + struct input_dev dev; + struct serio *serio; + char name[64]; + char phys[32]; + struct tq_struct tq; + unsigned char cmdbuf[4]; + unsigned char cmdcnt; + unsigned char set; + char release; + char ack; + char emul; + char error; + unsigned short id; +}; + +/* + * atkbd_interrupt(). Here takes place processing of data received from + * the keyboard into events. + */ + +static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct atkbd *atkbd = serio->private; + int code = data; + +#ifdef ATKBD_DEBUG + printk(KERN_DEBUG "atkbd.c: Received %02x\n", data); +#endif + + switch (code) { + case ATKBD_RET_ACK: + atkbd->ack = 1; + return; + case ATKBD_RET_NAK: + atkbd->ack = -1; + return; + } + + if (atkbd->cmdcnt) { + atkbd->cmdbuf[--atkbd->cmdcnt] = code; + return; + } + + switch (atkbd->keycode[code]) { + case ATKBD_KEY_BAT: + queue_task(&atkbd->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + case ATKBD_KEY_EMUL0: + atkbd->emul = 1; + return; + case ATKBD_KEY_EMUL1: + atkbd->emul = 2; + return; + case ATKBD_KEY_RELEASE: + atkbd->release = 1; + return; + } + + if (atkbd->emul) { + if (--atkbd->emul) return; + code |= 0x100; + } + + switch (atkbd->keycode[code]) { + case ATKBD_KEY_NULL: + break; + case ATKBD_KEY_UNKNOWN: + printk(KERN_WARNING "atkbd.c: Unknown key (set %d, scancode %#x, on %s) %s.\n", + atkbd->set, code, serio->phys, atkbd->release ? "released" : "pressed"); + break; + default: + input_report_key(&atkbd->dev, atkbd->keycode[code], !atkbd->release); + } + + atkbd->release = 0; +} + +/* + * atkbd_sendbyte() sends a byte to the keyboard, and waits for + * acknowledge. It doesn't handle resends according to the keyboard + * protocol specs, because if these are needed, the keyboard needs + * replacement anyway, and they only make a mess in the protocol. + */ + +static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte) +{ + int timeout = 10000; /* 100 msec */ + atkbd->ack = 0; + +#ifdef ATKBD_DEBUG + printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte); +#endif + serio_write(atkbd->serio, byte); + while (!atkbd->ack && timeout--) udelay(10); + + return -(atkbd->ack <= 0); +} + +/* + * atkbd_command() sends a command, and its parameters to the keyboard, + * then waits for the response and puts it in the param array. + */ + +static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command) +{ + int timeout = 50000; /* 500 msec */ + int send = (command >> 12) & 0xf; + int receive = (command >> 8) & 0xf; + int i; + + atkbd->cmdcnt = receive; + + if (command & 0xff) + if (atkbd_sendbyte(atkbd, command & 0xff)) + return (atkbd->cmdcnt = 0) - 1; + + for (i = 0; i < send; i++) + if (atkbd_sendbyte(atkbd, param[i])) + return (atkbd->cmdcnt = 0) - 1; + + while (atkbd->cmdcnt && timeout--) udelay(10); + + for (i = 0; i < receive; i++) + param[i] = atkbd->cmdbuf[(receive - 1) - i]; + + if (atkbd->cmdcnt) + return (atkbd->cmdcnt = 0) - 1; + + return 0; +} + +/* + * Event callback from the input module. Events that change the state of + * the hardware are processed here. + */ + +static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct atkbd *atkbd = dev->private; + char param[2]; + + if (!atkbd->serio->write) + return -1; + + switch (type) { + + case EV_LED: + + *param = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) + | (test_bit(LED_NUML, dev->led) ? 2 : 0) + | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); + atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS); + + if (atkbd->set == 4) { + param[0] = 0; + param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) + | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) + | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) + | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); + atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS); + } + + return 0; + } + + return -1; +} + +/* + * atkbd_set_3 checks if a keyboard has a working Set 3 support, and + * sets it into that. Unfortunately there are keyboards that can be switched + * to Set 3, but don't work well in that (BTC Multimedia ...) + */ + +static int atkbd_set_3(struct atkbd *atkbd) +{ + unsigned char param; + +/* + * For known special keyboards we can go ahead and set the correct set. + */ + + if (atkbd->id == 0xaca1) { + param = 3; + atkbd_command(atkbd, ¶m, ATKBD_CMD_SSCANSET); + return 3; + } + +/* + * We check for the extra keys on an some keyboards that need extra + * command to get enabled. This shouldn't harm any keyboards not + * knowing the command. + */ + + param = 0x71; + if (!atkbd_command(atkbd, ¶m, ATKBD_CMD_EX_ENABLE)) + return 4; + +/* + * Try to set the set we want. + */ + + param = atkbd_set; + if (atkbd_command(atkbd, ¶m, ATKBD_CMD_SSCANSET)) + return 2; + +/* + * Read set number. Beware here. Some keyboards always send '2' + * or some other number regardless into what mode they have been + * attempted to be set. Other keyboards treat the '0' command as + * 'set to set 0', and not 'report current set' as they should. + * In that case we time out, and return 2. + */ + + param = 0; + if (atkbd_command(atkbd, ¶m, ATKBD_CMD_GSCANSET)) + return 2; + +/* + * Here we return the set number the keyboard reports about + * itself. + */ + + return (param == 3) ? 3 : 2; +} + +/* + * atkbd_probe() probes for an AT keyboard on a serio port. + */ + +static int atkbd_probe(struct atkbd *atkbd) +{ + unsigned char param[2]; + +/* + * Full reset with selftest can on some keyboards be annoyingly slow, + * so we just do a reset-and-disable on the keyboard, which + * is considerably faster, but doesn't have to reset everything. + */ + + if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_DIS)) + return -1; + +/* + * Next, we check if it's a keyboard. It should send 0xab83 + * (0xab84 on IBM ThinkPad, and 0xaca1 on a NCD Sun layout keyboard, + * 0xab02 on unxlated i8042 and 0xab03 on unxlated ThinkPad, 0xab7f + * on Fujitsu Lifebook). + * If it's a mouse, it'll only send 0x00 (0x03 if it's MS mouse), + * and we'll time out here, and report an error. + */ + + param[0] = param[1] = 0; + + if (atkbd_command(atkbd, param, ATKBD_CMD_GETID)) + return -1; + + atkbd->id = (param[0] << 8) | param[1]; + + if (atkbd->id != 0xab83 && atkbd->id != 0xab84 && atkbd->id != 0xaca1 && + atkbd->id != 0xab7f && atkbd->id != 0xab02 && atkbd->id != 0xab03) + printk(KERN_WARNING "atkbd.c: Unusual keyboard ID: %#x on %s\n", + atkbd->id, atkbd->serio->phys); + + return 0; +} + +/* + * atkbd_initialize() sets the keyboard into a sane state. + */ + +static void atkbd_initialize(struct atkbd *atkbd) +{ + unsigned char param; + +/* + * Disable autorepeat. We don't need it, as we do it in software anyway, + * because that way can get faster repeat, and have less system load + * (less accesses to the slow ISA hardware). If this fails, we don't care, + * and will just ignore the repeated keys. + */ + + atkbd_command(atkbd, NULL, ATKBD_CMD_SETALL_MB); + +/* + * We also shut off all the leds. The console code will turn them back on, + * if needed. + */ + + param = 0; + atkbd_command(atkbd, ¶m, ATKBD_CMD_SETLEDS); + +/* + * Last, we enable the keyboard so that we get keypresses from it. + */ + + if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE)) + printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n", + atkbd->serio->phys); +} + +/* + * atkbd_disconnect() cleans up behind us ... + */ + +static void atkbd_disconnect(struct serio *serio) +{ + struct atkbd *atkbd = serio->private; + input_unregister_device(&atkbd->dev); + serio_close(serio); + kfree(atkbd); +} + +/* + * atkbd_powerup() is called when the keyboard sends the 0xaa character, + * meaning that it was disconnected and reconnected. We close the port + * in that case and let the upper layer find an appropriate driver for + * the device that was connected. It may be a mouse, or a keyboard, we + * don't know yet. + */ + +static void atkbd_powerup(void *data) +{ + struct atkbd *atkbd = data; + mdelay(40); /* FIXME!!! Wait some nicer way */ + serio_rescan(atkbd->serio); +} + +/* + * atkbd_connect() is called when the serio module finds and interface + * that isn't handled yet by an appropriate device driver. We check if + * there is an AT keyboard out there and if yes, we register ourselves + * to the input module. + */ + +static void atkbd_connect(struct serio *serio, struct serio_dev *dev) +{ + struct atkbd *atkbd; + int i; + + if ((serio->type & SERIO_TYPE) != SERIO_8042) + return; + + if (!(atkbd = kmalloc(sizeof(struct atkbd), GFP_KERNEL))) + return; + + memset(atkbd, 0, sizeof(struct atkbd)); + + if (serio->write) { + atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); + atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); + } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + + atkbd->serio = serio; + + atkbd->dev.keycode = atkbd->keycode; + atkbd->dev.event = atkbd_event; + atkbd->dev.private = atkbd; + + atkbd->tq.routine = atkbd_powerup; + atkbd->tq.data = atkbd; + + serio->private = atkbd; + + if (serio_open(serio, dev)) { + kfree(atkbd); + return; + } + + if (serio->write) { + + if (atkbd_probe(atkbd)) { + serio_close(serio); + kfree(atkbd); + return; + } + + atkbd->set = atkbd_set_3(atkbd); + + } else { + atkbd->set = 2; + atkbd->id = 0xab00; + } + + if (atkbd->set == 4) { + atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE); + sprintf(atkbd->name, "AT Set 2 Extended keyboard\n"); + } else + sprintf(atkbd->name, "AT Set %d keyboard", atkbd->set); + + sprintf(atkbd->phys, "%s/input0", serio->phys); + + if (atkbd->set == 3) + memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode)); + else + memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); + + atkbd->dev.name = atkbd->name; + atkbd->dev.phys = atkbd->phys; + atkbd->dev.idbus = BUS_I8042; + atkbd->dev.idvendor = 0x0001; + atkbd->dev.idproduct = atkbd->set; + atkbd->dev.idversion = atkbd->id; + + for (i = 0; i < 512; i++) + if (atkbd->keycode[i] && atkbd->keycode[i] <= 250) + set_bit(atkbd->keycode[i], atkbd->dev.keybit); + + input_register_device(&atkbd->dev); + + printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys); + + if (serio->write) + atkbd_initialize(atkbd); +} + + +static struct serio_dev atkbd_dev = { + interrupt: atkbd_interrupt, + connect: atkbd_connect, + disconnect: atkbd_disconnect +}; + +/* + * Module init and exit. + */ + +void __init atkbd_setup(char *str, int *ints) +{ + if (!ints[0]) atkbd_set = ints[1]; +} + +int __init atkbd_init(void) +{ + serio_register_device(&atkbd_dev); + return 0; +} + +void __exit atkbd_exit(void) +{ + serio_unregister_device(&atkbd_dev); +} + +module_init(atkbd_init); +module_exit(atkbd_exit); diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c new file mode 100644 index 000000000000..feaea91dc8b4 --- /dev/null +++ b/drivers/input/keyboard/maple_keyb.c @@ -0,0 +1,190 @@ +/* + * $Id: maple_keyb.c,v 1.1 2001/11/02 17:27:32 jsimmons 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"); + +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, 84, 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, 85, 89, 90, 91, 92, 93, 94, 95, + 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114, 0, 0, 0,124, 0,181,182,183,184,185,186,187,188,189, + 190,191,192,193,194,195,196,197,198, 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]); + } + } + + 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); + + 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.idbus = BUS_MAPLE; + + input_register_device(&kbd->dev); + + maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD); + + printk(KERN_INFO "input%d: keyboard(0x%lx): %s\n", + kbd->dev.number, data, kbd->dev.name); + + MOD_INC_USE_COUNT; + + 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); + + MOD_DEC_USE_COUNT; +} + + +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: + */ diff --git a/drivers/input/keyboard/ps2serkbd.c b/drivers/input/keyboard/ps2serkbd.c new file mode 100644 index 000000000000..afa4c107ff4e --- /dev/null +++ b/drivers/input/keyboard/ps2serkbd.c @@ -0,0 +1,298 @@ +/* + * based on: sunkbd.c and ps2serkbd.c + * + * $Id: ps2serkbd.c,v 1.5 2001/09/25 10:12:07 vojtech Exp $ + */ + +/* + * PS/2 keyboard via adapter at serial 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 + */ + +#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/tqueue.h> + +#define ATKBD_CMD_SETLEDS 0x10ed +#define ATKBD_CMD_GSCANSET 0x11f0 +#define ATKBD_CMD_SSCANSET 0x10f0 +#define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_ENABLE 0x00f4 +#define ATKBD_CMD_RESET_DIS 0x00f5 +#define ATKBD_CMD_SETALL_MB 0x00f8 +#define ATKBD_CMD_EX_ENABLE 0x10ea +#define ATKBD_CMD_EX_SETLEDS 0x20eb + +#define ATKBD_RET_ACK 0xfa +#define ATKBD_RET_NAK 0xfe + +#define ATKBD_KEY_UNKNOWN 0 +#define ATKBD_KEY_BAT 251 +#define ATKBD_KEY_EMUL0 252 +#define ATKBD_KEY_EMUL1 253 +#define ATKBD_KEY_RELEASE 254 +#define ATKBD_KEY_NULL 255 + +static unsigned char ps2serkbd_set2_keycode[512] = { + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41, 85, + 0, 56, 42, 0, 29, 16, 2, 89, 0, 0, 44, 31, 30, 17, 3, 90, + 0, 46, 45, 32, 18, 5, 4, 91, 0, 57, 47, 33, 20, 19, 6, 0, + 0, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 122, 89, 40,120, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 0, + 85, 86, 90, 91, 92, 93, 14, 94, 95, 79, 0, 75, 71,121, 0,123, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + 252, 0, 0, 65, 99, 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,251, 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, + 252,253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, + 0, 0, 92, 90, 85, 0,137, 0, 0, 0, 0, 91, 89,144,115, 0, + 136,100,255, 0, 97,149,164, 0,156, 0, 0,140,115, 0, 0,125, + 0,150, 0,154,152,163,151,126,112,166, 0,140, 0,147, 0,127, + 159,167,139,160,163, 0, 0,116,158, 0,150,165, 0, 0, 0,142, + 157, 0,114,166,168, 0, 0, 0,155, 0, 98,113, 0,148, 0,138, + 0, 0, 0, 0, 0, 0,153,140, 0, 0, 96, 0, 0, 0,143, 0, + 133, 0,116, 0,143, 0,174,133, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119 +}; + +/* + * Per-keyboard data. + */ + +struct ps2serkbd { + unsigned char keycode[512]; + struct input_dev dev; + struct serio *serio; + char name[64]; + char phys[32]; + struct tq_struct tq; + unsigned char cmdbuf[4]; + unsigned char cmdcnt; + unsigned char set; + char release; + char ack; + char emul; + char error; + unsigned short id; +}; + +/* + * ps2serkbd_interrupt() is called by the low level driver when a character + * is received. + */ + +static void ps2serkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + static int event_count=0; + struct ps2serkbd* ps2serkbd = serio->private; + int code=data; + +#if 0 + printk(KERN_WARNING "ps2serkbd.c(%8d): (scancode %#x)\n", event_count, data); +#endif + event_count++; + + switch (code) { + case ATKBD_RET_ACK: + ps2serkbd->ack = 1; + return; + case ATKBD_RET_NAK: + ps2serkbd->ack = -1; + return; + } + + if (ps2serkbd->cmdcnt) { + ps2serkbd->cmdbuf[--ps2serkbd->cmdcnt] = code; + return; + } + + switch (ps2serkbd->keycode[code]) { + case ATKBD_KEY_BAT: + queue_task(&ps2serkbd->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + case ATKBD_KEY_EMUL0: + ps2serkbd->emul = 1; + return; + case ATKBD_KEY_EMUL1: + ps2serkbd->emul = 2; + return; + case ATKBD_KEY_RELEASE: + ps2serkbd->release = 1; + return; + } + + if (ps2serkbd->emul) { + if (--ps2serkbd->emul) return; + code |= 0x100; + } + + switch (ps2serkbd->keycode[code]) { + case ATKBD_KEY_NULL: + break; + case ATKBD_KEY_UNKNOWN: + printk(KERN_WARNING "ps2serkbd.c: Unknown key (set %d, scancode %#x) %s.\n", + ps2serkbd->set, code, ps2serkbd->release ? "released" : "pressed"); + break; + default: + input_report_key(&ps2serkbd->dev, ps2serkbd->keycode[code], !ps2serkbd->release); + } + + ps2serkbd->release = 0; +} + +/* + * ps2serkbd_event() handles events from the input module. + */ + +static int ps2serkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + switch (type) { + + case EV_LED: + + return 0; + } + + return -1; +} + +static int ps2serkbd_initialize(struct ps2serkbd *ps2serkbd) +{ + return 0; +} + +static void ps2serkbd_reinit(void *data) +{ +} + + +static void ps2serkbd_connect(struct serio *serio, struct serio_dev *dev) +{ + struct ps2serkbd *ps2serkbd; + int i; + + if ((serio->type & SERIO_TYPE) != SERIO_RS232) + return; + + if ((serio->type & SERIO_PROTO) && (serio->type & SERIO_PROTO) != SERIO_PS2SER) + return; + + + if (!(ps2serkbd = kmalloc(sizeof(struct ps2serkbd), GFP_KERNEL))) + return; + + memset(ps2serkbd, 0, sizeof(struct ps2serkbd)); + + ps2serkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); + ps2serkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); + + ps2serkbd->serio = serio; + + ps2serkbd->dev.keycode = ps2serkbd->keycode; + ps2serkbd->dev.event = ps2serkbd_event; + ps2serkbd->dev.private = ps2serkbd; + + ps2serkbd->tq.routine = ps2serkbd_reinit; + ps2serkbd->tq.data = ps2serkbd; + + serio->private = ps2serkbd; + + if (serio_open(serio, dev)) { + kfree(ps2serkbd); + return; + } + + if (ps2serkbd_initialize(ps2serkbd) < 0) { + serio_close(serio); + kfree(ps2serkbd); + return; + } + + ps2serkbd->set = 4; + + if (ps2serkbd->set == 4) { + ps2serkbd->dev.ledbit[0] |= 0; + sprintf(ps2serkbd->name, "AT Set 2 Extended keyboard\n"); + } + memcpy(ps2serkbd->keycode, ps2serkbd_set2_keycode, sizeof(ps2serkbd->keycode)); + + sprintf(ps2serkbd->phys, "%s/input0", serio->phys); + + ps2serkbd->dev.name = ps2serkbd->name; + ps2serkbd->dev.phys = ps2serkbd->phys; + ps2serkbd->dev.idbus = BUS_RS232; + ps2serkbd->dev.idvendor = SERIO_PS2SER; + ps2serkbd->dev.idproduct = ps2serkbd->set; + ps2serkbd->dev.idversion = ps2serkbd->id; + + for (i = 0; i < 512; i++) + if (ps2serkbd->keycode[i] && ps2serkbd->keycode[i] <= 250) + set_bit(ps2serkbd->keycode[i], ps2serkbd->dev.keybit); + + input_register_device(&ps2serkbd->dev); +} + +/* + * ps2serkbd_disconnect() unregisters and closes behind us. + */ + +static void ps2serkbd_disconnect(struct serio *serio) +{ + struct ps2serkbd *ps2serkbd = serio->private; + input_unregister_device(&ps2serkbd->dev); + serio_close(serio); + kfree(ps2serkbd); +} + +static struct serio_dev ps2serkbd_dev = { +interrupt: + ps2serkbd_interrupt, +connect: + ps2serkbd_connect, +disconnect: + ps2serkbd_disconnect +}; + +/* + * The functions for insering/removing us as a module. + */ + +int __init ps2serkbd_init(void) +{ + serio_register_device(&ps2serkbd_dev); + return 0; +} + +void __exit ps2serkbd_exit(void) +{ + serio_unregister_device(&ps2serkbd_dev); +} + +module_init(ps2serkbd_init); +module_exit(ps2serkbd_exit); diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c new file mode 100644 index 000000000000..901567c2b355 --- /dev/null +++ b/drivers/input/keyboard/sunkbd.c @@ -0,0 +1,318 @@ +/* + * $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/tqueue.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Sun keyboard driver"); +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 tq_struct tq; + char name[64]; + char phys[32]; + char type; + char reset; + char layout; +}; + +/* + * sunkbd_interrupt() is called by the low level driver when a character + * is received. + */ + +static void sunkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + 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 */ + return; + } + + if (sunkbd->layout == -1) { + sunkbd->layout = data; + return; + } + + switch (data) { + + case SUNKBD_RET_RESET: + queue_task(&sunkbd->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + sunkbd->reset = -1; + return; + + case SUNKBD_RET_LAYOUT: + sunkbd->layout = -1; + return; + + case SUNKBD_RET_ALLUP: /* All keys released */ + return; + + default: + if (sunkbd->keycode[data & SUNKBD_KEY]) { + input_report_key(&sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE)); + } else { + printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n", + data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed"); + } + } +} + +/* + * 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) +{ + int t; + + t = 1000; + sunkbd->reset = -2; + sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET); + while (sunkbd->reset < 0 && --t) mdelay(1); + if (!t) return -1; + + sunkbd->type = sunkbd->reset; + + if (sunkbd->type == 4) { /* Type 4 keyboard */ + t = 250; + sunkbd->layout = -2; + sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT); + while (sunkbd->layout < 0 && --t) mdelay(1); + if (!t) 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; + int t = 1000; + + while (sunkbd->reset < 0 && --t) mdelay(1); + + 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_dev *dev) +{ + 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)); + + 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; + + sunkbd->tq.routine = sunkbd_reinit; + sunkbd->tq.data = sunkbd; + + sunkbd->dev.keycode = sunkbd->keycode; + sunkbd->dev.event = sunkbd_event; + sunkbd->dev.private = sunkbd; + + serio->private = sunkbd; + + if (serio_open(serio, dev)) { + 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 < 255; i++) + set_bit(sunkbd->keycode[i], sunkbd->dev.keybit); + clear_bit(0, sunkbd->dev.keybit); + + sprintf(sunkbd->name, "%s/input", serio->phys); + + sunkbd->dev.name = sunkbd->name; + sunkbd->dev.phys = sunkbd->phys; + sunkbd->dev.idbus = BUS_RS232; + sunkbd->dev.idvendor = SERIO_SUNKBD; + sunkbd->dev.idproduct = sunkbd->type; + sunkbd->dev.idversion = 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_dev sunkbd_dev = { + 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_device(&sunkbd_dev); + return 0; +} + +void __exit sunkbd_exit(void) +{ + serio_unregister_device(&sunkbd_dev); +} + +module_init(sunkbd_init); +module_exit(sunkbd_exit); diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c new file mode 100644 index 000000000000..6f0b2410788a --- /dev/null +++ b/drivers/input/keyboard/xtkbd.c @@ -0,0 +1,157 @@ +/* + * $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> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("XT keyboard driver"); +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]; +}; + +void xtkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct xtkbd *xtkbd = serio->private; + + switch (data) { + case XTKBD_EMUL0: + case XTKBD_EMUL1: + return; + default: + + if (xtkbd->keycode[data & XTKBD_KEY]) { + input_report_key(&xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE)); + } else { + printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n", + data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed"); + } + } +} + +void xtkbd_connect(struct serio *serio, struct serio_dev *dev) +{ + 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; + + xtkbd->dev.keycode = xtkbd->keycode; + xtkbd->dev.private = xtkbd; + + serio->private = xtkbd; + + if (serio_open(serio, dev)) { + 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.idbus = BUS_XTKBD; + xtkbd->dev.idvendor = 0x0001; + xtkbd->dev.idproduct = 0x0001; + xtkbd->dev.idversion = 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_dev xtkbd_dev = { + interrupt: xtkbd_interrupt, + connect: xtkbd_connect, + disconnect: xtkbd_disconnect +}; + +int __init xtkbd_init(void) +{ + serio_register_device(&xtkbd_dev); + return 0; +} + +void __exit xtkbd_exit(void) +{ + serio_unregister_device(&xtkbd_dev); +} + +module_init(xtkbd_init); +module_exit(xtkbd_exit); diff --git a/drivers/input/mouse/Config.help b/drivers/input/mouse/Config.help new file mode 100644 index 000000000000..c60ae83ff85d --- /dev/null +++ b/drivers/input/mouse/Config.help @@ -0,0 +1,87 @@ +CONFIG_INPUT_MOUSE + 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 + 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. + + If unsure, say Y. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called psmouse.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_MOUSE_SERIAL + 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. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called sermouse.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_MOUSE_INPORT + Say Y here if you have an InPort, Microsoft or ATI XL busmouse. + They are rather rare these days. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called inport.o. If you want to compile it as a + module, say M here and read <file.:Documentation/modules.txt>. + +CONFIG_MOUSE_ATIXL + Say Y here if your mouse is of the ATI XL variety. + +CONFIG_MOUSE_LOGIBM + Say Y here if you have a Logitech busmouse. + They are rather rare these days. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called logibm.o. If you want to compile it as a + module, say M here and read <file.:Documentation/modules.txt>. + +CONFIG_MOUSE_PC110PAD + Say Y if you have the IBM PC-110 micro-notebook and want its + touchscreen supported. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called pc110pad.o. If you want to compile it as a + module, say M here and read <file.:Documentation/modules.txt>. + +CONFIG_MOUSE_MAPLE + Say Y if you have a DreamCast console and a mouse attached to + its Maple bus. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called maplemouse.o. If you want to compile it as a + module, say M here and read <file.:Documentation/modules.txt>. + +CONFIG_MOUSE_AMIGA + Say Y here if you have an Amiga and want its native mouse + supported by the kernel. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called amimouse.o. If you want to compile it as a + module, say M here and read <file.:Documentation/modules.txt>. + +CONFIG_MOUSE_ACORN + Say Y here if you have the Acorn RiscPC computer and want its + native mouse supported. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called rpcmouse.o. If you want to compile it as a + module, say M here and read <file.:Documentation/modules.txt>. diff --git a/drivers/input/mouse/Config.in b/drivers/input/mouse/Config.in new file mode 100644 index 000000000000..d740c4188ebd --- /dev/null +++ b/drivers/input/mouse/Config.in @@ -0,0 +1,24 @@ +# +# Mouse driver configuration +# + +bool 'Mice' CONFIG_INPUT_MOUSE + +dep_tristate ' PS/2 mouse' CONFIG_MOUSE_PS2 $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_SERIO +dep_tristate ' Serial mouse' CONFIG_MOUSE_SERIAL $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_SERIO + +dep_tristate ' InPort/MS/ATIXL busmouse' CONFIG_MOUSE_INPORT $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_ISA +if [ "$CONFIG_MOUSE_INPORT" != "n" ]; then + bool ' ATI XL variant' CONFIG_MOUSE_ATIXL +fi +dep_tristate ' Logitech busmouse' CONFIG_MOUSE_LOGIBM $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_ISA +dep_tristate ' IBM PC110 touchpad' CONFIG_MOUSE_PC110PAD $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_ISA +if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then + dep_tristate ' Maple bus mouse' CONFIG_MOUSE_MAPLE $CONFIG_INPUT $CONFIG_INPUT_MOUSE $CONFIG_MAPLE +fi +if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga mouse' CONFIG_MOUSE_AMIGA $CONFIG_INPUT $CONFIG_INPUT_MOUSE +fi +if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + dep_tristate ' Acorn RiscPC mouse' CONFIG_MOUSE_ACORN $CONFIG_INPUT $CONFIG_INPUT_MOUSE +fi diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile new file mode 100644 index 000000000000..6ac13c13edcf --- /dev/null +++ b/drivers/input/mouse/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the mouse drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o +obj-$(CONFIG_MOUSE_ACORN) += 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 + +# The global Rules.make. + +include $(TOPDIR)/Rules.make diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c new file mode 100644 index 000000000000..819d96827187 --- /dev/null +++ b/drivers/input/mouse/amimouse.c @@ -0,0 +1,147 @@ +/* + * $Id: amimouse.c,v 1.9 2001/09/25 10:12:07 vojtech Exp $ + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Michael Rausch James Banks + * Matther Dillon David Giller + * Nathan Laredo Linus Torvalds + * Johan Myreen Jes Sorensen + * Russel King + */ + +/* + * Amiga mouse 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 <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 void 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) - lastx; + if (dx > 127) dx = (nx - 256) - lastx; + if (dy < -127) dy = (256 + ny) - lasty; + if (dy > 127) dy = (ny - 256) - lasty; + + amimouse_lastx = nx; + amimouse_lasty = ny; + + potgor = custom.potgor; + + 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); +} + +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", NULL)) { + amimouse_used--; + printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", amimouse_irq); + 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.idbus = BUS_AMIGA; + amimouse_dev.idvendor = 0x0001; + amimouse_dev.idproduct = 0x0002; + amimouse_dev.idversion = 0x0100; + + input_register_device(&amimouse_dev); + + printk(KERN_INFO "input: %s at joy0dat\n", amimouse_name); +} + +static void __exit amimouse_exit(void) +{ + input_unregister_device(&amimouse_dev); +} + +module_init(amimouse_init); +module_exit(amimouse_exit); diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c new file mode 100644 index 000000000000..9931ff92835e --- /dev/null +++ b/drivers/input/mouse/inport.c @@ -0,0 +1,193 @@ +/* + * $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 <asm/io.h> +#include <asm/irq.h> + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/input.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 + +MODULE_PARM(inport_irq, "i"); + +static int inport_irq = INPORT_IRQ; +static int inport_used = 0; + +static void 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", + idbus: BUS_ISA, + idvendor: INPORT_VENDOR, + idproduct: 0x0001, + idversion: 0x0100, +}; + +static void 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); + + 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); +} + +#ifndef MODULE +static int __init inport_setup(char *str) +{ + int ints[4]; + str = get_options(str, ARRAY_SIZE(ints), ints); + if (ints[0] > 0) inport_irq = ints[1]; + return 1; +} +__setup("inport_irq=", inport_setup); +#endif + +static int __init inport_init(void) +{ + unsigned char a,b,c; + + if (check_region(INPORT_BASE, INPORT_EXTENT)) + return -EBUSY; + + a = inb(INPORT_SIGNATURE_PORT); + b = inb(INPORT_SIGNATURE_PORT); + c = inb(INPORT_SIGNATURE_PORT); + if (( a == b ) || ( a != c )) + return -ENODEV; + + outb(INPORT_RESET, INPORT_CONTROL_PORT); + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + + request_region(INPORT_BASE, INPORT_EXTENT, "inport"); + + 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); diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c new file mode 100644 index 000000000000..f1b34343de90 --- /dev/null +++ b/drivers/input/mouse/logibm.c @@ -0,0 +1,182 @@ +/* + * $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 <asm/io.h> +#include <asm/irq.h> + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/input.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 + +MODULE_PARM(logibm_irq, "i"); + +static int logibm_irq = LOGIBM_IRQ; +static int logibm_used = 0; + +static void 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", + idbus: BUS_ISA, + idvendor: 0x0003, + idproduct: 0x0001, + idversion: 0x0100, +}; + +static void 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; + + input_report_rel(&logibm_dev, REL_X, dx); + input_report_rel(&logibm_dev, REL_Y, 255 - dy); + input_report_key(&logibm_dev, BTN_MIDDLE, buttons & 1); + input_report_key(&logibm_dev, BTN_LEFT, buttons & 2); + input_report_key(&logibm_dev, BTN_RIGHT, buttons & 4); +} + +#ifndef MODULE +static int __init logibm_setup(char *str) +{ + int ints[4]; + str = get_options(str, ARRAY_SIZE(ints), ints); + if (ints[0] > 0) logibm_irq = ints[1]; + return 1; +} +__setup("logibm_irq=", logibm_setup); +#endif + +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); diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c new file mode 100644 index 000000000000..34d90206a5b8 --- /dev/null +++ b/drivers/input/mouse/maplemouse.c @@ -0,0 +1,137 @@ +/* + * $Id: maplemouse.c,v 1.1 2001/11/02 17:27:32 jsimmons 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); +} + + +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); + + 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.idbus = BUS_MAPLE; + + input_register_device(&mouse->dev); + + maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE); + + printk(KERN_INFO "input%d: mouse(0x%lx): %s\n", + mouse->dev.number, data, mouse->dev.name); + + MOD_INC_USE_COUNT; + + 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); + + MOD_DEC_USE_COUNT; +} + + +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: + */ diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c new file mode 100644 index 000000000000..61ee96685dcf --- /dev/null +++ b/drivers/input/mouse/pc110pad.c @@ -0,0 +1,163 @@ +/* + * $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 <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 void 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; + + 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)); + + pc110pad_count = 0; +} + +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) +{ + unsigned long flags; + + if (pc110pad_used++) + return 0; + + save_flags(flags); + cli(); + pc110pad_interrupt(0,0,0); + pc110pad_interrupt(0,0,0); + pc110pad_interrupt(0,0,0); + outb(PC110PAD_ON, pc110pad_io + 2); + pc110pad_count = 0; + restore_flags(flags); + + return 0; +} + +static int __init pc110pad_init(void) +{ + 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", 0)) + { + 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.idbus = BUS_ISA; + pc110pad_dev.idvendor = 0x0003; + pc110pad_dev.idproduct = 0x0001; + pc110pad_dev.idversion = 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, 0); + release_region(pc110pad_io, 4); +} + +module_init(pc110pad_init); +module_exit(pc110pad_exit); diff --git a/drivers/input/mouse/psmouse.c b/drivers/input/mouse/psmouse.c new file mode 100644 index 000000000000..ba846a57caa4 --- /dev/null +++ b/drivers/input/mouse/psmouse.c @@ -0,0 +1,652 @@ +/* + * $Id: psmouse.c,v 1.18 2002/03/13 10:03:43 vojtech Exp $ + * + * Copyright (c) 1999-2001 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 + * + * 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/serio.h> +#include <linux/init.h> +#include <linux/tqueue.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("PS/2 mouse driver"); +MODULE_LICENSE("GPL"); + +#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 0x01f2 +#define PSMOUSE_CMD_SETRATE 0x10f3 +#define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_RESET_DIS 0x00f6 + +#define PSMOUSE_RET_BAT 0xaa +#define PSMOUSE_RET_ACK 0xfa +#define PSMOUSE_RET_NAK 0xfe + +struct psmouse { + struct input_dev dev; + struct serio *serio; + char *vendor; + char *name; + struct tq_struct tq; + unsigned char cmdbuf[8]; + unsigned char packet[8]; + unsigned char cmdcnt; + unsigned char pktcnt; + unsigned char type; + unsigned long last; + char acking; + char ack; + char error; + char devname[64]; + char phys[32]; +}; + +#define PSMOUSE_PS2 1 +#define PSMOUSE_PS2PP 2 +#define PSMOUSE_PS2TPP 3 +#define PSMOUSE_GENPS 4 +#define PSMOUSE_IMPS 5 +#define PSMOUSE_IMEX 6 + +static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2" }; + +/* + * psmouse_process_packet() anlyzes the PS/2 mouse packet contents and + * reports relevant events to the input module. + */ + +static void psmouse_process_packet(struct psmouse *psmouse) +{ + struct input_dev *dev = &psmouse->dev; + unsigned char *packet = psmouse->packet; + +/* + * The PS2++ protocol is a little bit complex + */ + + if (psmouse->type == PSMOUSE_PS2PP || psmouse->type == PSMOUSE_PS2TPP) { + + if ((packet[0] & 0x40) == 0x40 && (int) packet[1] - (int) ((packet[0] & 0x10) << 4) > 191 ) { + + switch (((packet[1] >> 4) & 0x03) | ((packet[0] >> 2) & 0xc0)) { + + case 1: /* Mouse extra info */ + + input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, + (int) (packet[2] & 7) - (int) (packet[2] & 8)); + input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); + input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); + + break; + + case 3: /* TouchPad extra info */ + + input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, + (int) ((packet[2] >> 4) & 7) - (int) ((packet[2] >> 4) & 8)); + packet[0] = packet[2] | 0x08; + + break; + + default: + + printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", + ((packet[1] >> 4) & 0x03) | ((packet[0] >> 2) & 0xc0)); + + } + + packet[0] &= 0x0f; + packet[1] = 0; + packet[2] = 0; + + } + } + +/* + * 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] & 7) - (int) (packet[2] & 8)); + 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); + +} + +/* + * psmouse_interrupt() handles incoming characters, either gathering them into + * packets or passing them to the command routine as command output. + */ + +static void psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct psmouse *psmouse = serio->private; + + if (psmouse->acking) { + switch (data) { + case PSMOUSE_RET_ACK: + psmouse->ack = 1; + break; + case PSMOUSE_RET_NAK: + psmouse->ack = -1; + break; + default: + psmouse->ack = 1; /* Workaround for mice which don't ACK the Get ID command */ + if (psmouse->cmdcnt) + psmouse->cmdbuf[--psmouse->cmdcnt] = data; + break; + } + psmouse->acking = 0; + return; + } + + if (psmouse->cmdcnt) { + psmouse->cmdbuf[--psmouse->cmdcnt] = data; + return; + } + + if (psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/20)) { + printk(KERN_WARNING "psmouse.c: Lost synchronization, throwing %d bytes away.\n", psmouse->pktcnt); + psmouse->pktcnt = 0; + } + + psmouse->last = jiffies; + psmouse->packet[psmouse->pktcnt++] = data; + + if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) { + if ((psmouse->packet[0] & 0x08) == 0x08) psmouse_process_packet(psmouse); + psmouse->pktcnt = 0; + return; + } + + if (psmouse->pktcnt == 1 && psmouse->packet[0] == PSMOUSE_RET_BAT) { + queue_task(&psmouse->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + } +} + +/* + * 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. + */ + +static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte) +{ + int timeout = 10000; /* 100 msec */ + psmouse->ack = 0; + psmouse->acking = 1; + + serio_write(psmouse->serio, byte); + while (!psmouse->ack && timeout--) udelay(10); + + return -(psmouse->ack <= 0); +} + +/* + * psmouse_command() sends a command and its parameters to the mouse, + * then waits for the response and puts it in the param array. + */ + +static int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command) +{ + int timeout = 500000; /* 500 msec */ + int send = (command >> 12) & 0xf; + int receive = (command >> 8) & 0xf; + int i; + + psmouse->cmdcnt = receive; + + if (command & 0xff) + if (psmouse_sendbyte(psmouse, command & 0xff)) + return (psmouse->cmdcnt = 0) - 1; + + for (i = 0; i < send; i++) + if (psmouse_sendbyte(psmouse, param[i])) + return (psmouse->cmdcnt = 0) - 1; + + while (psmouse->cmdcnt && timeout--) udelay(1); + + for (i = 0; i < receive; i++) + param[i] = psmouse->cmdbuf[(receive - 1) - i]; + + if (psmouse->cmdcnt) + return (psmouse->cmdcnt = 0) - 1; + + return 0; +} + +/* + * psmouse_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 psmouse_ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) +{ + unsigned char d; + int i; + + if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) + return -1; + + for (i = 6; i >= 0; i -= 2) { + d = (command >> i) & 3; + if(psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES)) + return -1; + } + + if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL)) + return -1; + + return 0; +} + +/* + * psmouse_extensions() probes for any extensions to the basic PS/2 protocol + * the mouse may have. + */ + +static int psmouse_extensions(struct psmouse *psmouse) +{ + unsigned char param[4]; + + param[0] = 0; + psmouse->vendor = "Generic"; + psmouse->name = "Mouse"; + +/* + * Try Genius NetMouse magic init. + */ + + 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); + + if (param[0] == 0x00 && param[1] == 0x33 && param[2] == 0x55) { + psmouse->vendor = "Genius"; + psmouse->name = "Mouse"; + + set_bit(BTN_EXTRA, psmouse->dev.keybit); + set_bit(BTN_SIDE, psmouse->dev.keybit); + set_bit(REL_WHEEL, psmouse->dev.relbit); + + return PSMOUSE_GENPS; + } + +/* + * Try Logitech magic ID. + */ + + 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); + psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO); + + if (param[1]) { + + int i; + static int logitech_4btn[] = { 12, 40, 41, 42, 43, 73, 80, -1 }; + static int logitech_wheel[] = { 75, 76, 80, 81, 83, 88, -1 }; + static int logitech_ps2pp[] = { 12, 13, 40, 41, 42, 43, 50, 51, 52, 53, 73, 75, + 76, 80, 81, 83, 88, 96, 97, -1 }; + + int devicetype = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); + + psmouse->vendor = "Logitech"; + psmouse->name = "Mouse"; + + if (param[1] < 3) + clear_bit(BTN_MIDDLE, psmouse->dev.keybit); + if (param[1] < 2) + clear_bit(BTN_RIGHT, psmouse->dev.keybit); + + psmouse->type = PSMOUSE_PS2; + + for (i = 0; logitech_ps2pp[i] != -1; i++) + if (logitech_ps2pp[i] == devicetype) psmouse->type = PSMOUSE_PS2PP; + + if (psmouse->type != PSMOUSE_PS2PP) return PSMOUSE_PS2; + + for (i = 0; logitech_4btn[i] != -1; i++) + if (logitech_4btn[i] == devicetype) set_bit(BTN_SIDE, psmouse->dev.keybit); + + for (i = 0; logitech_wheel[i] != -1; i++) + if (logitech_wheel[i] == devicetype) set_bit(REL_WHEEL, psmouse->dev.relbit); + +/* + * Do Logitech PS2++ / PS2T++ magic init. + */ + + if (devicetype == 97) { /* TouchPad 3 */ + + set_bit(REL_WHEEL, psmouse->dev.relbit); + set_bit(REL_HWHEEL, psmouse->dev.relbit); + + param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; /* Unprotect RAM */ + psmouse_command(psmouse, param, 0x30d1); + param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; /* Enable features */ + psmouse_command(psmouse, param, 0x30d1); + param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; /* Enable PS2++ */ + psmouse_command(psmouse, param, 0x30d1); + + param[0] = 0; + if (!psmouse_command(psmouse, param, 0x13d1) && + param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) + return PSMOUSE_PS2TPP; + + } else { + psmouse_ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ + psmouse_ps2pp_cmd(psmouse, param, 0xDB); + + if ((param[0] & 0x78) == 0x48 && (param[1] & 0xf3) == 0xc2 && + (param[2] & 3) == ((param[1] >> 2) & 3)) + return PSMOUSE_PS2PP; + } + + } + +/* + * Try IntelliMouse magic init. + */ + + 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); + + if (param[0] == 3) { + + set_bit(REL_WHEEL, psmouse->dev.relbit); + +/* + * Try IntelliMouse Explorer magic init. + */ + + 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); + + if (param[0] == 4) { + + psmouse->vendor = "Microsoft"; + psmouse->name = "IntelliMouse Explorer"; + + set_bit(BTN_SIDE, psmouse->dev.keybit); + set_bit(BTN_EXTRA, psmouse->dev.keybit); + + return PSMOUSE_IMEX; + } + + psmouse->vendor = "Microsoft"; + psmouse->name = "IntelliMouse"; + + return PSMOUSE_IMPS; + } + +/* + * Okay, all failed, we have a standard mouse here. The number of the buttons is + * still a question, though. + */ + + psmouse->vendor = "Generic"; + psmouse->name = "Mouse"; + + return PSMOUSE_PS2; +} + +/* + * psmouse_probe() probes for a PS/2 mouse. + */ + +static int psmouse_probe(struct psmouse *psmouse) +{ + unsigned char param[2]; + +/* + * First we reset and disable the mouse. + */ + + if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS)) + return -1; + +/* + * Next, 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] = param[1] = 0xa5; + + if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETID)) + return -1; + + if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04) + return -1; + +/* + * And here we try to determine if it has any extensions over the + * basic PS/2 3-button mouse. + */ + + return psmouse->type = psmouse_extensions(psmouse); +} + +/* + * 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 to a highest possible value. + * We try 100 first in case mouse fails to set 200. + */ + + param[0] = 100; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + + param[0] = 200; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); + +/* + * We also set the resolution and scaling. + */ + + param[0] = 3; + psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES); + psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11); + +/* + * We set the mouse into streaming mode. + */ + + psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM); + +/* + * Last, we enable the mouse so that we get reports from it. + */ + + if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) { + printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); + } + +} + +/* + * psmouse_disconnect() cleans up after we don't want talk + * to the mouse anymore. + */ + +static void psmouse_disconnect(struct serio *serio) +{ + struct psmouse *psmouse = serio->private; + input_unregister_device(&psmouse->dev); + serio_close(serio); + kfree(psmouse); +} + +/* + * psmouse_powerup() is called when we get the powerup + * sequence - 0xaa [0x00], so that the mouse/kbd is re-probed. + */ + +static void psmouse_powerup(void *data) +{ + struct psmouse *psmouse = data; + + if (psmouse->packet[0] == PSMOUSE_RET_BAT && (psmouse->pktcnt == 1 || + (psmouse->pktcnt == 2 && psmouse->packet[1] == 0x00))) { + mdelay(40); /* FIXME!!! Wait some nicer way */ + serio_rescan(psmouse->serio); + } +} + +/* + * psmouse_connect() is a callback form the serio module when + * an unhandled serio port is found. + */ + +static void psmouse_connect(struct serio *serio, struct serio_dev *dev) +{ + struct psmouse *psmouse; + + if ((serio->type & SERIO_TYPE) != SERIO_8042) + return; + + if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) + return; + + memset(psmouse, 0, sizeof(struct psmouse)); + + 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->tq.routine = psmouse_powerup; + psmouse->tq.data = psmouse; + + serio->private = psmouse; + + if (serio_open(serio, dev)) { + kfree(psmouse); + return; + } + + if (psmouse_probe(psmouse) <= 0) { + serio_close(serio); + kfree(psmouse); + return; + } + + 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.idbus = BUS_I8042; + psmouse->dev.idvendor = psmouse->type; + psmouse->dev.idproduct = 0x0002; + psmouse->dev.idversion = 0x0100; + + input_register_device(&psmouse->dev); + + printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); + + psmouse_initialize(psmouse); +} + +static struct serio_dev psmouse_dev = { + interrupt: psmouse_interrupt, + connect: psmouse_connect, + disconnect: psmouse_disconnect +}; + +int __init psmouse_init(void) +{ + serio_register_device(&psmouse_dev); + return 0; +} + +void __exit psmouse_exit(void) +{ + serio_unregister_device(&psmouse_dev); +} + +module_init(psmouse_init); +module_exit(psmouse_exit); diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c new file mode 100644 index 000000000000..04d16de5defc --- /dev/null +++ b/drivers/input/mouse/rpcmouse.c @@ -0,0 +1,111 @@ +/* + * $Id: rpcmouse.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Russel King + */ + +/* + * Acorn RiscPC mouse driver for Linux/ARM + */ + +/* + * 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/sched.h> +#include <linux/ptrace.h> +#include <linux/interrupt.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/iomd.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Acorn RiscPC mouse driver"); +MODULE_LICENSE("GPL"); + +#define IOMD_MOUSEBTN 0x800C4000 + +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", + idbus: BUS_ISA, + idvendor: 0x0005, + idproduct: 0x0001, + idversion: 0x0100, +}; + +static void rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + short x, y, dx, dy, b; + + x = (short) inl(IOMD_MOUSEX); + y = (short) inl(IOMD_MOUSEY); + b = (short) inl(IOMD_MOUSEBTN); + + dx = x - rpcmouse_lastx; + dy = y - rpcmouse_lasty; + + rpcmouse_lastx = x; + rpcmouse_lasty = y; + + input_report_rel(&rpcmouse_dev, REL_X, dx); + input_report_rel(&rpcmouse_dev, REL_Y, dy); + + input_report_key(&amimouse_dev, BTN_LEFT, buttons & 0x10); + input_report_key(&amimouse_dev, BTN_MIDDLE, buttons & 0x20); + input_report_key(&amimouse_dev, BTN_RIGHT, buttons & 0x40); +} + +static int __init rpcmouse_init(void) +{ + rpcmouse_lastx = (short) inl(IOMD_MOUSEX); + rpcmouse_lasty = (short) inl(IOMD_MOUSEY); + + if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, SA_SHIRQ, "rpcmouse", NULL)) { + printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n"); + return -1; + } + + input_register_device(&rpcmouse_dev); + printk(KERN_INFO "input%d: Acorn RiscPC mouse irq %d", IRQ_VSYNCPULSE); + + return 0; +} + +static void __exit rpcmouse_exit(void) +{ + input_unregister_device(&rpcmouse_dev); + free_irq(IRQ_VSYNCPULSE, NULL); +} + +module_init(rpcmouse_init); +module_exit(rpcmouse_exit); diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c new file mode 100644 index 000000000000..4dd1a4e9033c --- /dev/null +++ b/drivers/input/mouse/sermouse.c @@ -0,0 +1,299 @@ +/* + * $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> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Serial mouse driver"); +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 input_dev *dev = &sermouse->dev; + signed char *buf = sermouse->buf; + + 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; + } + + 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 input_dev *dev = &sermouse->dev; + signed char *buf = sermouse->buf; + + if (data & 0x40) sermouse->count = 0; + + 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 & 7) - (data & 8)); + 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; + } + + sermouse->count++; +} + +/* + * sermouse_interrupt() handles incoming characters, either gathering them into + * packets or passing them to the command routine as command output. + */ + +static void sermouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct sermouse *sermouse = serio->private; + + if (time_after(jiffies, sermouse->last + HZ/20)) sermouse->count = 0; + sermouse->last = jiffies; + + if (sermouse->type > SERIO_SUN) + sermouse_process_ms(sermouse, data); + else + sermouse_process_msc(sermouse, data); +} + +/* + * 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_dev *dev) +{ + 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)); + + 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.idbus = BUS_RS232; + sermouse->dev.idvendor = sermouse->type; + sermouse->dev.idproduct = c; + sermouse->dev.idversion = 0x0100; + + if (serio_open(serio, dev)) { + 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_dev sermouse_dev = { + interrupt: sermouse_interrupt, + connect: sermouse_connect, + disconnect: sermouse_disconnect +}; + +int __init sermouse_init(void) +{ + serio_register_device(&sermouse_dev); + return 0; +} + +void __exit sermouse_exit(void) +{ + serio_unregister_device(&sermouse_dev); +} + +module_init(sermouse_init); +module_exit(sermouse_exit); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 01fb98c7f30f..a5b4152e2c97 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -1,5 +1,5 @@ /* - * $Id: mousedev.c,v 1.38 2001/12/26 21:08:33 jsimmons Exp $ + * $Id: mousedev.c,v 1.42 2002/04/09 20:51:26 jdeneux Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * @@ -38,6 +38,10 @@ #include <linux/config.h> #include <linux/smp_lock.h> #include <linux/random.h> +#include <linux/major.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"); @@ -225,7 +229,14 @@ static int mousedev_release(struct inode * inode, struct file * file) static int mousedev_open(struct inode * inode, struct file * file) { struct mousedev_list *list; - int i = minor(inode->i_rdev) - MOUSEDEV_MINOR_BASE; + int i; + +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX + if (major(inode->i_rdev) == MISC_MAJOR) + i = MOUSEDEV_MIX; + else +#endif + i = minor(inode->i_rdev) - MOUSEDEV_MINOR_BASE; if (i >= MOUSEDEV_MINORS || !mousedev_table[i]) return -ENODEV; @@ -494,6 +505,12 @@ static struct input_handler mousedev_handler = { id_table: mousedev_ids, }; +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX +static struct miscdevice psaux_mouse = { + PSMOUSE_MINOR, "psaux", &mousedev_fops +}; +#endif + static int __init mousedev_init(void) { input_register_handler(&mousedev_handler); @@ -504,6 +521,9 @@ static int __init mousedev_init(void) mousedev_mix.exist = 1; mousedev_mix.minor = MOUSEDEV_MIX; mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX + misc_register(&psaux_mouse); +#endif printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); @@ -512,6 +532,9 @@ static int __init mousedev_init(void) static void __exit mousedev_exit(void) { +#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX + misc_deregister(&psaux_mouse); +#endif input_unregister_minor(mousedev_mix.devfs); input_unregister_handler(&mousedev_handler); } diff --git a/drivers/input/power.c b/drivers/input/power.c new file mode 100644 index 000000000000..84af6b392dd5 --- /dev/null +++ b/drivers/input/power.c @@ -0,0 +1,180 @@ +/* + * $Id: power.c,v 1.10 2001/09/25 09:17:15 vojtech Exp $ + * + * Copyright (c) 2001 "Crazy" James Simmons + * + * Input driver Power Management. + * + * Sponsored by Transvirtual Technology. + */ + +/* + * 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 <jsimmons@transvirtual.com>. + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/delay.h> +#include <linux/pm.h> + +static struct input_handler power_handler; + +/* + * Power management can't be done in a interrupt context. So we have to + * use keventd. + */ +static int suspend_button_pushed = 0; +static void suspend_button_task_handler(void *data) +{ + //extern void pm_do_suspend(void); + udelay(200); /* debounce */ + //pm_do_suspend(); + suspend_button_pushed = 0; +} + +static struct tq_struct suspend_button_task = { + routine: suspend_button_task_handler +}; + +static void power_event(struct input_handle *handle, unsigned int type, + unsigned int code, int down) +{ + struct input_dev *dev = handle->dev; + + printk("Entering power_event\n"); + + if (type != EV_KEY || type != EV_PWR) return; + + if (type == EV_PWR) { + switch (code) { + case KEY_SUSPEND: + printk("Powering down entire device\n"); + + //pm_send_all(PM_SUSPEND, dev); + + if (!suspend_button_pushed) { + suspend_button_pushed = 1; + schedule_task(&suspend_button_task); + } + break; + case KEY_POWER: + /* Hum power down the machine. */ + break; + default: + return; + } + } else { + switch (code) { + case KEY_SUSPEND: + printk("Powering down input device\n"); + /* This is risky. See pm.h for details. */ + if (dev->state != PM_RESUME) + dev->state = PM_RESUME; + else + dev->state = PM_SUSPEND; + pm_send(dev->pm_dev, dev->state, dev); + break; + case KEY_POWER: + /* Turn the input device off completely ? */ + break; + default: + return; + } + } + return; +} + +static struct input_handle *power_connect(struct input_handler *handler, + struct input_dev *dev, + struct input_device_id *id) +{ + struct input_handle *handle; + + if (!test_bit(EV_KEY, dev->evbit) || !test_bit(EV_PWR, dev->evbit)) + return NULL; + + if (!test_bit(KEY_SUSPEND, dev->keybit) || (!test_bit(KEY_POWER, dev->keybit))) + return NULL; + + if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) + return NULL; + memset(handle, 0, sizeof(struct input_handle)); + + handle->dev = dev; + handle->handler = handler; + + input_open_device(handle); + + printk(KERN_INFO "power.c: Adding power management to input layer\n"); + return handle; +} + +static void power_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + kfree(handle); +} + +static struct input_device_id power_ids[] = { + { + flags: INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, + evbit: { BIT(EV_KEY) }, + keybit: { [LONG(KEY_SUSPEND)] = BIT(KEY_SUSPEND) } + }, + { + flags: INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, + evbit: { BIT(EV_KEY) }, + keybit: { [LONG(KEY_POWER)] = BIT(KEY_POWER) } + }, + { + flags: INPUT_DEVICE_ID_MATCH_EVBIT, + evbit: { BIT(EV_PWR) }, + }, + { }, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, power_ids); + +static struct input_handler power_handler = { + event: power_event, + connect: power_connect, + disconnect: power_disconnect, + name: "power", + id_table: power_ids, +}; + +static int __init power_init(void) +{ + input_register_handler(&power_handler); + return 0; +} + +static void __exit power_exit(void) +{ + input_unregister_handler(&power_handler); +} + +module_init(power_init); +module_exit(power_exit); + +MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>"); +MODULE_DESCRIPTION("Input Power Management driver"); diff --git a/drivers/input/serio/Config.help b/drivers/input/serio/Config.help index a82785414452..ae97e4223fc0 100644 --- a/drivers/input/serio/Config.help +++ b/drivers/input/serio/Config.help @@ -12,6 +12,18 @@ CONFIG_SERIO The module will be called serio.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +CONFIG_SERIO_I8042 + i8042 is the chip over which the standard AT keyboard and PS/2 + mouse are connected to the computer. If you use these devices, + you'll need to say Y here. + + If unsure, say Y. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called i8042.o. If you want to compile it + as a module, say M here and read <file:Documentation/modules.txt>. + CONFIG_SERIO_SERPORT Say Y here if you plan to use an input device (mouse, joystick, tablet, 6dof) that communicates over the RS232 serial (COM) port. @@ -24,3 +36,38 @@ CONFIG_SERIO_SERPORT inserted in and removed from the running kernel whenever you want). The module will be called serport.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_SERIO_CT82C710 + Say Y here if you have a Texas Instruments TravelMate notebook + equipped with the ct82c710 chip and want to use a mouse connected + to the "QuickPort". + + If unsure, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called ct82c710.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_SERIO_PARKBD + Say Y here if you built a simple parallel port adapter to attach + an additional AT keyboard, XT keyboard or PS/2 mouse. + + More information is available: <file:Documentation/input/input.txt> + + If unsure, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called parkbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + +CONFIG_SERIO_ACORN + Say Y here if you have the Acorn RiscPC and want to use an AT + keyboard connected to its keyboard controller. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called rpckbd.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. + diff --git a/drivers/input/serio/Config.in b/drivers/input/serio/Config.in index cc566ad20586..e378e810c18e 100644 --- a/drivers/input/serio/Config.in +++ b/drivers/input/serio/Config.in @@ -4,4 +4,16 @@ tristate 'Serial i/o support' CONFIG_SERIO +dep_tristate ' i8042 PC Keyboard controller' CONFIG_SERIO_I8042 $CONFIG_SERIO $CONFIG_ISA +if [ "$CONFIG_SERIO_I8042" != "n" ]; then + hex ' Register Base Address' CONFIG_I8042_REG_BASE 60 + int ' PS/2 Keyboard IRQ' CONFIG_I8042_KBD_IRQ 1 + int ' PS/2 AUX IRQ' CONFIG_I8042_AUX_IRQ 12 +fi dep_tristate ' Serial port line discipline' CONFIG_SERIO_SERPORT $CONFIG_SERIO +dep_tristate ' ct82c710 Aux port controller' CONFIG_SERIO_CT82C710 $CONFIG_SERIO $CONFIG_ISA +dep_tristate ' Parallel port keyboard adapter' CONFIG_SERIO_PARKBD $CONFIG_SERIO $CONFIG_PARPORT + +if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + dep_tristate ' Acorn RiscPC keyboard controller' CONFIG_SERIO_ACORN $CONFIG_SERIO +fi diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 5865d9be9059..8e8e036a0e17 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -9,7 +9,11 @@ export-objs := serio.o # Each configuration option enables a list of files. obj-$(CONFIG_SERIO) += serio.o +obj-$(CONFIG_SERIO_I8042) += i8042.o +obj-$(CONFIG_SERIO_PARKBD) += parkbd.o obj-$(CONFIG_SERIO_SERPORT) += serport.o +obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o +obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o # The global Rules.make. diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c new file mode 100644 index 000000000000..8202a64606bb --- /dev/null +++ b/drivers/input/serio/ct82c710.c @@ -0,0 +1,212 @@ +/* + * $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * 82C710 C&T mouse port chip 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/module.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/errno.h> +#include <linux/sched.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("82C710 C&T mouse port chip driver"); +MODULE_LICENSE("GPL"); + +static char ct82c710_name[] = "C&T 82c710 mouse port"; +static char ct82c710_phys[16]; + +/* + * ct82c710 interface + */ + +#define CT82C710_DEV_IDLE 0x01 /* Device Idle */ +#define CT82C710_RX_FULL 0x02 /* Device Char received */ +#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */ +#define CT82C710_RESET 0x08 /* Device Reset */ +#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */ +#define CT82C710_ERROR_FLAG 0x20 /* Device Error */ +#define CT82C710_CLEAR 0x40 /* Device Clear */ +#define CT82C710_ENABLE 0x80 /* Device Enable */ + +#define CT82C710_IRQ 12 + +static int ct82c710_data = 0; +static int ct82c710_status = 0; + +static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs); + +/* + * Wait for device to send output char and flush any input char. + */ + +static int ct82c170_wait(void) +{ + int timeout = 60000; + + while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE)) + != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) { + + if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data); + + udelay(1); + timeout--; + } + + return !timeout; +} + +static void ct82c710_close(struct serio *serio) +{ + if (ct82c170_wait()) + printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); + + outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status); + + if (ct82c170_wait()) + printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); + + free_irq(CT82C710_IRQ, NULL); +} + +static int ct82c710_open(struct serio *serio) +{ + unsigned char status; + + if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL)) + return -1; + + status = inb_p(ct82c710_status); + + status |= (CT82C710_ENABLE | CT82C710_RESET); + outb_p(status, ct82c710_status); + + status &= ~(CT82C710_RESET); + outb_p(status, ct82c710_status); + + status |= CT82C710_INTS_ON; + outb_p(status, ct82c710_status); /* Enable interrupts */ + + while (ct82c170_wait()) { + printk(KERN_ERR "ct82c710: Device busy in open()\n"); + status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); + outb_p(status, ct82c710_status); + free_irq(CT82C710_IRQ, NULL); + return -1; + } + + return 0; +} + +/* + * Write to the 82C710 mouse device. + */ + +static int ct82c710_write(struct serio *port, unsigned char c) +{ + if (ct82c170_wait()) return -1; + outb_p(c, ct82c710_data); + return 0; +} + +static struct serio ct82c710_port = +{ + type: SERIO_8042, + name: ct82c710_name, + phys: ct82c710_phys, + write: ct82c710_write, + open: ct82c710_open, + close: ct82c710_close, +}; + +/* + * Interrupt handler for the 82C710 mouse port. A character + * is waiting in the 82C710. + */ + +static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs) +{ + if (ct82c710_port.dev) + ct82c710_port.dev->interrupt(&ct82c710_port, inb(ct82c710_data), 0); +} + +/* + * See if we can find a 82C710 device. Read mouse address. + */ + +static int __init ct82c710_probe(void) +{ + outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ + outb_p(0xaa, 0x3fa); /* Inverse of 55 */ + outb_p(0x36, 0x3fa); /* Address the chip */ + outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ + outb_p(0x1b, 0x2fa); /* Inverse of e4 */ + outb_p(0x0f, 0x390); /* Write index */ + if (inb_p(0x391) != 0xe4) /* Config address found? */ + return -1; /* No: no 82C710 here */ + + outb_p(0x0d, 0x390); /* Write index */ + ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */ + ct82c710_status = ct82c710_data + 1; + outb_p(0x0f, 0x390); + outb_p(0x0f, 0x391); /* Close config mode */ + + return 0; +} + +int __init ct82c710_init(void) +{ + if (ct82c710_probe()) + return -ENODEV; + + if (request_region(ct82c710_data, 2, "ct82c710")) + return -EBUSY; + + sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data); + + serio_register_port(&ct82c710_port); + + printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n", + ct82c710_data, CT82C710_IRQ); + + return 0; +} + +void __exit ct82c710_exit(void) +{ + serio_unregister_port(&ct82c710_port); + release_region(ct82c710_data, 2); +} + +module_init(ct82c710_init); +module_exit(ct82c710_exit); diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c new file mode 100644 index 000000000000..5e222b2f2e31 --- /dev/null +++ b/drivers/input/serio/i8042.c @@ -0,0 +1,717 @@ +/* + * $Id: i8042.c,v 1.21 2002/03/01 22:09:27 jsimmons Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * i8042 keyboard and mouse 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 <asm/io.h> + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/sched.h> /* request/free_irq */ + +#include "i8042.h" + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(i8042_noaux, "1i"); +MODULE_PARM(i8042_unlock, "1i"); +MODULE_PARM(i8042_reset, "1i"); +MODULE_PARM(i8042_direct, "1i"); + +static int i8042_noaux; +static int i8042_unlock; +static int i8042_reset; +static int i8042_direct; + +spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED; + +struct i8042_values { + int irq; + unsigned char disable; + unsigned char irqen; + unsigned char exists; + unsigned char *name; + unsigned char *phys; +}; + +static struct serio i8042_kbd_port; +static struct serio i8042_aux_port; +static unsigned char i8042_initial_ctr; +static unsigned char i8042_ctr; + +#ifdef I8042_DEBUG_IO +static unsigned long i8042_start; +#endif + +static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG]; +static unsigned char i8042_unxlate_table[128] = { + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 +}; + +static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* + * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to + * be ready for reading values from it / writing values to it. + */ + +static int i8042_wait_read(void) +{ + int i = 0; + while ((~inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { + udelay(50); + i++; + } + return -(i == I8042_CTL_TIMEOUT); +} + +static int i8042_wait_write(void) +{ + int i = 0; + while ((inb(I8042_STATUS_REG) & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { + udelay(50); + i++; + } + return -(i == I8042_CTL_TIMEOUT); +} + +/* + * i8042_flush() flushes all data that may be in the keyboard and mouse buffers + * of the i8042 down the toilet. + */ + +static int i8042_flush(void) +{ + unsigned long flags; + int i = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + while ((inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) +#ifdef I8042_DEBUG_IO + printk(KERN_DEBUG "i8042.c: %02x <- i8042 (flush) [%d]\n", + inb(I8042_DATA_REG), (int) (jiffies - i8042_start)); +#else + inb(I8042_DATA_REG); +#endif + + spin_unlock_irqrestore(&i8042_lock, flags); + + return i; +} + +/* + * i8042_command() executes a command on the i8042. It also sends the input parameter(s) + * of the commands to it, and receives the output value(s). The parameters are to be + * stored in the param array, and the output is placed into the same array. The number + * of the parameters and output values is encoded in bits 8-11 of the command + * number. + */ + +static int i8042_command(unsigned char *param, int command) +{ + unsigned long flags; + int retval = 0, i = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + retval = i8042_wait_write(); + if (!retval) { +#ifdef I8042_DEBUG_IO + printk(KERN_DEBUG "i8042.c: %02x -> i8042 (command) [%d]\n", + command & 0xff, (int) (jiffies - i8042_start)); +#endif + outb(command & 0xff, I8042_COMMAND_REG); + } + + if (!retval) + for (i = 0; i < ((command >> 12) & 0xf); i++) { + if ((retval = i8042_wait_write())) break; +#ifdef I8042_DEBUG_IO + printk(KERN_DEBUG "i8042.c: %02x -> i8042 (parameter) [%d]\n", + param[i], (int) (jiffies - i8042_start)); +#endif + outb(param[i], I8042_DATA_REG); + } + + if (!retval) + for (i = 0; i < ((command >> 8) & 0xf); i++) { + if ((retval = i8042_wait_read())) break; + if (inb(I8042_STATUS_REG) & I8042_STR_AUXDATA) + param[i] = ~inb(I8042_DATA_REG); + else + param[i] = inb(I8042_DATA_REG); +#ifdef I8042_DEBUG_IO + printk(KERN_DEBUG "i8042.c: %02x <- i8042 (return) [%d]\n", + param[i], (int) (jiffies - i8042_start)); +#endif + } + + spin_unlock_irqrestore(&i8042_lock, flags); + +#ifdef I8042_DEBUG_IO + if (retval) + printk(KERN_DEBUG "i8042.c: -- i8042 (timeout) [%d]\n", + (int) (jiffies - i8042_start)); +#endif + + return retval; +} + +/* + * i8042_kbd_write() sends a byte out through the keyboard interface. + * It also automatically refreshes the CTR value, since some i8042's + * trash their CTR after attempting to send data to an nonexistent + * device. + */ + +static int i8042_kbd_write(struct serio *port, unsigned char c) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + if(!(retval = i8042_wait_write())) { +#ifdef I8042_DEBUG_IO + printk(KERN_DEBUG "i8042.c: %02x -> i8042 (kbd-data) [%d]\n", + c, (int) (jiffies - i8042_start)); +#endif + outb(c, I8042_DATA_REG); + } + + spin_unlock_irqrestore(&i8042_lock, flags); + + return retval; +} + +/* + * i8042_aux_write() sends a byte out through the aux interface. + */ + +static int i8042_aux_write(struct serio *port, unsigned char c) +{ + int retval; + +/* + * Send the byte out. + */ + + retval = i8042_command(&c, I8042_CMD_AUX_SEND); + +/* + * Here we restore the CTR value. I don't know why, but i8042's in half-AT + * mode tend to trash their CTR when doing the AUX_SEND command. + */ + + retval += i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR); + +/* + * Make sure the interrupt happens and the character is received even + * in the case the IRQ isn't wired, so that we can receive further + * characters later. + */ + + i8042_interrupt(0, port, NULL); + return retval; +} + +/* + * i8042_open() is called when a port is open by the higher layer. + * It allocates an interrupt and enables the port. + */ + +static int i8042_open(struct serio *port) +{ + struct i8042_values *values = port->driver; + +/* + * Allocate the interrupt + */ + + if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL)) { + printk(KERN_ERR "i8042.c: Can't get irq %d for %s\n", values->irq, values->name); + return -1; + } + +/* + * Enable the device and its interrupt. + */ + + i8042_ctr |= values->irqen; + i8042_ctr &= ~values->disable; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name); + return -1; + } + +/* + * Flush buffers + */ + + i8042_flush(); + + return 0; +} + +/* + * i8042_close() frees the interrupt, and disables the interface when the + * upper layer doesn't need it anymore. + */ + +static void i8042_close(struct serio *port) +{ + struct i8042_values *values = port->driver; + +/* + * Disable the device and its interrupt. + */ + + i8042_ctr &= ~values->irqen; + i8042_ctr |= values->disable; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name); + return; + } + +/* + * Free the interrupt + */ + + free_irq(values->irq, NULL); +} + +/* + * Structures for registering the devices in the serio.c module. + */ + +static struct i8042_values i8042_kbd_values = { + irq: I8042_KBD_IRQ, + irqen: I8042_CTR_KBDINT, + disable: I8042_CTR_KBDDIS, + name: "KBD", + exists: 0, +}; + +static struct serio i8042_kbd_port = +{ + type: SERIO_8042, + write: i8042_kbd_write, + open: i8042_open, + close: i8042_close, + driver: &i8042_kbd_values, + name: "i8042 Kbd Port", + phys: "isa0060/serio0", +}; + +static struct i8042_values i8042_aux_values = { + irq: I8042_AUX_IRQ, + irqen: I8042_CTR_AUXINT, + disable: I8042_CTR_AUXDIS, + name: "AUX", + exists: 0, +}; + +static struct serio i8042_aux_port = +{ + type: SERIO_8042, + write: i8042_aux_write, + open: i8042_open, + close: i8042_close, + driver: &i8042_aux_values, + name: "i8042 Aux Port", + phys: "isa0060/serio1", +}; + +/* + * i8042_interrupt() is the most important function in this driver - + * it handles the interrupts from the i8042, and sends incoming bytes + * to the upper layers. + */ + +static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + unsigned char str, data; + + spin_lock_irqsave(&i8042_lock, flags); + + while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) { + + data = inb(I8042_DATA_REG); + +#ifdef I8042_DEBUG_IO + printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s) [%d]\n", + data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", (int) (jiffies - i8042_start)); +#endif + + if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) { + if (i8042_aux_port.dev) + i8042_aux_port.dev->interrupt(&i8042_aux_port, data, 0); + } else { + if (i8042_kbd_values.exists && i8042_kbd_port.dev) { + if (!i8042_direct) { + if (data > 0x7f) { + if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) { + i8042_kbd_port.dev->interrupt(&i8042_kbd_port, 0xf0, 0); + data = i8042_unxlate_table[data & 0x7f]; + } + } else { + set_bit(data, i8042_unxlate_seen); + data = i8042_unxlate_table[data]; + } + } + i8042_kbd_port.dev->interrupt(&i8042_kbd_port, data, 0); + } + } + } + + spin_unlock_irqrestore(&i8042_lock, flags); +} + +/* + * i8042_controller init initializes the i8042 controller, and, + * most importantly, sets it into non-xlated mode. + */ + +static int __init i8042_controller_init(void) +{ + +/* + * Check the i/o region before we touch it. + */ +#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) + if (check_region(I8042_DATA_REG, 16)) { + printk(KERN_ERR "i8042.c: %#x port already in use!\n", I8042_DATA_REG); + return -1; + } +#endif + +/* + * Test the i8042. We need to know if it thinks it's working correctly + * before doing anything else. + */ + + i8042_flush(); + + if (i8042_reset) { + + unsigned char param; + + if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { + printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n"); + return -1; + } + + if (param != I8042_RET_CTL_TEST) { + printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n", + param, I8042_RET_CTL_TEST); + return -1; + } + } + +/* + * Read the CTR. + */ + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { + printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); + return -1; + } + +/* + * Save the CTR for restoral on unload / reboot. + */ + + i8042_initial_ctr = i8042_ctr; + +/* + * Disable both interfaces and their interrupts. + */ + + i8042_ctr |= I8042_CTR_KBDDIS; + i8042_ctr &= ~I8042_CTR_KBDINT; + +/* + * Handle keylock. + */ + + if (~inb(I8042_STATUS_REG) & I8042_STR_KEYLOCK) { + + if (i8042_unlock) { + i8042_ctr |= I8042_CTR_IGNKEYLOCK; + } else { + printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); + } + } + +/* + * If the chip is configured into nontranslated mode by the BIOS, don't + * bother enabling translating and just use that happily. + */ + + if (~i8042_ctr & I8042_CTR_XLATE) + i8042_direct = 1; + +/* + * Set nontranslated mode for the kbd interface if requested by an option. + * This is vital for a working scancode set 3 support. After this the kbd + * interface becomes a simple serial in/out, like the aux interface is. If + * the user doesn't wish this, the driver tries to untranslate the values + * after the i8042 translates them. + */ + + if (i8042_direct) + i8042_ctr &= ~I8042_CTR_XLATE; + +/* + * Write CTR back. + */ + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n"); + return -1; + } + + return 0; +} + +/* + * Here we try to reset everything back to a state in which the BIOS will be + * able to talk to the hardware when rebooting. + */ + +void i8042_controller_cleanup(void) +{ + +/* + * Reset the controller. + */ + + if (i8042_reset) { + unsigned char param; + + if (i8042_command(¶m, I8042_CMD_CTL_TEST)) + printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n"); + } + +/* + * Restore the original control register setting. + */ + + i8042_ctr = i8042_initial_ctr; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_WARNING "i8042.c: Can't restore CTR.\n"); + +/* + * Reset anything that is connected to the ports if the ports + * are enabled in the original config. + */ + + if (i8042_kbd_values.exists) + i8042_kbd_write(&i8042_kbd_port, 0xff); + + if (i8042_aux_values.exists) + i8042_aux_write(&i8042_aux_port, 0xff); +} + +/* + * i8042_check_aux() applies as much paranoia as it can at detecting + * the presence of an AUX interface. + */ + +static int __init i8042_check_aux(struct i8042_values *values, struct serio *port) +{ + unsigned char param; + + i8042_flush(); + +/* + * Internal loopback test - filters out AT-type i8042's + */ + + param = 0x5a; + + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa5) + return -1; + +/* + * External connection test - filters out AT-soldered PS/2 i8042's + */ + + if (i8042_command(¶m, I8042_CMD_AUX_TEST) || param) + return -1; + +/* + * Bit assignment test - filters out PS/2 i8042's in AT mode + */ + + if (i8042_command(¶m, I8042_CMD_AUX_DISABLE)) + return -1; + + if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS)) + return -1; + + if (i8042_command(¶m, I8042_CMD_AUX_TEST) || param) { + +/* + * We've got an old AMI i8042 with 'Bad Cache' commands. + */ + + i8042_command(¶m, I8042_CMD_AUX_ENABLE); + return -1; + } + + if (i8042_command(¶m, I8042_CMD_AUX_ENABLE)) + return -1; + + if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS)) + return -1; + +/* + * Disable the interface. + */ + + i8042_ctr |= I8042_CTR_AUXDIS; + i8042_ctr &= ~I8042_CTR_AUXINT; + + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + return -1; + + return 0; +} + +/* + * i8042_port_register() marks the device as existing, + * registers it, and reports to the user. + */ + +static int __init i8042_port_register(struct i8042_values *values, struct serio *port) +{ + values->exists = 1; + serio_register_port(port); + printk(KERN_INFO "serio: i8042 %s port at %#x,%#x irq %d\n", + values->name, I8042_DATA_REG, I8042_COMMAND_REG, values->irq); + + return 0; +} + +/* + * Module init and cleanup functions. + */ + +void __init i8042_setup(char *str, int *ints) +{ + if (!strcmp(str, "i8042_reset=1")) + i8042_reset = 1; + if (!strcmp(str, "i8042_noaux=1")) + i8042_noaux = 1; + if (!strcmp(str, "i8042_unlock=1")) + i8042_unlock = 1; + if (!strcmp(str, "i8042_direct=1")) + i8042_direct = 1; +} + +/* + * Reset the 8042 back to original mode. + */ +static int i8042_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) + i8042_controller_cleanup(); + return NOTIFY_DONE; +} + +static struct notifier_block i8042_notifier= +{ + i8042_notify_sys, + NULL, + 0 +}; + +int __init i8042_init(void) +{ +#ifdef I8042_DEBUG_IO + i8042_start = jiffies; +#endif + + if (i8042_controller_init()) + return -ENODEV; + + i8042_port_register(&i8042_kbd_values, &i8042_kbd_port); + + if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port)) + i8042_port_register(&i8042_aux_values, &i8042_aux_port); + +/* + * On ix86 platforms touching the i8042 data register region can do really + * bad things. Because of this the region is always reserved on ix86 boxes. + */ +#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) + request_region(I8042_DATA_REG, 16, "i8042"); +#endif + register_reboot_notifier(&i8042_notifier); + return 0; +} + +void __exit i8042_exit(void) +{ + unregister_reboot_notifier(&i8042_notifier); + + if (i8042_kbd_values.exists) + serio_unregister_port(&i8042_kbd_port); + + if (i8042_aux_values.exists) + serio_unregister_port(&i8042_aux_port); + + i8042_controller_cleanup(); +#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) + release_region(I8042_DATA_REG, 16); +#endif +} + +module_init(i8042_init); +module_exit(i8042_exit); diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h new file mode 100644 index 000000000000..569f81942f50 --- /dev/null +++ b/drivers/input/serio/i8042.h @@ -0,0 +1,128 @@ +#ifndef _I8042_H +#define _I8042_H + +/* + * $Id: i8042.h,v 1.6 2001/10/05 22:48:09 vojtech Exp $ + * + * Copyright (c) 1999-2001 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 + * + * 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 + */ + +/* + * If you want to reset your i8042 upon boot, define this. + */ + +#undef I8042_RESET + +/* + * If you want to trace all the i/o the i8042 module does for + * debugging purposes, define this. + */ + +#undef I8042_DEBUG_IO + +/* + * On most PC based systems the keyboard IRQ is 1. + */ + +#define I8042_KBD_IRQ CONFIG_I8042_KBD_IRQ + +/* + * On most PC based systems the aux port IRQ is 12. There are exceptions, + * though. Unfortunately IRQ probing is not possible without touching + * the device attached to the port. + */ + +#define I8042_AUX_IRQ CONFIG_I8042_AUX_IRQ + +/* + * This is in 50us units, the time we wait for the i8042 to react. This + * has to be long enough for the i8042 itself to timeout on sending a byte + * to a non-existent mouse. + */ + +#define I8042_CTL_TIMEOUT 10000 + +/* + * Register numbers. + */ + +#define I8042_COMMAND_REG CONFIG_I8042_REG_BASE + 4 +#define I8042_STATUS_REG CONFIG_I8042_REG_BASE + 4 +#define I8042_DATA_REG CONFIG_I8042_REG_BASE + +/* + * Status register bits. + */ + +#define I8042_STR_PARITY 0x80 +#define I8042_STR_TIMEOUT 0x40 +#define I8042_STR_AUXDATA 0x20 +#define I8042_STR_KEYLOCK 0x10 +#define I8042_STR_CMDDAT 0x08 +#define I8042_STR_IBF 0x02 +#define I8042_STR_OBF 0x01 + +/* + * Control register bits. + */ + +#define I8042_CTR_KBDINT 0x01 +#define I8042_CTR_AUXINT 0x02 +#define I8042_CTR_IGNKEYLOCK 0x08 +#define I8042_CTR_KBDDIS 0x10 +#define I8042_CTR_AUXDIS 0x20 +#define I8042_CTR_XLATE 0x40 + +/* + * Commands. + */ + +#define I8042_CMD_CTL_RCTR 0x0120 +#define I8042_CMD_CTL_WCTR 0x1060 +#define I8042_CMD_CTL_TEST 0x01aa + +#define I8042_CMD_KBD_DISABLE 0x00ad +#define I8042_CMD_KBD_ENABLE 0x00ae +#define I8042_CMD_KBD_TEST 0x01ab +#define I8042_CMD_KBD_LOOP 0x11d2 + +#define I8042_CMD_AUX_DISABLE 0x00a7 +#define I8042_CMD_AUX_ENABLE 0x00a8 +#define I8042_CMD_AUX_TEST 0x01a9 +#define I8042_CMD_AUX_SEND 0x10d4 +#define I8042_CMD_AUX_LOOP 0x11d3 + +/* + * Return codes. + */ + +#define I8042_RET_CTL_TEST 0x55 + +/* + * Expected maximum internal i8042 buffer size. This is used for flushing + * the i8042 buffers. 32 should be more than enough. + */ + +#define I8042_BUFFER_SIZE 32 + +#endif /* _I8042_H */ diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c new file mode 100644 index 000000000000..18e8c51256f4 --- /dev/null +++ b/drivers/input/serio/parkbd.c @@ -0,0 +1,203 @@ +/* + * $Id: parkbd.c,v 1.10 2002/03/13 10:09:20 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * Parallel port to Keyboard port adapter 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/parport.h> +#include <linux/init.h> +#include <linux/serio.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(parkbd, "1i"); +MODULE_PARM(parkbd_mode, "1i"); + +#define PARKBD_CLOCK 0x01 /* Strobe & Ack */ +#define PARKBD_DATA 0x02 /* AutoFd & Busy */ + +static int parkbd = 0; +static int parkbd_mode = SERIO_8042; + +static int parkbd_buffer = 0; +static int parkbd_counter = 0; +static int parkbd_last = 0; +static int parkbd_writing = 0; +static unsigned long parkbd_start = 0; + +static struct pardevice *parkbd_dev; + +static char parkbd_name[] = "PARKBD AT/XT keyboard adapter"; +static char parkbd_phys[32]; + +static int parkbd_readlines(void) +{ + return (parport_read_status(parkbd_dev->port) >> 6) ^ 2; +} + +static void parkbd_writelines(int data) +{ + parport_write_control(parkbd_dev->port, (~data & 3) | 0x10); +} + +static int parkbd_write(struct serio *port, unsigned char c) +{ + unsigned char p; + + if (!parkbd_mode) return -1; + + p = c ^ (c >> 4); + p = p ^ (p >> 2); + p = p ^ (p >> 1); + + parkbd_counter = 0; + parkbd_writing = 1; + parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600; + + parkbd_writelines(2); + + return 0; +} + +static int parkbd_open(struct serio *port) +{ + return 0; +} + +static void parkbd_close(struct serio *port) +{ +} + +static struct serio parkbd_port = +{ + write: parkbd_write, + open: parkbd_open, + close: parkbd_close, + name: parkbd_name, + phys: parkbd_phys, +}; + +static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + + if (parkbd_writing) { + + if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) { + parkbd_counter = 0; + parkbd_buffer = 0; + parkbd_writing = 0; + parkbd_writelines(3); + return; + } + + parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2); + + if (parkbd_counter == 11) { + parkbd_counter = 0; + parkbd_buffer = 0; + parkbd_writing = 0; + parkbd_writelines(3); + } + + } else { + + if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) { + parkbd_counter = 0; + parkbd_buffer = 0; + } + + parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++; + + if (parkbd_counter == parkbd_mode + 10) { + if (parkbd_port.dev) + parkbd_port.dev->interrupt(&parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0); + } + } + + parkbd_last = jiffies; +} + +static int parkbd_getport(void) +{ + struct parport *pp; + + if (parkbd < 0) { + printk(KERN_ERR "parkbd: no port specified\n"); + return -ENODEV; + } + + for (pp = parport_enumerate(); pp != NULL && (parkbd > 0); pp = pp->next) parkbd--; + + if (pp == NULL) { + printk(KERN_ERR "parkbd: no such parport\n"); + return -ENODEV; + } + + parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL); + + if (!parkbd_dev) + return -ENODEV; + + if (parport_claim(parkbd_dev)) { + parport_unregister_device(parkbd_dev); + return -EBUSY; + } + + parkbd_start = jiffies; + + return 0; +} + + +int __init parkbd_init(void) +{ + if (parkbd_getport()) return -1; + parkbd_writelines(3); + parkbd_port.type = parkbd_mode; + + sprintf(parkbd_phys, "%s/serio0", parkbd_dev->port->name); + + serio_register_port(&parkbd_port); + + printk(KERN_INFO "serio: PARKBD %s adapter on %s\n", + parkbd_mode ? "AT" : "XT", parkbd_dev->port->name); + + return 0; +} + +void __exit parkbd_exit(void) +{ + parport_release(parkbd_dev); + serio_unregister_port(&parkbd_port); + parport_unregister_device(parkbd_dev); +} + +module_init(parkbd_init); +module_exit(parkbd_exit); diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c new file mode 100644 index 000000000000..a90879b13047 --- /dev/null +++ b/drivers/input/serio/rpckbd.c @@ -0,0 +1,117 @@ +/* + * $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $ + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * unknown author + */ + +/* + * Acorn RiscPC PS/2 keyboard controller driver for Linux/ARM + */ + +/* + * 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/serio.h> + +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/iomd.h> +#include <asm/system.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); +MODULE_LICENSE("GPL"); + +static inline void rpckbd_write(unsigned char val) +{ + while(!(inb(IOMD_KCTRL) & (1 << 7))); + outb(val, IOMD_KARTTX); +} + +static struct serio rpckbd_port = +{ + type: SERIO_8042, + write: rpckbd_write, + name: "RiscPC PS/2 kbd port", + phys: "rpckbd/serio0", +}; + +static void rpckbd_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + kbd_pt_regs = regs; + + while (inb(IOMD_KCTRL) & (1 << 5)) + if (rpckbd_port.dev) + rpckbd_port.dev->interrupt(&rpckbd_port, inb(IOMD_KARTRX), 0); + +} + +static void rpckbd_tx(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int __init rpckbd_init(void) +{ + unsigned long flags; + + /* Reset the keyboard state machine. */ + outb(0, IOMD_KCTRL); + outb(8, IOMD_KCTRL); + + save_flags_cli(flags); + + if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", NULL) != 0) { + printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ!\n") + return -EBUSY; + } + + if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", NULL) != 0) { + printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ!\n") + free_irq(IRQ_KEYBOARDRX, NULL); + return -EBUSY; + } + + disable_irq(IRQ_KEYBOARDTX); + (void)IOMD_KARTRX; + + restore_flags(flags); + + register_serio_port(&rpckbd_port); + printk(KERN_INFO "serio: RiscPC PS/2 kbd port irq %d %d\n", IRQ_KEYBOARDRX, IRQ_KEYBOARDTX); + + return 0; +} + +static void __exit rpckbd_exit(void) +{ + free_irq(IRQ_KEYBOARDRX, NULL); + free_irq(IRQ_KEYBOARDTX, NULL); + + unregister_serio_port(&rpckbd_port); +} + +module_init(rpckbd_init); +module_exit(rpckbd_exit); diff --git a/drivers/input/touchscreen/Config.help b/drivers/input/touchscreen/Config.help new file mode 100644 index 000000000000..d31ba3326e0a --- /dev/null +++ b/drivers/input/touchscreen/Config.help @@ -0,0 +1,16 @@ +CONFIG_INPUT_TOUCHSCREEN + Say Y here, and a list of supported touchscreens will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +CONFIG_TOUCHSCREEN_GUNZE + Say Y here if you have the Gunze AHL-51 touchscreen connected to + your system. + + If unsure, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called gunze.o. If you want to compile it as a + module, say M here and read <file:Documentation/modules.txt>. diff --git a/drivers/input/touchscreen/Config.in b/drivers/input/touchscreen/Config.in new file mode 100644 index 000000000000..f48cbd5e2371 --- /dev/null +++ b/drivers/input/touchscreen/Config.in @@ -0,0 +1,7 @@ +# +# Mouse driver configuration +# + +bool 'Touchscreens' CONFIG_INPUT_TOUCHSCREEN + +dep_tristate ' Gunze AHL-51S touchscreen' CONFIG_TOUCHSCREEN_GUNZE $CONFIG_INPUT $CONFIG_INPUT_TOUCHSCREEN $CONFIG_SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile new file mode 100644 index 000000000000..d36ec4de1b15 --- /dev/null +++ b/drivers/input/touchscreen/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the mouse drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o + +# The global Rules.make. + +include $(TOPDIR)/Rules.make diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c new file mode 100644 index 000000000000..8ea3050692e9 --- /dev/null +++ b/drivers/input/touchscreen/gunze.c @@ -0,0 +1,178 @@ +/* + * $Id: gunze.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ + * + * Copyright (c) 2000-2001 Vojtech Pavlik + */ + +/* + * Gunze AHL-51S touchscreen 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/errno.h> +#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_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Gunze AHL-51S touchscreen driver"); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define GUNZE_MAX_LENGTH 10 + +static char *gunze_name = "Gunze AHL-51S TouchScreen"; + +/* + * Per-touchscreen data. + */ + +struct gunze { + struct input_dev dev; + struct serio *serio; + int idx; + unsigned char data[GUNZE_MAX_LENGTH]; + char phys[32]; +}; + +static void gunze_process_packet(struct gunze* gunze) +{ + struct input_dev *dev = &gunze->dev; + + if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' || + (gunze->data[0] != 'T' && gunze->data[0] != 'R')) { + gunze->data[10] = 0; + printk(KERN_WARNING "gunze.c: bad packet: >%s<\n", gunze->data); + return; + } + + input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10) * 4); + input_report_abs(dev, ABS_Y, 3072 - simple_strtoul(gunze->data + 6, NULL, 10) * 3); + input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T'); +} + +static void gunze_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct gunze* gunze = serio->private; + + if (data == '\r') { + gunze_process_packet(gunze); + gunze->idx = 0; + } else { + if (gunze->idx < GUNZE_MAX_LENGTH) + gunze->data[gunze->idx++] = data; + } +} + +/* + * gunze_disconnect() is the opposite of gunze_connect() + */ + +static void gunze_disconnect(struct serio *serio) +{ + struct gunze* gunze = serio->private; + input_unregister_device(&gunze->dev); + serio_close(serio); + kfree(gunze); +} + +/* + * gunze_connect() is the routine that is called when someone adds a + * new serio device. It looks whether it was registered as a Gunze touchscreen + * and if yes, registers it as an input device. + */ + +static void gunze_connect(struct serio *serio, struct serio_dev *dev) +{ + struct gunze *gunze; + + if (serio->type != (SERIO_RS232 | SERIO_GUNZE)) + return; + + if (!(gunze = kmalloc(sizeof(struct gunze), GFP_KERNEL))) + return; + + memset(gunze, 0, sizeof(struct gunze)); + + gunze->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + gunze->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + gunze->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + gunze->dev.absmin[ABS_X] = 96; gunze->dev.absmin[ABS_Y] = 72; + gunze->dev.absmax[ABS_X] = 4000; gunze->dev.absmax[ABS_Y] = 3000; + + gunze->serio = serio; + serio->private = gunze; + + sprintf(gunze->phys, "%s/input0", serio->phys); + + gunze->dev.private = gunze; + gunze->dev.name = gunze_name; + gunze->dev.phys = gunze->phys; + gunze->dev.idbus = BUS_RS232; + gunze->dev.idvendor = SERIO_GUNZE; + gunze->dev.idproduct = 0x0051; + gunze->dev.idversion = 0x0100; + + if (serio_open(serio, dev)) { + kfree(gunze); + return; + } + + input_register_device(&gunze->dev); + + printk(KERN_INFO "input: %s on %s\n", gunze_name, serio->phys); +} + +/* + * The serio device structure. + */ + +static struct serio_dev gunze_dev = { + interrupt: gunze_interrupt, + connect: gunze_connect, + disconnect: gunze_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init gunze_init(void) +{ + serio_register_device(&gunze_dev); + return 0; +} + +void __exit gunze_exit(void) +{ + serio_unregister_device(&gunze_dev); +} + +module_init(gunze_init); +module_exit(gunze_exit); diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c new file mode 100644 index 000000000000..50d238711aab --- /dev/null +++ b/drivers/input/tsdev.c @@ -0,0 +1,443 @@ +/* + * $Id: tsdev.c,v 1.15 2002/04/10 16:50:19 jsimmons Exp $ + * + * Copyright (c) 2001 "Crazy" james Simmons + * + * Input driver to Touchscreen device driver module. + * + * Sponsored by Transvirtual Technology + */ + +/* + * 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 <jsimmons@transvirtual.com>. + */ + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINORS 32 +#define TSDEV_BUFFER_SIZE 64 + +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/config.h> +#include <linux/smp_lock.h> +#include <linux/random.h> +#include <linux/time.h> + +#ifndef CONFIG_INPUT_TSDEV_SCREEN_X +#define CONFIG_INPUT_TSDEV_SCREEN_X 240 +#endif +#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y +#define CONFIG_INPUT_TSDEV_SCREEN_Y 320 +#endif + +struct tsdev { + int exist; + int open; + int minor; + char name[16]; + wait_queue_head_t wait; + struct tsdev_list *list; + struct input_handle handle; + devfs_handle_t devfs; +}; + +/* From Compaq's Touch Screen Specification version 0.2 (draft) */ +typedef struct { + short pressure; + short x; + short y; + short millisecs; +} TS_EVENT; + +struct tsdev_list { + struct fasync_struct *fasync; + struct tsdev_list *next; + struct tsdev *tsdev; + int head, tail; + int oldx, oldy, pendown; + TS_EVENT event[TSDEV_BUFFER_SIZE]; +}; + +static struct input_handler tsdev_handler; + +static struct tsdev *tsdev_table[TSDEV_MINORS]; + +static int xres = CONFIG_INPUT_TSDEV_SCREEN_X; +static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y; + +static int tsdev_fasync(int fd, struct file *file, int on) +{ + struct tsdev_list *list = file->private_data; + int retval; + + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int tsdev_open(struct inode *inode, struct file *file) +{ + int i = minor(inode->i_rdev) - TSDEV_MINOR_BASE; + struct tsdev_list *list; + + if (i >= TSDEV_MINORS || !tsdev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL))) + return -ENOMEM; + memset(list, 0, sizeof(struct tsdev_list)); + + list->tsdev = tsdev_table[i]; + list->next = tsdev_table[i]->list; + tsdev_table[i]->list = list; + file->private_data = list; + + if (!list->tsdev->open++) + if (list->tsdev->exist) + input_open_device(&list->tsdev->handle); + return 0; +} + +static int tsdev_release(struct inode *inode, struct file *file) +{ + struct tsdev_list *list = file->private_data; + struct tsdev_list **listptr; + + listptr = &list->tsdev->list; + tsdev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->tsdev->open) { + if (list->tsdev->exist) { + input_close_device(&list->tsdev->handle); + } else { + input_unregister_minor(list->tsdev->devfs); + tsdev_table[list->tsdev->minor] = NULL; + kfree(list->tsdev); + } + } + kfree(list); + return 0; +} + +static ssize_t tsdev_read(struct file *file, char *buffer, size_t count, + loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct tsdev_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + add_wait_queue(&list->tsdev->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list->head == list->tail) { + if (!list->tsdev->exist) { + retval = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->tsdev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail + && retval + sizeof(TS_EVENT) <= count) { + if (copy_to_user + (buffer + retval, list->event + list->tail, + sizeof(TS_EVENT))) + return -EFAULT; + list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); + retval += sizeof(TS_EVENT); + } + return retval; +} + +/* No kernel lock - fine */ +static unsigned int tsdev_poll(struct file *file, poll_table * wait) +{ + struct tsdev_list *list = file->private_data; + + poll_wait(file, &list->tsdev->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + return 0; +} + +static int tsdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ +/* + struct tsdev_list *list = file->private_data; + struct tsdev *evdev = list->tsdev; + struct input_dev *dev = tsdev->handle.dev; + int retval; + + switch (cmd) { + case HHEHE: + return 0; + case hjff: + return 0; + default: + return 0; + } +*/ + return -EINVAL; +} + +struct file_operations tsdev_fops = { + owner: THIS_MODULE, + open: tsdev_open, + release: tsdev_release, + read: tsdev_read, + poll: tsdev_poll, + fasync: tsdev_fasync, + ioctl: tsdev_ioctl, +}; + +static void tsdev_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct tsdev *tsdev = handle->private; + struct tsdev_list *list = tsdev->list; + struct timeval time; + int size; + + while (list) { + switch (type) { + case EV_ABS: + switch (code) { + case ABS_X: + if (!list->pendown) + return; + + size = + handle->dev->absmax[ABS_X] - + handle->dev->absmin[ABS_X]; + if (size > 0) + list->oldx = + ((value - + handle->dev->absmin[ABS_X]) * + xres / size); + else + list->oldx = + ((value - + handle->dev->absmin[ABS_X])); + break; + case ABS_Y: + if (!list->pendown) + return; + + size = + handle->dev->absmax[ABS_Y] - + handle->dev->absmin[ABS_Y]; + if (size > 0) + list->oldy = + ((value - + handle->dev->absmin[ABS_Y]) * + yres / size); + else + list->oldy = + ((value - + handle->dev->absmin[ABS_Y])); + break; + case ABS_PRESSURE: + list->pendown = + ((value > + handle->dev-> + absmin[ABS_PRESSURE])) ? value - + handle->dev->absmin[ABS_PRESSURE] : 0; + break; + } + break; + + case EV_REL: + switch (code) { + case REL_X: + if (!list->pendown) + return; + + list->oldx += value; + if (list->oldx < 0) + list->oldx = 0; + else if (list->oldx > xres) + list->oldx = xres; + break; + case REL_Y: + if (!list->pendown) + return; + + list->oldy += value; + if (list->oldy < 0) + list->oldy = 0; + else if (list->oldy > xres) + list->oldy = xres; + break; + } + break; + + case EV_KEY: + if (code == BTN_TOUCH || code == BTN_MOUSE) { + switch (value) { + case 0: + list->pendown = 0; + break; + case 1: + if (!list->pendown) + list->pendown = 1; + break; + case 2: + return; + } + } else + return; + break; + } + do_gettimeofday(&time); + list->event[list->head].millisecs = time.tv_usec / 100; + list->event[list->head].pressure = list->pendown; + list->event[list->head].x = list->oldx; + list->event[list->head].y = list->oldy; + list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + list = list->next; + } + wake_up_interruptible(&tsdev->wait); +} + +static struct input_handle *tsdev_connect(struct input_handler *handler, + struct input_dev *dev, + struct input_device_id *id) +{ + struct tsdev *tsdev; + int minor; + + for (minor = 0; minor < TSDEV_MINORS && tsdev_table[minor]; + minor++); + if (minor == TSDEV_MINORS) { + printk(KERN_ERR + "tsdev: You have way too many touchscreens\n"); + return NULL; + } + + if (!(tsdev = kmalloc(sizeof(struct tsdev), GFP_KERNEL))) + return NULL; + memset(tsdev, 0, sizeof(struct tsdev)); + init_waitqueue_head(&tsdev->wait); + + tsdev->minor = minor; + tsdev_table[minor] = tsdev; + sprintf(tsdev->name, "ts%d", minor); + + tsdev->handle.dev = dev; + tsdev->handle.name = tsdev->name; + tsdev->handle.handler = handler; + tsdev->handle.private = tsdev; + + tsdev->devfs = + input_register_minor("ts%d", minor, TSDEV_MINOR_BASE); + + tsdev->exist = 1; + + return &tsdev->handle; +} + +static void tsdev_disconnect(struct input_handle *handle) +{ + struct tsdev *tsdev = handle->private; + + tsdev->exist = 0; + + if (tsdev->open) { + input_close_device(handle); + wake_up_interruptible(&tsdev->wait); + } else { + input_unregister_minor(tsdev->devfs); + tsdev_table[tsdev->minor] = NULL; + kfree(tsdev); + } +} + +static struct input_device_id tsdev_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_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 */ + + {},/* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, tsdev_ids); + +static struct input_handler tsdev_handler = { + event: tsdev_event, + connect: tsdev_connect, + disconnect: tsdev_disconnect, + fops: &tsdev_fops, + minor: TSDEV_MINOR_BASE, + name: "tsdev", + id_table: tsdev_ids, +}; + +static int __init tsdev_init(void) +{ + input_register_handler(&tsdev_handler); + printk(KERN_INFO "ts: Compaq touchscreen protocol output\n"); + return 0; +} + +static void __exit tsdev_exit(void) +{ + input_unregister_handler(&tsdev_handler); +} + +module_init(tsdev_init); +module_exit(tsdev_exit); + +MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>"); +MODULE_DESCRIPTION("Input driver to touchscreen converter"); +MODULE_PARM(xres, "i"); +MODULE_PARM_DESC(xres, "Horizontal screen resolution"); +MODULE_PARM(yres, "i"); +MODULE_PARM_DESC(yres, "Vertical screen resolution"); diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c index 5021b597997d..baf4b52dfe61 100644 --- a/drivers/isdn/capi/capifs.c +++ b/drivers/isdn/capi/capifs.c @@ -221,11 +221,9 @@ static void capifs_put_super(struct super_block *sb) kfree(sbi); } -static int capifs_statfs(struct super_block *sb, struct statfs *buf); - static struct super_operations capifs_sops = { put_super: capifs_put_super, - statfs: capifs_statfs, + statfs: simple_statfs, }; static int capifs_parse_options(char *options, struct capifs_sb_info *sbi) @@ -354,19 +352,6 @@ fail: return -EINVAL; } -static int capifs_statfs(struct super_block *sb, struct statfs *buf) -{ - buf->f_type = CAPIFS_SUPER_MAGIC; - buf->f_bsize = 1024; - buf->f_blocks = 0; - buf->f_bfree = 0; - buf->f_bavail = 0; - buf->f_files = 0; - buf->f_ffree = 0; - buf->f_namelen = NAME_MAX; - return 0; -} - static struct inode *capifs_new_inode(struct super_block *sb) { struct inode *inode = new_inode(sb); diff --git a/drivers/message/fusion/Makefile b/drivers/message/fusion/Makefile index c5298d750522..538032badea0 100644 --- a/drivers/message/fusion/Makefile +++ b/drivers/message/fusion/Makefile @@ -51,9 +51,3 @@ obj-$(CONFIG_FUSION_CTL) += mptctl.o obj-$(CONFIG_FUSION_LAN) += mptlan.o include $(TOPDIR)/Rules.make - - -# EXP... -## Fusion MPT extra's... -##mptscsih.o: $(mptscsih-objs) -## $(LD) -r -o $@ $(mptscsih-objs) diff --git a/drivers/net/hamradio/soundmodem/Makefile b/drivers/net/hamradio/soundmodem/Makefile index 4698281fdeb3..a3fe3f4e61cd 100644 --- a/drivers/net/hamradio/soundmodem/Makefile +++ b/drivers/net/hamradio/soundmodem/Makefile @@ -32,5 +32,5 @@ $(obj)/sm_hapn4800.o: $(obj)/sm_tbl_hapn4800.h $(obj)/sm_fsk9600.o: $(obj)/sm_tbl_fsk9600.h $(obj)/sm_tbl_%: $(obj)/gentbl - $< + $(obj)/gentbl diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index ad14e1ba25f7..be8f9cee7e88 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -596,6 +596,7 @@ ips_setup(char *ips_str, int *dummy) { } return (1); +} __setup("ips=", ips_setup); @@ -632,11 +633,10 @@ __setup("ips=", ips_setup); } } } +} #endif -} - /****************************************************************************/ /* */ /* Routine Name: ips_detect */ diff --git a/drivers/tc/Makefile b/drivers/tc/Makefile index 9ba601d0984b..c7f242db75cc 100644 --- a/drivers/tc/Makefile +++ b/drivers/tc/Makefile @@ -15,5 +15,16 @@ obj-$(CONFIG_VT) += lk201.o lk201-map.o lk201-remap.o include $(TOPDIR)/Rules.make -$(obj)/lk201-map.c: $(src)/lk201-map.map +$(obj)/lk201-map.o: $(obj)/lk201-map.c + +# Uncomment if you're changing the keymap and have an appropriate +# loadkeys version for the map. By default, we'll use the shipped +# versions. +# GENERATE_KEYMAP := 1 + +ifdef GENERATE_KEYMAP + +$(obj)/lk201-map.c: $(obj)/%.c: $(src)/%.map loadkeys --mktable $< > $@ + +endif diff --git a/drivers/tc/lk201-map.c_shipped b/drivers/tc/lk201-map.c_shipped new file mode 100644 index 000000000000..a9df8f5bf62b --- /dev/null +++ b/drivers/tc/lk201-map.c_shipped @@ -0,0 +1,265 @@ + +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include <linux/types.h> +#include <linux/keyboard.h> +#include <linux/kd.h> + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, + 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf111, 0xf112, 0xf113, 0xf060, 0xf031, 0xf032, + 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, 0xf039, 0xf030, + 0xf02d, 0xf03d, 0xf07f, 0xf114, 0xf115, 0xf116, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf009, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, + 0xfb79, 0xfb75, 0xfb69, 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, + 0xf117, 0xf118, 0xf119, 0xf307, 0xf308, 0xf309, 0xf30b, 0xf702, + 0xf207, 0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, + 0xfb6b, 0xfb6c, 0xf03b, 0xf027, 0xf05c, 0xf603, 0xf304, 0xf305, + 0xf306, 0xf200, 0xf700, 0xf03e, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, + 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf200, 0xf601, + 0xf600, 0xf602, 0xf301, 0xf302, 0xf303, 0xf30e, 0xf200, 0xf703, + 0xf020, 0xf200, 0xf200, 0xf300, 0xf310, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short shift_map[NR_KEYS] = { + 0xf200, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, + 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf203, + 0xf11c, 0xf110, 0xf111, 0xf112, 0xf113, 0xf07e, 0xf021, 0xf040, + 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, 0xf028, 0xf029, + 0xf05f, 0xf02b, 0xf07f, 0xf114, 0xf115, 0xf116, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf009, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, + 0xfb59, 0xfb55, 0xfb49, 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, + 0xf117, 0xf20b, 0xf20a, 0xf307, 0xf308, 0xf309, 0xf30b, 0xf702, + 0xf207, 0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, + 0xfb4b, 0xfb4c, 0xf03a, 0xf022, 0xf07c, 0xf603, 0xf304, 0xf305, + 0xf306, 0xf200, 0xf700, 0xf03c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, + 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf200, 0xf601, + 0xf600, 0xf602, 0xf301, 0xf302, 0xf303, 0xf30e, 0xf200, 0xf703, + 0xf020, 0xf200, 0xf200, 0xf300, 0xf310, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, + 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf202, + 0xf11c, 0xf110, 0xf111, 0xf112, 0xf113, 0xf200, 0xf200, 0xf040, + 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, 0xf05b, 0xf05d, 0xf07d, + 0xf05c, 0xf200, 0xf200, 0xf114, 0xf115, 0xf116, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, + 0xfb79, 0xfb75, 0xfb69, 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, + 0xf117, 0xf118, 0xf119, 0xf911, 0xf912, 0xf913, 0xf30b, 0xf702, + 0xf207, 0xf914, 0xfb73, 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, + 0xfb6b, 0xfb6c, 0xf200, 0xf200, 0xf200, 0xf603, 0xf90e, 0xf90f, + 0xf910, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76, + 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf601, + 0xf600, 0xf602, 0xf90b, 0xf90c, 0xf90d, 0xf30e, 0xf200, 0xf703, + 0xf200, 0xf200, 0xf200, 0xf90a, 0xf310, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, + 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf204, + 0xf11c, 0xf110, 0xf111, 0xf112, 0xf113, 0xf81b, 0xf200, 0xf000, + 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, 0xf200, 0xf200, + 0xf01f, 0xf200, 0xf008, 0xf114, 0xf115, 0xf116, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, + 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, + 0xf117, 0xf118, 0xf119, 0xf307, 0xf308, 0xf309, 0xf30b, 0xf702, + 0xf207, 0xf001, 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, + 0xf00b, 0xf00c, 0xf200, 0xf007, 0xf01c, 0xf603, 0xf304, 0xf305, + 0xf306, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf200, 0xf601, + 0xf600, 0xf602, 0xf301, 0xf302, 0xf303, 0xf30e, 0xf200, 0xf703, + 0xf000, 0xf200, 0xf200, 0xf300, 0xf310, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, + 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf200, + 0xf11c, 0xf110, 0xf111, 0xf112, 0xf113, 0xf200, 0xf200, 0xf000, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf01f, 0xf200, 0xf200, 0xf114, 0xf115, 0xf116, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, + 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, + 0xf117, 0xf118, 0xf119, 0xf307, 0xf308, 0xf309, 0xf30b, 0xf702, + 0xf207, 0xf001, 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, + 0xf00b, 0xf00c, 0xf200, 0xf200, 0xf200, 0xf603, 0xf304, 0xf305, + 0xf306, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf601, + 0xf600, 0xf602, 0xf301, 0xf302, 0xf303, 0xf30e, 0xf200, 0xf703, + 0xf200, 0xf200, 0xf200, 0xf300, 0xf310, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short alt_map[NR_KEYS] = { + 0xf200, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, + 0xf507, 0xf508, 0xf509, 0xf50a, 0xf50b, 0xf50c, 0xf50d, 0xf200, + 0xf11c, 0xf510, 0xf511, 0xf512, 0xf513, 0xf01b, 0xf831, 0xf832, + 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, 0xf839, 0xf830, + 0xf82d, 0xf83d, 0xf87f, 0xf114, 0xf115, 0xf116, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf809, 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, + 0xf879, 0xf875, 0xf869, 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, + 0xf117, 0xf118, 0xf119, 0xf907, 0xf908, 0xf909, 0xf30b, 0xf702, + 0xf207, 0xf861, 0xf873, 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, + 0xf86b, 0xf86c, 0xf83b, 0xf827, 0xf85c, 0xf603, 0xf904, 0xf905, + 0xf906, 0xf200, 0xf700, 0xf200, 0xf87a, 0xf878, 0xf863, 0xf876, + 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf200, 0xf210, + 0xf600, 0xf211, 0xf901, 0xf902, 0xf903, 0xf30e, 0xf200, 0xf703, + 0xf820, 0xf200, 0xf200, 0xf900, 0xf310, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, + 0xf507, 0xf508, 0xf509, 0xf50a, 0xf50b, 0xf50c, 0xf50d, 0xf200, + 0xf11c, 0xf510, 0xf511, 0xf512, 0xf513, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf114, 0xf115, 0xf20c, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, + 0xf819, 0xf815, 0xf809, 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, + 0xf117, 0xf118, 0xf119, 0xf307, 0xf308, 0xf309, 0xf30b, 0xf702, + 0xf207, 0xf801, 0xf813, 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, + 0xf80b, 0xf80c, 0xf200, 0xf200, 0xf200, 0xf603, 0xf304, 0xf305, + 0xf306, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf601, + 0xf600, 0xf602, 0xf301, 0xf302, 0xf303, 0xf30e, 0xf200, 0xf703, + 0xf200, 0xf200, 0xf200, 0xf300, 0xf20c, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0 +}; + +unsigned int keymap_count = 7; + + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', 'À'}, {'`', 'a', 'à'}, + {'\'', 'A', 'Á'}, {'\'', 'a', 'á'}, + {'^', 'A', 'Â'}, {'^', 'a', 'â'}, + {'~', 'A', 'Ã'}, {'~', 'a', 'ã'}, + {'"', 'A', 'Ä'}, {'"', 'a', 'ä'}, + {'O', 'A', 'Å'}, {'o', 'a', 'å'}, + {'0', 'A', 'Å'}, {'0', 'a', 'å'}, + {'A', 'A', 'Å'}, {'a', 'a', 'å'}, + {'A', 'E', 'Æ'}, {'a', 'e', 'æ'}, + {',', 'C', 'Ç'}, {',', 'c', 'ç'}, + {'`', 'E', 'È'}, {'`', 'e', 'è'}, + {'\'', 'E', 'É'}, {'\'', 'e', 'é'}, + {'^', 'E', 'Ê'}, {'^', 'e', 'ê'}, + {'"', 'E', 'Ë'}, {'"', 'e', 'ë'}, + {'`', 'I', 'Ì'}, {'`', 'i', 'ì'}, + {'\'', 'I', 'Í'}, {'\'', 'i', 'í'}, + {'^', 'I', 'Î'}, {'^', 'i', 'î'}, + {'"', 'I', 'Ï'}, {'"', 'i', 'ï'}, + {'-', 'D', 'Ð'}, {'-', 'd', 'ð'}, + {'~', 'N', 'Ñ'}, {'~', 'n', 'ñ'}, + {'`', 'O', 'Ò'}, {'`', 'o', 'ò'}, + {'\'', 'O', 'Ó'}, {'\'', 'o', 'ó'}, + {'^', 'O', 'Ô'}, {'^', 'o', 'ô'}, + {'~', 'O', 'Õ'}, {'~', 'o', 'õ'}, + {'"', 'O', 'Ö'}, {'"', 'o', 'ö'}, + {'/', 'O', 'Ø'}, {'/', 'o', 'ø'}, + {'`', 'U', 'Ù'}, {'`', 'u', 'ù'}, + {'\'', 'U', 'Ú'}, {'\'', 'u', 'ú'}, + {'^', 'U', 'Û'}, {'^', 'u', 'û'}, + {'"', 'U', 'Ü'}, {'"', 'u', 'ü'}, + {'\'', 'Y', 'Ý'}, {'\'', 'y', 'ý'}, + {'T', 'H', 'Þ'}, {'t', 'h', 'þ'}, + {'s', 's', 'ß'}, {'"', 'y', 'ÿ'}, + {'s', 'z', 'ß'}, {'i', 'j', 'ÿ'}, +}; + +unsigned int accent_table_size = 68; diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index b0a4836a4f9b..db068168efe2 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -1151,7 +1151,8 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum, endpoint = bulk_out_endpoint[0]; bluetooth->bulk_out_endpointAddress = endpoint->bEndpointAddress; - + bluetooth->bulk_out_buffer_size = endpoint->wMaxPacketSize * 2; + /* create our write urb pool */ for (i = 0; i < NUM_BULK_URBS; ++i) { struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); @@ -1166,8 +1167,6 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum, } bluetooth->write_urb_pool[i] = urb; } - - bluetooth->bulk_out_buffer_size = endpoint->wMaxPacketSize * 2; endpoint = interrupt_in_endpoint[0]; bluetooth->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); diff --git a/drivers/usb/class/printer.c b/drivers/usb/class/printer.c index 7689c3e9caed..cd9b52896fa5 100644 --- a/drivers/usb/class/printer.c +++ b/drivers/usb/class/printer.c @@ -372,7 +372,7 @@ static void usblp_cleanup (struct usblp *usblp) { devfs_unregister (usblp->devfs); usblp_table [usblp->minor] = NULL; - usb_deregister_dev (&usblp_driver, 1, usblp->minor); + usb_deregister_dev (1, usblp->minor); info("usblp%d: removed", usblp->minor); kfree (usblp->writeurb->transfer_buffer); @@ -812,20 +812,10 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum, init_waitqueue_head(&usblp->wait); usblp->ifnum = ifnum; - retval = usb_register_dev(&usblp_driver, 1, &usblp->minor); + retval = usb_register_dev(&usblp_fops, USBLP_MINOR_BASE, 1, &usblp->minor); if (retval) { - if (retval != -ENODEV) { - err("Not able to get a minor for this device."); - goto abort; - } - /* Look for a free usblp_table entry on our own. */ - while (usblp_table[usblp->minor]) { - usblp->minor++; - if (usblp->minor >= USBLP_MINORS) { - err("no more free usblp devices"); - goto abort; - } - } + err("Not able to get a minor for this device."); + goto abort; } usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL); @@ -901,7 +891,7 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum, return usblp; abort_minor: - usb_deregister_dev (&usblp_driver, 1, usblp->minor); + usb_deregister_dev (1, usblp->minor); abort: if (usblp) { usb_free_urb(usblp->writeurb); @@ -1111,9 +1101,6 @@ static struct usb_driver usblp_driver = { name: "usblp", probe: usblp_probe, disconnect: usblp_disconnect, - fops: &usblp_fops, - minor: USBLP_MINOR_BASE, - num_minors: USBLP_MINORS, id_table: usblp_ids, }; diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 506dfecc19d2..11a56285c9ee 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -2,17 +2,17 @@ # Makefile for USB Core files and filesystem # -export-objs := usb.o hcd.o hcd-pci.o urb.o message.o config.o +export-objs := usb.o hcd.o hcd-pci.o urb.o message.o config.o file.o usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \ - config.o + config.o file.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o endif ifeq ($(CONFIG_USB_DEVICEFS),y) - usbcore-objs += devio.o inode.o drivers.o devices.o + usbcore-objs += devio.o inode.o devices.o endif obj-$(CONFIG_USB) += usbcore.o diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c new file mode 100644 index 000000000000..eba43f1d84e5 --- /dev/null +++ b/drivers/usb/core/file.c @@ -0,0 +1,182 @@ +/* + * drivers/usb/file.c + * + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, + more docs, etc) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * (C) Copyright Greg Kroah-Hartman 2002 + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/spinlock.h> +#include <linux/errno.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include <linux/usb.h> + +devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ +EXPORT_SYMBOL(usb_devfs_handle); + +#define MAX_USB_MINORS 256 +static struct file_operations *usb_minors[MAX_USB_MINORS]; +static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED; + +static int usb_open(struct inode * inode, struct file * file) +{ + int minor = minor(inode->i_rdev); + struct file_operations *c; + int err = -ENODEV; + struct file_operations *old_fops, *new_fops = NULL; + + spin_lock (&minor_lock); + c = usb_minors[minor]; + spin_unlock (&minor_lock); + + if (!c || !(new_fops = fops_get(c))) + return err; + old_fops = file->f_op; + file->f_op = new_fops; + /* Curiouser and curiouser... NULL ->open() as "no device" ? */ + if (file->f_op->open) + err = file->f_op->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 usb_fops = { + owner: THIS_MODULE, + open: usb_open, +}; + +int usb_major_init(void) +{ + if (devfs_register_chrdev(USB_MAJOR, "usb", &usb_fops)) { + err("unable to get major %d for usb devices", USB_MAJOR); + return -EBUSY; + } + + usb_devfs_handle = devfs_mk_dir(NULL, "usb", NULL); + + return 0; +} + +void usb_major_cleanup(void) +{ + devfs_unregister(usb_devfs_handle); + devfs_unregister_chrdev(USB_MAJOR, "usb"); +} + +/** + * usb_register_dev - register a USB device, and ask for a minor number + * @fops: the file operations for this USB device + * @minor: the requested starting minor for this device. + * @num_minors: number of minor numbers requested for this device + * @start_minor: place to put the new starting minor number + * + * This should be called by all USB drivers that use the USB major number. + * If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be + * dynamically allocated out of the list of available ones. If it is not + * enabled, the minor number will be based on the next available free minor, + * starting at the requested @minor. + * + * usb_deregister_dev() must be called when the driver is done with + * the minor numbers given out by this function. + * + * Returns -EINVAL if something bad happens with trying to register a + * device, and 0 on success, alone with a value that the driver should + * use in start_minor. + */ +int usb_register_dev (struct file_operations *fops, int minor, int num_minors, int *start_minor) +{ + int i; + int j; + int good_spot; + int retval = -EINVAL; + +#ifdef CONFIG_USB_DYNAMIC_MINORS + /* + * We don't care what the device tries to start at, we want to start + * at zero to pack the devices into the smallest available space with + * no holes in the minor range. + */ + minor = 0; +#endif + + dbg ("asking for %d minors, starting at %d", num_minors, minor); + + if (fops == NULL) + goto exit; + + *start_minor = 0; + spin_lock (&minor_lock); + for (i = minor; i < MAX_USB_MINORS; ++i) { + if (usb_minors[i]) + continue; + + good_spot = 1; + for (j = 1; j <= num_minors-1; ++j) + if (usb_minors[i+j]) { + good_spot = 0; + break; + } + if (good_spot == 0) + continue; + + *start_minor = i; + dbg("found a minor chunk free, starting at %d", i); + for (i = *start_minor; i < (*start_minor + num_minors); ++i) + usb_minors[i] = fops; + + retval = 0; + goto exit; + } +exit: + spin_unlock (&minor_lock); + return retval; +} +EXPORT_SYMBOL(usb_register_dev); + +/** + * usb_deregister_dev - deregister a USB device's dynamic minor. + * @num_minors: number of minor numbers to put back. + * @start_minor: the starting minor number + * + * Used in conjunction with usb_register_dev(). This function is called + * when the USB driver is finished with the minor numbers gotten from a + * call to usb_register_dev() (usually when the device is disconnected + * from the system.) + * + * This should be called by all drivers that use the USB major number. + */ +void usb_deregister_dev (int num_minors, int start_minor) +{ + int i; + + dbg ("removing %d minors starting at %d", num_minors, start_minor); + + spin_lock (&minor_lock); + for (i = start_minor; i < (start_minor + num_minors); ++i) + usb_minors[i] = NULL; + spin_unlock (&minor_lock); +} +EXPORT_SYMBOL(usb_deregister_dev); + + diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 8d31640ff966..c637805a4705 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -48,7 +48,6 @@ static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED; static int mount_count; /* = 0 */ static struct dentry *devices_dentry; -static struct dentry *drivers_dentry; static int num_buses; /* = 0 */ static uid_t devuid; /* = 0 */ @@ -548,16 +547,6 @@ static int create_special_files (void) return -ENODEV; } - drivers_dentry = fs_create_file ("drivers", - listmode | S_IFREG, - NULL, NULL, - &usbdevfs_drivers_fops, - listuid, listgid); - if (drivers_dentry == NULL) { - err ("Unable to create drivers usbfs file"); - return -ENODEV; - } - return 0; } @@ -565,10 +554,7 @@ static void remove_special_files (void) { if (devices_dentry) fs_remove_file (devices_dentry); - if (drivers_dentry) - fs_remove_file (drivers_dentry); devices_dentry = NULL; - drivers_dentry = NULL; remove_mount(); } @@ -581,11 +567,6 @@ void usbfs_update_special (void) if (inode) inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; } - if (drivers_dentry) { - inode = devices_dentry->d_inode; - if (inode) - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - } } void usbfs_add_bus(struct usb_bus *bus) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4842fbf06737..10591d54aebb 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -30,7 +30,6 @@ #include <linux/interrupt.h> /* for in_interrupt() */ #include <linux/kmod.h> #include <linux/init.h> -#include <linux/devfs_fs_kernel.h> #include <linux/spinlock.h> #include <linux/errno.h> @@ -45,6 +44,8 @@ extern int usb_hub_init(void); extern void usb_hub_cleanup(void); +extern int usb_major_init(void); +extern void usb_major_cleanup(void); /* * Prototypes for the device driver probing/loading functions @@ -58,75 +59,23 @@ static void usb_check_support(struct usb_device *); */ LIST_HEAD(usb_driver_list); -devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ - -#define MAX_USB_MINORS 256 -static struct usb_driver *usb_minors[MAX_USB_MINORS]; -static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED; - -static int usb_register_minors (struct usb_driver *driver, int num_minors, int start_minor) -{ - int i; - - dbg("registering %d minors, starting at %d", num_minors, start_minor); - - if (start_minor + num_minors >= MAX_USB_MINORS) - return -EINVAL; - - spin_lock (&minor_lock); - for (i = start_minor; i < (start_minor + num_minors); ++i) - if (usb_minors[i]) { - spin_unlock (&minor_lock); - err("minor %d is already in use, error registering %s driver", - i, driver->name); - return -EINVAL; - } - - for (i = start_minor; i < (start_minor + num_minors); ++i) - usb_minors[i] = driver; - - spin_unlock (&minor_lock); - return 0; -} - -static void usb_deregister_minors (struct usb_driver *driver, int num_minors, int start_minor) -{ - int i; - - dbg ("%s is removing %d minors starting at %d", driver->name, - num_minors, start_minor); - - spin_lock (&minor_lock); - for (i = start_minor; i < (start_minor + num_minors); ++i) - usb_minors[i] = NULL; - spin_unlock (&minor_lock); -} /** - * usb_register - register a USB driver - * @new_driver: USB operations for the driver + * usb_register - register a USB driver + * @new_driver: USB operations for the driver * - * Registers a USB driver with the USB core. The list of unattached - * interfaces will be rescanned whenever a new driver is added, allowing - * the new driver to attach to any recognized devices. - * Returns a negative error code on failure and 0 on success. + * Registers a USB driver with the USB core. The list of unattached + * interfaces will be rescanned whenever a new driver is added, allowing + * the new driver to attach to any recognized devices. + * Returns a negative error code on failure and 0 on success. + * + * NOTE: if you want your driver to use the USB major number, you must call + * usb_register_dev() to enable that functionality. This function no longer + * takes care of that. */ int usb_register(struct usb_driver *new_driver) { int retval = 0; - - if ((new_driver->fops) && (new_driver->num_minors == 0)) { - err ("%s driver must specify num_minors", new_driver->name); - return -EINVAL; - } - -#ifndef CONFIG_USB_DYNAMIC_MINORS - if (new_driver->fops != NULL) { - retval = usb_register_minors (new_driver, new_driver->num_minors, new_driver->minor); - if (retval) - return retval; - } -#endif info("registered new driver %s", new_driver->name); @@ -144,92 +93,6 @@ int usb_register(struct usb_driver *new_driver) /** - * usb_register_dev - register a USB device, and ask for a minor number - * @new_driver: USB operations for the driver - * @num_minors: number of minor numbers requested for this device - * @start_minor: place to put the new starting minor number - * - * Used to ask the USB core for a new minor number for a device that has - * just showed up. This is used to dynamically allocate minor numbers - * from the pool of USB reserved minor numbers. - * - * This should be called by all drivers that use the USB major number. - * This only returns a good value of CONFIG_USB_DYNAMIC_MINORS is - * selected by the user. - * - * usb_deregister_dev() should be called when the driver is done with - * the minor numbers given out by this function. - * - * Returns -ENODEV if CONFIG_USB_DYNAMIC_MINORS is not enabled in this - * kernel, -EINVAL if something bad happens with trying to register a - * device, and 0 on success, alone with a value that the driver should - * use in start_minor. - */ -#ifdef CONFIG_USB_DYNAMIC_MINORS -int usb_register_dev (struct usb_driver *new_driver, int num_minors, int *start_minor) -{ - int i; - int j; - int good_spot; - int retval = -EINVAL; - - dbg ("%s is asking for %d minors", new_driver->name, num_minors); - - if (new_driver->fops == NULL) - goto exit; - - *start_minor = 0; - spin_lock (&minor_lock); - for (i = 0; i < MAX_USB_MINORS; ++i) { - if (usb_minors[i]) - continue; - - good_spot = 1; - for (j = 1; j <= num_minors-1; ++j) - if (usb_minors[i+j]) { - good_spot = 0; - break; - } - if (good_spot == 0) - continue; - - *start_minor = i; - spin_unlock (&minor_lock); - retval = usb_register_minors (new_driver, num_minors, *start_minor); - if (retval) { - /* someone snuck in here, so let's start looking all over again */ - spin_lock (&minor_lock); - i = 0; - continue; - } - goto exit; - } - spin_unlock (&minor_lock); -exit: - return retval; -} - -/** - * usb_deregister_dev - deregister a USB device's dynamic minor. - * @driver: USB operations for the driver - * @num_minors: number of minor numbers to put back. - * @start_minor: the starting minor number - * - * Used in conjunction with usb_register_dev(). This function is called - * when the USB driver is finished with the minor numbers gotten from a - * call to usb_register_dev() (usually when the device is disconnected - * from the system.) - * - * This should be called by all drivers that use the USB major number. - */ -void usb_deregister_dev (struct usb_driver *driver, int num_minors, int start_minor) -{ - usb_deregister_minors (driver, num_minors, start_minor); -} -#endif /* CONFIG_USB_DYNAMIC_MINORS */ - - -/** * usb_scan_devices - scans all unclaimed USB interfaces * Context: !in_interrupt () * @@ -298,11 +161,15 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) } /** - * usb_deregister - unregister a USB driver - * @driver: USB operations of the driver to unregister - * Context: !in_interrupt () + * usb_deregister - unregister a USB driver + * @driver: USB operations of the driver to unregister + * Context: !in_interrupt () * - * Unlinks the specified driver from the internal USB driver list. + * Unlinks the specified driver from the internal USB driver list. + * + * NOTE: If you called usb_register_dev(), you still need to call + * usb_deregister_dev() to clean up your driver's allocated minor numbers, + * this * call will no longer do it for you. */ void usb_deregister(struct usb_driver *driver) { @@ -310,11 +177,6 @@ void usb_deregister(struct usb_driver *driver) info("deregistering driver %s", driver->name); -#ifndef CONFIG_USB_DYNAMIC_MINORS - if (driver->fops != NULL) - usb_deregister_minors (driver, driver->num_minors, driver->minor); -#endif - /* * first we remove the driver, to be sure it doesn't get used by * another thread while we are stepping through removing entries @@ -1357,55 +1219,6 @@ int usb_new_device(struct usb_device *dev) return 0; } -static int usb_open(struct inode * inode, struct file * file) -{ - int minor = minor(inode->i_rdev); - struct usb_driver *c; - int err = -ENODEV; - struct file_operations *old_fops, *new_fops = NULL; - - spin_lock (&minor_lock); - c = usb_minors[minor]; - spin_unlock (&minor_lock); - - if (!c || !(new_fops = fops_get(c->fops))) - return err; - old_fops = file->f_op; - file->f_op = new_fops; - /* Curiouser and curiouser... NULL ->open() as "no device" ? */ - if (file->f_op->open) - err = file->f_op->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 usb_fops = { - owner: THIS_MODULE, - open: usb_open, -}; - -int usb_major_init(void) -{ - if (devfs_register_chrdev(USB_MAJOR, "usb", &usb_fops)) { - err("unable to get major %d for usb devices", USB_MAJOR); - return -EBUSY; - } - - usb_devfs_handle = devfs_mk_dir(NULL, "usb", NULL); - - return 0; -} - -void usb_major_cleanup(void) -{ - devfs_unregister(usb_devfs_handle); - devfs_unregister_chrdev(USB_MAJOR, "usb"); -} - #ifdef CONFIG_PROC_FS struct list_head *usb_driver_get_list(void) @@ -1463,11 +1276,6 @@ EXPORT_SYMBOL(usb_register); EXPORT_SYMBOL(usb_deregister); EXPORT_SYMBOL(usb_scan_devices); -#ifdef CONFIG_USB_DYNAMIC_MINORS -EXPORT_SYMBOL(usb_register_dev); -EXPORT_SYMBOL(usb_deregister_dev); -#endif - EXPORT_SYMBOL(usb_alloc_dev); EXPORT_SYMBOL(usb_free_dev); EXPORT_SYMBOL(usb_get_dev); @@ -1489,5 +1297,4 @@ EXPORT_SYMBOL(__usb_get_extra_descriptor); EXPORT_SYMBOL(usb_get_current_frame_number); -EXPORT_SYMBOL(usb_devfs_handle); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/Config.help b/drivers/usb/host/Config.help index 2456b823e269..7cfd9ff8e5aa 100644 --- a/drivers/usb/host/Config.help +++ b/drivers/usb/host/Config.help @@ -36,26 +36,7 @@ CONFIG_USB_OHCI_HCD The module will be called ohci-hcd.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. -CONFIG_USB_UHCI - The Universal Host Controller Interface is a standard by Intel for - accessing the USB hardware in the PC (which is also called the USB - host controller). If your USB host controller conforms to this - standard, you may want to say Y, but see below. All recent boards - with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX, - i810, i820) conform to this standard. Also all VIA PCI chipsets - (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro - 133). - - Currently there exist two drivers for UHCI host controllers: this - one and the so-called JE driver, which you can get from - "UHCI alternate (JE) support", below. You need only one. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called usb-uhci.o. If you want to compile it as a - module, say M here and read <file:Documentation/modules.txt>. - -CONFIG_USB_UHCI_ALT +CONFIG_USB_UHCI_HCD_ALT The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB host controller). If your USB host controller conforms to this @@ -65,31 +46,11 @@ CONFIG_USB_UHCI_ALT (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro 133). If unsure, say Y. - Currently there exist two drivers for UHCI host controllers: this - so-called JE driver, and the one you get from "UHCI support", above. - You need only one. - This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called uhci.o. If you want to compile it as a + The module will be called uhci-hcd.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. -CONFIG_USB_OHCI - The Open Host Controller Interface is a standard by - Compaq/Microsoft/National for accessing the USB PC hardware (also - called USB host controller). If your USB host controller conforms to - this standard, say Y. The USB host controllers on most non-Intel - architectures and on several x86 compatibles with non-Intel chipsets - -- like SiS (aktual 610, 610 and so on) or ALi (ALi IV, ALi V, - Aladdin Pro..) -- conform to this standard. - - You may want to read <file:Documentation/usb/ohci.txt>. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called usb-ohci.o. If you want to compile it - as a module, say M here and read <file:Documentation/modules.txt>. - CONFIG_USB_SL811HS Say Y here if you have a SL811HS USB host controller in your system. diff --git a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in index 806495da5726..dbfb819f8d33 100644 --- a/drivers/usb/host/Config.in +++ b/drivers/usb/host/Config.in @@ -2,23 +2,9 @@ # USB Host Controller Drivers # comment 'USB Host Controller Drivers' -dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL -dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL -if [ "$CONFIG_USB_UHCI_HCD_ALT" != "y" ]; then - dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL -fi -if [ "$CONFIG_USB_UHCI_HCD" != "y" ]; then - dep_tristate ' UHCI HCD Alternate (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL -fi -#if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then -# dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB -#fi -#if [ "$CONFIG_USB_UHCI" != "y" ]; then -# dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB -#else -# define_bool CONFIG_USB_UHCI_ALT n -#fi -#dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB +dep_tristate ' EHCI HCD (USB 2.0) support' CONFIG_USB_EHCI_HCD $CONFIG_USB +dep_tristate ' OHCI HCD support' CONFIG_USB_OHCI_HCD $CONFIG_USB +dep_tristate ' UHCI HCD (most Intel and VIA) support' CONFIG_USB_UHCI_HCD_ALT $CONFIG_USB if [ "$CONFIG_ARM" = "y" ]; then dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB dep_tristate ' SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index b6aba6fb9c49..1d9bd9c37300 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -190,6 +190,8 @@ static void ohci_dump_roothub (struct ohci_hcd *controller, int verbose) __u32 temp, ndp, i; temp = roothub_a (controller); + if (temp == ~(u32)0) + return; ndp = (temp & RH_A_NDP); if (verbose) { diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index bfd4bc4315d6..db4276ead92c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -502,11 +502,19 @@ static void ohci_irq (struct usb_hcd *hcd) if ((ohci->hcca->done_head != 0) && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; - } else if ((ints = (readl (®s->intrstatus) - & readl (®s->intrenable))) == 0) { + + /* cardbus/... hardware gone before remove() */ + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", hcd->self.bus_name); + return; + + /* interrupt for some other device? */ + } else if ((ints &= readl (®s->intrenable)) == 0) { return; } + // dbg ("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); if (ints & OHCI_INTR_UE) { diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 23919dfb7c84..33279c5cc906 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -1665,37 +1665,15 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) uhci_unlink_generic(uhci, urb); - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urbp->status = urb->status = -ECONNABORTED; + spin_lock(&uhci->urb_remove_list_lock); - spin_lock(&uhci->urb_remove_list_lock); - - /* If we're the first, set the next interrupt bit */ - if (list_empty(&uhci->urb_remove_list)) - uhci_set_next_interrupt(uhci); - - list_add(&urbp->urb_list, &uhci->urb_remove_list); - - spin_unlock(&uhci->urb_remove_list_lock); - - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - } else { - urb->status = -ENOENT; - - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - - if (in_interrupt()) { /* wait at least 1 frame */ - static int errorcount = 10; - - if (errorcount--) - dbg("uhci_urb_dequeue called from interrupt for urb %p", urb); - udelay(1000); - } else - schedule_timeout(1+1*HZ/1000); - - uhci_finish_urb(hcd, urb); - } + /* If we're the first, set the next interrupt bit */ + if (list_empty(&uhci->urb_remove_list)) + uhci_set_next_interrupt(uhci); + list_add(&urbp->urb_list, &uhci->urb_remove_list); + spin_unlock(&uhci->urb_remove_list_lock); + spin_unlock_irqrestore(&uhci->urb_list_lock, flags); return 0; } @@ -1788,7 +1766,7 @@ static void stall_callback(unsigned long ptr) tmp = tmp->next; - u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; + u->transfer_flags |= USB_TIMEOUT_KILLED; uhci_urb_dequeue(hcd, u); } diff --git a/drivers/usb/host/usb-uhci-hcd.c b/drivers/usb/host/usb-uhci-hcd.c index f7979e431024..1c476e00c633 100644 --- a/drivers/usb/host/usb-uhci-hcd.c +++ b/drivers/usb/host/usb-uhci-hcd.c @@ -271,21 +271,15 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags static int uhci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { unsigned long flags=0; - struct uhci_hcd *uhci; + struct uhci_hcd *uhci = hcd_to_uhci (hcd); + int ret; dbg("uhci_urb_dequeue called for %p",urb); - uhci = hcd_to_uhci (hcd); - - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - int ret; - spin_lock_irqsave (&uhci->urb_list_lock, flags); - ret = uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB); - spin_unlock_irqrestore (&uhci->urb_list_lock, flags); - return ret; - } - else - return uhci_unlink_urb_sync(uhci, urb); + spin_lock_irqsave (&uhci->urb_list_lock, flags); + ret = uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB); + spin_unlock_irqrestore (&uhci->urb_list_lock, flags); + return ret; } /*--------------------------------------------------------------------------*/ static int uhci_get_frame (struct usb_hcd *hcd) diff --git a/drivers/usb/host/usb-uhci-q.c b/drivers/usb/host/usb-uhci-q.c index 0ac0d798ab76..da8b67787b46 100644 --- a/drivers/usb/host/usb-uhci-q.c +++ b/drivers/usb/host/usb-uhci-q.c @@ -541,22 +541,6 @@ static void uhci_clean_iso_step1(struct uhci_hcd *uhci, urb_priv_t *urb_priv) } } /*-------------------------------------------------------------------*/ -static void uhci_clean_iso_step2(struct uhci_hcd *uhci, urb_priv_t *urb_priv) -{ - struct list_head *p; - uhci_desc_t *td; - int now=UHCI_GET_CURRENT_FRAME(uhci); - - dbg("uhci_clean_iso_step2"); - while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { - td = list_entry (p, uhci_desc_t, desc_list); - list_del (p); - INIT_LIST_HEAD(&td->horizontal); - list_add_tail (&td->horizontal, &uhci->free_desc_td); - td->last_used=now; - } -} -/*-------------------------------------------------------------------*/ /* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink) CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark) CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink) @@ -759,44 +743,6 @@ static int uhci_unlink_urb_async (struct uhci_hcd *uhci, struct urb *urb, int mo return 0; // completion will follow } /*-------------------------------------------------------------------*/ -// kills an urb by unlinking descriptors and waiting for at least one frame -static int uhci_unlink_urb_sync (struct uhci_hcd *uhci, struct urb *urb) -{ - uhci_desc_t *qh; - urb_priv_t *urb_priv; - unsigned long flags=0; - - spin_lock_irqsave (&uhci->urb_list_lock, flags); -// err("uhci_unlink_urb_sync %p, %i",urb,urb->status); - - // move descriptors out the the running chains, dequeue urb - uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_DONT_STORE); - - urb_priv = urb->hcpriv; - - spin_unlock_irqrestore (&uhci->urb_list_lock, flags); - - // cleanup the rest - switch (usb_pipetype (urb->pipe)) { - case PIPE_INTERRUPT: - case PIPE_ISOCHRONOUS: - uhci_wait_ms(1); - uhci_clean_iso_step2(uhci, urb_priv); - break; - - case PIPE_BULK: - case PIPE_CONTROL: - qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); - uhci_clean_transfer(uhci, urb, qh, CLEAN_TRANSFER_DELETION_MARK); - uhci_wait_ms(1); - } - urb->status = -ENOENT; // mark urb as killed - - finish_urb(uhci,urb); - - return 0; -} -/*-------------------------------------------------------------------*/ // unlink urbs for specific device or all devices static void uhci_unlink_urbs(struct uhci_hcd *uhci, struct usb_device *usb_dev, int remove_all) { @@ -816,8 +762,6 @@ static void uhci_unlink_urbs(struct uhci_hcd *uhci, struct usb_device *usb_dev, // err("unlink urb: %p, dev %p, ud %p", urb, usb_dev,urb->dev); - //urb->transfer_flags |=USB_ASYNC_UNLINK; - if (remove_all || (usb_dev == urb->dev)) { spin_unlock_irqrestore (&uhci->urb_list_lock, flags); err("forced removing of queued URB %p due to disconnect",urb); @@ -850,7 +794,7 @@ static void uhci_check_timeouts(struct uhci_hcd *uhci) type = usb_pipetype (urb->pipe); if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) { - urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; + urb->transfer_flags |= USB_TIMEOUT_KILLED; async_dbg("uhci_check_timeout: timeout for %p",urb); uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB); } diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index a4cccb7e4df7..6514054a8813 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -401,6 +401,7 @@ static void mdc800_usb_download_notify (struct urb *urb) ***************************************************************************/ static struct usb_driver mdc800_usb_driver; +static struct file_operations mdc800_device_ops; /* * Callback to search the Mustek MDC800 on the USB Bus @@ -476,7 +477,7 @@ static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum, down (&mdc800->io_lock); - retval = usb_register_dev (&mdc800_usb_driver, 1, &mdc800->minor); + retval = usb_register_dev (&mdc800_device_ops, MDC800_DEVICE_MINOR_BASE, 1, &mdc800->minor); if (retval && (retval != -ENODEV)) { err ("Not able to get a minor for this device."); return 0; @@ -537,7 +538,7 @@ static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr) if (mdc800->state == NOT_CONNECTED) return; - usb_deregister_dev (&mdc800_usb_driver, 1, mdc800->minor); + usb_deregister_dev (1, mdc800->minor); mdc800->state=NOT_CONNECTED; @@ -942,12 +943,10 @@ MODULE_DEVICE_TABLE (usb, mdc800_table); */ static struct usb_driver mdc800_usb_driver = { + owner: THIS_MODULE, name: "mdc800", probe: mdc800_usb_probe, disconnect: mdc800_usb_disconnect, - fops: &mdc800_device_ops, - minor: MDC800_DEVICE_MINOR_BASE, - num_minors: 1, id_table: mdc800_table }; diff --git a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c index 54e181fdb695..2bb1de40e3c0 100644 --- a/drivers/usb/image/scanner.c +++ b/drivers/usb/image/scanner.c @@ -971,17 +971,11 @@ probe_scanner(struct usb_device *dev, unsigned int ifnum, down(&scn_mutex); - retval = usb_register_dev(&scanner_driver, 1, &scn_minor); + retval = usb_register_dev(&usb_scanner_fops, SCN_BASE_MNR, 1, &scn_minor); if (retval) { - if (retval != -ENODEV) { - err ("Not able to get a minor for this device."); - up(&scn_mutex); - return NULL; - } - for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) { - if (!p_scn_table[scn_minor]) - break; - } + err ("Not able to get a minor for this device."); + up(&scn_mutex); + return NULL; } /* Check to make sure that the last slot isn't already taken */ @@ -1112,7 +1106,7 @@ disconnect_scanner(struct usb_device *dev, void *ptr) dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor); devfs_unregister(scn->devfs); - usb_deregister_dev(&scanner_driver, 1, scn->scn_minor); + usb_deregister_dev(1, scn->scn_minor); p_scn_table[scn->scn_minor] = NULL; usb_free_urb(scn->scn_irq); up (&(scn->sem)); @@ -1125,9 +1119,6 @@ usb_driver scanner_driver = { name: "usbscanner", probe: probe_scanner, disconnect: disconnect_scanner, - fops: &usb_scanner_fops, - minor: SCN_BASE_MNR, - num_minors: SCN_MAX_MNR, id_table: NULL, /* This would be scanner_device_ids, but we need to check every USB device, in case we match a user defined vendor/product ID. */ diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index 2d7478dcf7c7..cbed3efbb7c4 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -296,9 +296,8 @@ aiptek_probe(struct usb_device *dev, unsigned int ifnum, input_register_device(&aiptek->dev); - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - aiptek->dev.number, aiptek->features->name, dev->bus->busnum, - dev->devnum, ifnum); + printk(KERN_INFO "input: %s on usb%d:%d.%d\n", + aiptek->features->name, dev->bus->busnum, dev->devnum, ifnum); return aiptek; } diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 0e4347fa11e1..51b7808d7472 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -193,7 +193,7 @@ static int hiddev_fasync(int fd, struct file *file, int on) static void hiddev_cleanup(struct hiddev *hiddev) { devfs_unregister(hiddev->devfs); - usb_deregister_dev(&hiddev_driver, 1, hiddev->minor); + usb_deregister_dev(1, hiddev->minor); hiddev_table[hiddev->minor] = NULL; kfree(hiddev); } @@ -626,21 +626,16 @@ int hiddev_connect(struct hid_device *hid) if (i == hid->maxapplication) return -1; - retval = usb_register_dev (&hiddev_driver, 1, &minor); + retval = usb_register_dev (&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor); if (retval) { - if (retval != -ENODEV) { - err ("Not able to get a minor for this device."); - return -1; - } - for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++); - if (minor == HIDDEV_MINORS) { - printk(KERN_ERR "hiddev: no more free hiddev devices\n"); - return -1; - } + err ("Not able to get a minor for this device."); + return -1; } - if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) + if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) { + usb_deregister_dev (1, minor); return -1; + } memset(hiddev, 0, sizeof(struct hiddev)); init_waitqueue_head(&hiddev->wait); @@ -708,9 +703,6 @@ static void *hiddev_usbd_probe(struct usb_device *dev, unsigned int ifnum, static /* const */ struct usb_driver hiddev_driver = { name: "hiddev", probe: hiddev_usbd_probe, - fops: &hiddev_fops, - minor: HIDDEV_MINOR_BASE, - num_minors: HIDDEV_MINORS, }; int __init hiddev_init(void) diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 7df4e9eaba47..3658e693484f 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -742,14 +742,9 @@ static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum, if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999) return NULL; - retval = usb_register_dev (&dabusb_driver, 1, &devnum); - if (retval) { - if (retval != -ENODEV) - return NULL; - devnum = dabusb_find_struct (); - if (devnum == -1) - return NULL; - } + retval = usb_register_dev (&dabusb_fops, DABUSB_MINOR, 1, &devnum); + if (retval) + return NULL; s = &dabusb[devnum]; @@ -791,7 +786,7 @@ static void dabusb_disconnect (struct usb_device *usbdev, void *ptr) dbg("dabusb_disconnect"); - usb_deregister_dev (&dabusb_driver, 1, s->devnum); + usb_deregister_dev (1, s->devnum); s->remove_pending = 1; wake_up (&s->wait); if (s->state == _started) @@ -814,9 +809,6 @@ static struct usb_driver dabusb_driver = name: "dabusb", probe: dabusb_probe, disconnect: dabusb_disconnect, - fops: &dabusb_fops, - minor: DABUSB_MINOR, - num_minors: NRDABUSB, id_table: dabusb_ids, }; diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index a77c19622b53..0d55087b038b 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -1948,23 +1948,11 @@ static void *auerswald_probe (struct usb_device *usbdev, unsigned int ifnum, init_waitqueue_head (&cp->bufferwait); down (&dev_table_mutex); - ret = usb_register_dev (&auerswald_driver, 1, &dtindex); + ret = usb_register_dev (&auerswald_fops, AUER_MINOR_BASE, 1, &dtindex); if (ret) { - if (ret != -ENODEV) { - err ("Not able to get a minor for this device."); - up (&dev_table_mutex); - goto pfail; - } - /* find a free slot in the device table */ - for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) { - if (dev_table[dtindex] == NULL) - break; - } - if ( dtindex >= AUER_MAX_DEVICES) { - err ("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES); - up (&dev_table_mutex); - goto pfail; - } + err ("Not able to get a minor for this device."); + up (&dev_table_mutex); + goto pfail; } /* Give the device a name */ @@ -2096,7 +2084,7 @@ static void auerswald_disconnect (struct usb_device *usbdev, void *driver_contex devfs_unregister (cp->devfs); /* give back our USB minor number */ - usb_deregister_dev (&auerswald_driver, 1, cp->dtindex); + usb_deregister_dev (1, cp->dtindex); /* Stop the interrupt endpoint */ auerswald_int_release (cp); @@ -2153,9 +2141,6 @@ static struct usb_driver auerswald_driver = { name: "auerswald", probe: auerswald_probe, disconnect: auerswald_disconnect, - fops: &auerswald_fops, - minor: AUER_MINOR_BASE, - num_minors: AUER_MAX_DEVICES, id_table: auerswald_ids, }; diff --git a/drivers/usb/misc/brlvger.c b/drivers/usb/misc/brlvger.c index 7792f1c86ec5..52b815eb57a3 100644 --- a/drivers/usb/misc/brlvger.c +++ b/drivers/usb/misc/brlvger.c @@ -246,9 +246,6 @@ static struct usb_driver brlvger_driver = name: "brlvger", probe: brlvger_probe, disconnect: brlvger_disconnect, - fops: &brlvger_fops, - minor: BRLVGER_MINOR, - num_minors: MAX_NR_BRLVGER_DEVS, id_table: brlvger_ids, }; @@ -316,21 +313,10 @@ brlvger_probe (struct usb_device *dev, unsigned ifnum, down(&reserve_sem); - retval = usb_register_dev(&brlvger_driver, 1, &i); + retval = usb_register_dev(&brlvger_fops, BRLVGER_MINOR, 1, &i); if (retval) { - if (retval != -ENODEV) { - err("Not able to get a minor for this device."); - goto error; - } - for( i = 0; i < MAX_NR_BRLVGER_DEVS; i++ ) - if( display_table[i] == NULL ) - break; - - if( i == MAX_NR_BRLVGER_DEVS ) { - err( "This driver cannot handle more than %d " - "braille displays", MAX_NR_BRLVGER_DEVS); - goto error; - } + err("Not able to get a minor for this device."); + goto error; } if( !(priv = kmalloc (sizeof *priv, GFP_KERNEL)) ){ @@ -431,7 +417,7 @@ brlvger_disconnect(struct usb_device *dev, void *ptr) info("Display %d disconnecting", priv->subminor); devfs_unregister(priv->devfs); - usb_deregister_dev(&brlvger_driver, 1, priv->subminor); + usb_deregister_dev(1, priv->subminor); down(&disconnect_sem); display_table[priv->subminor] = NULL; diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index b3f5d0b7bf7f..705f753b8ed2 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -49,7 +49,11 @@ #define DRIVER_AUTHOR "Cesar Miquel <miquel@df.uba.ar>" #define DRIVER_DESC "USB Rio 500 driver" -#define RIO_MINOR 64 +#ifdef CONFIG_USB_DYNAMIC_MINORS + #define RIO_MINOR 0 +#else + #define RIO_MINOR 64 +#endif /* stall/wait timeout for rio */ #define NAK_TIMEOUT (HZ) @@ -65,6 +69,7 @@ struct rio_usb_data { unsigned int ifnum; /* Interface number of the USB device */ int isopen; /* nz if open */ int present; /* Device is present on the bus */ + int minor; /* minor number assigned to us */ char *obuf, *ibuf; /* transfer buffers */ char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ wait_queue_head_t wait_q; /* for timeouts */ @@ -449,9 +454,16 @@ static void *probe_rio(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { struct rio_usb_data *rio = &rio_instance; + int retval; info("USB Rio found at address %d", dev->devnum); + retval = usb_register_dev(&usb_rio_fops, RIO_MINOR, 1, &rio->minor); + if (retval) { + err("Not able to get a minor for this device."); + return NULL; + } + rio->present = 1; rio->rio_dev = dev; @@ -486,6 +498,7 @@ static void disconnect_rio(struct usb_device *dev, void *ptr) struct rio_usb_data *rio = (struct rio_usb_data *) ptr; devfs_unregister(rio->devfs); + usb_deregister_dev(1, rio->minor); down(&(rio->lock)); if (rio->isopen) { @@ -515,9 +528,6 @@ static struct usb_driver rio_driver = { name: "rio500", probe: probe_rio, disconnect: disconnect_rio, - fops: &usb_rio_fops, - minor: RIO_MINOR, - num_minors: 1, id_table: rio_table, }; diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index 11e3f6bb4d5e..a7d9c72cdbcc 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -1,46 +1,34 @@ /* -** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller -** -** Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) -** -** -** ChangeLog: -** .... Most of the time spend reading sources & docs. -** v0.2.x First official release for the Linux kernel. -** v0.3.0 Beutified and structured, some bugs fixed. -** v0.3.x URBifying bulk requests and bugfixing. First relatively -** stable release. Still can touch device's registers only -** from top-halves. -** v0.4.0 Control messages remained unurbified are now URBs. -** Now we can touch the HW at any time. -** v0.4.9 Control urbs again use process context to wait. Argh... -** Some long standing bugs (enable_net_traffic) fixed. -** Also nasty trick about resubmiting control urb from -** interrupt context used. Please let me know how it -** behaves. Pegasus II support added since this version. -** TODO: suppressing HCD warnings spewage on disconnect. -** v0.4.13 Ethernet address is now set at probe(), not at open() -** time as this seems to break dhcpd. -** v0.5.0 branch to 2.5.x kernels -** v0.5.1 ethtool support added -*/ - -/* - * 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. + * Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) * - * 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. + * 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. * - * 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 + * ChangeLog: + * .... Most of the time spent on reading sources & docs. + * v0.2.x First official release for the Linux kernel. + * v0.3.0 Beutified and structured, some bugs fixed. + * v0.3.x URBifying bulk requests and bugfixing. First relatively + * stable release. Still can touch device's registers only + * from top-halves. + * v0.4.0 Control messages remained unurbified are now URBs. + * Now we can touch the HW at any time. + * v0.4.9 Control urbs again use process context to wait. Argh... + * Some long standing bugs (enable_net_traffic) fixed. + * Also nasty trick about resubmiting control urb from + * interrupt context used. Please let me know how it + * behaves. Pegasus II support added since this version. + * TODO: suppressing HCD warnings spewage on disconnect. + * v0.4.13 Ethernet address is now set at probe(), not at open() + * time as this seems to break dhcpd. + * v0.5.0 branch to 2.5.x kernels + * v0.5.1 ethtool support added + * v0.5.5 rx socket buffers are in a pool and the their allocation + * is out of the interrupt routine. */ + #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> @@ -58,14 +46,13 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.5.4 (2002/04/11)" +#define DRIVER_VERSION "v0.5.6 (2002/06/23)" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" static const char driver_name[] = "pegasus"; -#define PEGASUS_USE_INTR -#define PEGASUS_WRITE_EEPROM +#undef PEGASUS_WRITE_EEPROM #define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ BMSR_100FULL | BMSR_ANEGCAPABLE) @@ -499,13 +486,57 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) return 0; } +static void fill_skb_pool(pegasus_t *pegasus) +{ + int i; + + for (i=0; i < RX_SKBS; i++) { + if (pegasus->rx_pool[i]) + continue; + pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2); + /* + ** we give up if the allocation fail. the tasklet will be + ** rescheduled again anyway... + */ + if (pegasus->rx_pool[i] == NULL) + return; + pegasus->rx_pool[i]->dev = pegasus->net; + skb_reserve(pegasus->rx_pool[i], 2); + } +} + +static void free_skb_pool(pegasus_t *pegasus) +{ + int i; + + for (i=0; i < RX_SKBS; i++) { + if (pegasus->rx_pool[i]) { + dev_kfree_skb(pegasus->rx_pool[i]); + pegasus->rx_pool[i] = NULL; + } + } +} + +static inline struct sk_buff *pull_skb(pegasus_t *pegasus) +{ + int i; + struct sk_buff *skb; + + for (i=0; i < RX_SKBS; i++) { + if (likely(pegasus->rx_pool[i] != NULL)) { + skb = pegasus->rx_pool[i]; + pegasus->rx_pool[i] = NULL; + return skb; + } + } + return NULL; +} + static void read_bulk_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; struct net_device *net; - int count = urb->actual_length; - int rx_status; - struct sk_buff *skb; + int rx_status, count = urb->actual_length; __u16 pkt_len; if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING)) @@ -519,7 +550,7 @@ static void read_bulk_callback(struct urb *urb) case 0: break; case -ETIMEDOUT: - dbg("reset MAC"); + dbg("%s: reset MAC", net->name); pegasus->flags &= ~PEGASUS_RX_BUSY; break; case -ENOENT: @@ -546,23 +577,24 @@ static void read_bulk_callback(struct urb *urb) } pkt_len = (rx_status & 0xfff) - 8; - if (!pegasus->rx_skb) - goto tl_sched; - + if (pegasus->rx_skb == NULL) + printk("%s: rx_skb == NULL\n", __FUNCTION__); + /* + ** we are sure at this point pegasus->rx_skb != NULL + ** so we go ahead and pass up the packet. + */ skb_put(pegasus->rx_skb, pkt_len); pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); netif_rx(pegasus->rx_skb); - - if (!(skb = dev_alloc_skb(PEGASUS_MTU + 2))) { - pegasus->rx_skb = NULL; - goto tl_sched; - } - - skb->dev = net; - skb_reserve(skb, 2); - pegasus->rx_skb = skb; pegasus->stats.rx_packets++; pegasus->stats.rx_bytes += pkt_len; + + spin_lock(&pegasus->rx_pool_lock); + pegasus->rx_skb = pull_skb(pegasus); + spin_unlock(&pegasus->rx_pool_lock); + + if (pegasus->rx_skb == NULL) + goto tl_sched; goon: FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), @@ -587,11 +619,19 @@ static void rx_fixup(unsigned long data) pegasus = (pegasus_t *)data; + spin_lock_irq(&pegasus->rx_pool_lock); + fill_skb_pool(pegasus); + spin_unlock_irq(&pegasus->rx_pool_lock); if (pegasus->flags & PEGASUS_RX_URB_FAIL) if (pegasus->rx_skb) goto try_again; - - if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) { + if (pegasus->rx_skb == NULL) { + spin_lock_irq(&pegasus->rx_pool_lock); + pegasus->rx_skb = pull_skb(pegasus); + spin_unlock_irq(&pegasus->rx_pool_lock); + } + if (pegasus->rx_skb == NULL) { + warn("wow, low on memory"); tasklet_schedule(&pegasus->rx_tl); return; } @@ -625,7 +665,6 @@ static void write_bulk_callback(struct urb *urb) netif_wake_queue(pegasus->net); } -#ifdef PEGASUS_USE_INTR static void intr_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; @@ -662,7 +701,6 @@ static void intr_callback(struct urb *urb) } } } -#endif static void pegasus_tx_timeout(struct net_device *net) { @@ -748,15 +786,62 @@ static void set_carrier(struct net_device *net) } +static void free_all_urbs(pegasus_t *pegasus) +{ + usb_free_urb(pegasus->intr_urb); + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); +} + +static void unlink_all_urbs(pegasus_t *pegasus) +{ + usb_unlink_urb(pegasus->intr_urb); + usb_unlink_urb(pegasus->tx_urb); + usb_unlink_urb(pegasus->rx_urb); + usb_unlink_urb(pegasus->ctrl_urb); +} + +static int alloc_urbs(pegasus_t *pegasus) +{ + pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->ctrl_urb) { + return 0; + } + pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->rx_urb) { + usb_free_urb(pegasus->ctrl_urb); + return 0; + } + pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->tx_urb) { + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + return 0; + } + pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pegasus->intr_urb) { + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + return 0; + } + + return 1; +} + static int pegasus_open(struct net_device *net) { pegasus_t *pegasus = (pegasus_t *) net->priv; int res; - if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) + if (pegasus->rx_skb == NULL) + pegasus->rx_skb = pull_skb(pegasus); + /* + ** Note: no point to free the pool. it is empty :-) + */ + if (!pegasus->rx_skb) return -ENOMEM; - pegasus->rx_skb->dev = net; - skb_reserve(pegasus->rx_skb, 2); down(&pegasus->sem); FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, @@ -765,19 +850,20 @@ static int pegasus_open(struct net_device *net) read_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) warn("%s: failed rx_urb %d", __FUNCTION__, res); -#ifdef PEGASUS_USE_INTR FILL_INT_URB(pegasus->intr_urb, pegasus->usb, usb_rcvintpipe(pegasus->usb, 3), pegasus->intr_buff, sizeof(pegasus->intr_buff), intr_callback, pegasus, pegasus->intr_interval); if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) warn("%s: failed intr_urb %d", __FUNCTION__, res); -#endif netif_start_queue(net); pegasus->flags |= PEGASUS_RUNNING; if ((res = enable_net_traffic(net, pegasus->usb))) { err("can't enable_net_traffic() - %d", res); res = -EIO; + usb_unlink_urb(pegasus->rx_urb); + usb_unlink_urb(pegasus->intr_urb); + free_skb_pool(pegasus); goto exit; } set_carrier(net); @@ -797,13 +883,7 @@ static int pegasus_close(struct net_device *net) netif_stop_queue(net); if (!(pegasus->flags & PEGASUS_UNPLUG)) disable_net_traffic(pegasus); - - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->tx_urb); - usb_unlink_urb(pegasus->ctrl_urb); -#ifdef PEGASUS_USE_INTR - usb_unlink_urb(pegasus->intr_urb); -#endif + unlink_all_urbs(pegasus); up(&pegasus->sem); return 0; @@ -986,38 +1066,14 @@ static void *pegasus_probe(struct usb_device *dev, unsigned int ifnum, pegasus->dev_index = dev_index; init_waitqueue_head(&pegasus->ctrl_wait); - pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->ctrl_urb) { - kfree(pegasus); - return NULL; - } - pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->rx_urb) { - usb_free_urb(pegasus->ctrl_urb); - kfree(pegasus); - return NULL; - } - pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->tx_urb) { - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); - kfree(pegasus); - return NULL; - } - pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pegasus->intr_urb) { - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + if (!alloc_urbs(pegasus)) { kfree(pegasus); return NULL; } net = init_etherdev(NULL, 0); if (!net) { - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + free_all_urbs(pegasus); kfree(pegasus); return NULL; } @@ -1039,32 +1095,26 @@ static void *pegasus_probe(struct usb_device *dev, unsigned int ifnum, net->set_multicast_list = pegasus_set_multicast; net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; + spin_lock_init(&pegasus->rx_pool_lock); pegasus->features = usb_dev_id[dev_index].private; -#ifdef PEGASUS_USE_INTR get_interrupt_interval(pegasus); -#endif if (reset_mac(pegasus)) { err("can't reset MAC"); unregister_netdev(pegasus->net); - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + free_all_urbs(pegasus); kfree(pegasus->net); kfree(pegasus); pegasus = NULL; goto exit; } - - info("%s: %s", net->name, usb_dev_id[dev_index].name); - set_ethernet_addr(pegasus); - + fill_skb_pool(pegasus); + printk("%s: %s\n", net->name, usb_dev_id[dev_index].name); if (pegasus->features & PEGASUS_II) { info("setup Pegasus II specific registers"); setup_pegasus_II(pegasus); } - pegasus->phy = mii_phy_probe(pegasus); if (pegasus->phy == 0xff) { warn("can't locate MII phy, using default"); @@ -1087,14 +1137,9 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) pegasus->flags |= PEGASUS_UNPLUG; unregister_netdev(pegasus->net); usb_put_dev(dev); - usb_unlink_urb(pegasus->intr_urb); - usb_unlink_urb(pegasus->tx_urb); - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->ctrl_urb); - usb_free_urb(pegasus->intr_urb); - usb_free_urb(pegasus->tx_urb); - usb_free_urb(pegasus->rx_urb); - usb_free_urb(pegasus->ctrl_urb); + unlink_all_urbs(pegasus); + free_all_urbs(pegasus); + free_skb_pool(pegasus); if (pegasus->rx_skb) dev_kfree_skb(pegasus->rx_skb); kfree(pegasus->net); diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h index 6e25d3efd5e1..d231998a4698 100644 --- a/drivers/usb/net/pegasus.h +++ b/drivers/usb/net/pegasus.h @@ -2,18 +2,8 @@ * Copyright (c) 1999-2002 Petko Manolov - Petkan (petkan@users.sourceforge.net) * * 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 + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. */ @@ -23,6 +13,7 @@ #define HAS_HOME_PNA 0x40000000 #define PEGASUS_MTU 1536 +#define RX_SKBS 4 #define EPROM_WRITE 0x01 #define EPROM_READ 0x02 @@ -100,10 +91,12 @@ typedef struct pegasus { int intr_interval; struct tasklet_struct rx_tl; struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; + struct sk_buff *rx_pool[RX_SKBS]; struct sk_buff *rx_skb; struct usb_ctrlrequest dr; wait_queue_head_t ctrl_wait; struct semaphore sem; + spinlock_t rx_pool_lock; unsigned char intr_buff[8]; __u8 tx_buff[PEGASUS_MTU]; __u8 eth_regs[4]; diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 3934c4dd5069..ffd2737fee5b 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -1,15 +1,14 @@ /* - * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) - * - * 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. + * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.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/config.h> #include <linux/sched.h> +#include <linux/init.h> #include <linux/signal.h> #include <linux/slab.h> #include <linux/module.h> @@ -19,7 +18,6 @@ #include <linux/ethtool.h> #include <linux/devfs_fs_kernel.h> #include <linux/usb.h> -#include <linux/init.h> #include <asm/uaccess.h> /* Version Information */ @@ -106,7 +104,7 @@ unsigned long multicast_filter_limit = 32; static void fill_skb_pool(rtl8150_t *); static void free_skb_pool(rtl8150_t *); -static struct sk_buff *pull_skb(rtl8150_t *); +static inline struct sk_buff *pull_skb(rtl8150_t *); static void rtl8150_disconnect(struct usb_device *dev, void *ptr); static void *rtl8150_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); @@ -312,7 +310,7 @@ static void read_bulk_callback(struct urb *urb) case -ENOENT: return; /* the urb is in unlink state */ case -ETIMEDOUT: - warn("reset needed may be?.."); + warn("may be reset is needed?.."); goto goon; default: warn("Rx status %d", urb->status); @@ -331,13 +329,13 @@ static void read_bulk_callback(struct urb *urb) netif_rx(dev->rx_skb); dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; - + + spin_lock(&dev->rx_pool_lock); skb = pull_skb(dev); + spin_unlock(&dev->rx_pool_lock); if (!skb) goto resched; - skb->dev = netdev; - skb_reserve(skb, 2); dev->rx_skb = skb; goon: FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), @@ -361,11 +359,16 @@ static void rx_fixup(unsigned long data) dev = (rtl8150_t *)data; + spin_lock_irq(&dev->rx_pool_lock); fill_skb_pool(dev); + spin_unlock_irq(&dev->rx_pool_lock); if (test_bit(RX_URB_FAIL, &dev->flags)) if (dev->rx_skb) goto try_again; - if (!(skb = pull_skb(dev))) + spin_lock_irq(&dev->rx_pool_lock); + skb = pull_skb(dev); + spin_unlock_irq(&dev->rx_pool_lock); + if (skb == NULL) goto tlsched; dev->rx_skb = skb; FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), @@ -426,51 +429,41 @@ static void fill_skb_pool(rtl8150_t *dev) { struct sk_buff *skb; int i; - unsigned long flags; - spin_lock_irqsave(&dev->rx_pool_lock, flags); for (i = 0; i < RX_SKB_POOL_SIZE; i++) { if (dev->rx_skb_pool[i]) continue; skb = dev_alloc_skb(RTL8150_MTU + 2); if (!skb) { - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); return; } skb->dev = dev->netdev; skb_reserve(skb, 2); dev->rx_skb_pool[i] = skb; } - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); } static void free_skb_pool(rtl8150_t *dev) { int i; - spin_lock_irq(&dev->rx_pool_lock); for (i = 0; i < RX_SKB_POOL_SIZE; i++) if (dev->rx_skb_pool[i]) dev_kfree_skb(dev->rx_skb_pool[i]); - spin_unlock_irq(&dev->rx_pool_lock); } -static struct sk_buff *pull_skb(rtl8150_t *dev) +static inline struct sk_buff *pull_skb(rtl8150_t *dev) { struct sk_buff *skb; int i; - unsigned long flags; - spin_lock_irqsave(&dev->rx_pool_lock, flags); for (i = 0; i < RX_SKB_POOL_SIZE; i++) { if (dev->rx_skb_pool[i]) { skb = dev->rx_skb_pool[i]; dev->rx_skb_pool[i] = NULL; - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); return skb; } } - spin_unlock_irqrestore(&dev->rx_pool_lock, flags); return NULL; } @@ -578,8 +571,8 @@ static int rtl8150_open(struct net_device *netdev) if (dev == NULL) { return -ENODEV; } - - dev->rx_skb = pull_skb(dev); + if (dev->rx_skb == NULL) + dev->rx_skb = pull_skb(dev); if (!dev->rx_skb) return -ENOMEM; @@ -816,13 +809,13 @@ static void rtl8150_disconnect(struct usb_device *udev, void *ptr) dev = NULL; } -static int __init usb_rtl8150_init(void) +int __init usb_rtl8150_init(void) { info(DRIVER_DESC " " DRIVER_VERSION); return usb_register(&rtl8150_driver); } -static void __exit usb_rtl8150_exit(void) +void __exit usb_rtl8150_exit(void) { usb_deregister(&rtl8150_driver); } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index bcf22f6c2d9f..b7b61cf82d58 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -428,18 +428,13 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, void *data, u16 size) { int status; - struct usb_ctrlrequest *dr; - - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); - if (!dr) - return -ENOMEM; /* fill in the devrequest structure */ - dr->bRequestType = requesttype; - dr->bRequest = request; - dr->wValue = cpu_to_le16(value); - dr->wIndex = cpu_to_le16(index); - dr->wLength = cpu_to_le16(size); + us->dr->bRequestType = requesttype; + us->dr->bRequest = request; + us->dr->wValue = cpu_to_le16(value); + us->dr->wIndex = cpu_to_le16(index); + us->dr->wLength = cpu_to_le16(size); /* lock the URB */ down(&(us->current_urb_sem)); @@ -452,7 +447,7 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, /* fill the URB */ FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, - (unsigned char*) dr, data, size, + (unsigned char*) us->dr, data, size, usb_stor_blocking_completion, NULL); /* submit the URB */ @@ -1162,7 +1157,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us) int pipe; /* issue the command -- use usb_control_msg() because - * the state machine is not yet alive */ + * this is not a scsi queued-command */ pipe = usb_rcvctrlpipe(us->pusb_dev, 0); result = usb_control_msg(us->pusb_dev, pipe, US_BULK_GET_MAX_LUN, @@ -1181,8 +1176,8 @@ int usb_stor_Bulk_max_lun(struct us_data *us) if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); - /* Use usb_clear_halt() because the state machine - * is not yet alive */ + /* Use usb_clear_halt() because this is not a + * scsi queued-command */ usb_clear_halt(us->pusb_dev, pipe); } @@ -1356,27 +1351,6 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) * Reset routines ***********************************************************************/ -struct us_timeout { - struct us_data *us; - spinlock_t timer_lock; -}; - -/* The timeout event handler - */ -static void usb_stor_timeout_handler(unsigned long to__) -{ - struct us_timeout *to = (struct us_timeout *) to__; - struct us_data *us = to->us; - - US_DEBUGP("Timeout occurred\n"); - - /* abort the current request */ - usb_stor_abort_transport(us); - - /* let the reset routine know we have finished */ - spin_unlock(&to->timer_lock); -} - /* This is the common part of the device reset code. * * It's handy that every transport mechanism uses the control endpoint for @@ -1385,28 +1359,20 @@ static void usb_stor_timeout_handler(unsigned long to__) * Basically, we send a reset with a 20-second timeout, so we don't get * jammed attempting to do the reset. */ -void usb_stor_reset_common(struct us_data *us, u8 request, u8 requesttype, +static int usb_stor_reset_common(struct us_data *us, + u8 request, u8 requesttype, u16 value, u16 index, void *data, u16 size) { int result; - struct us_timeout timeout_data = {us, SPIN_LOCK_UNLOCKED}; - struct timer_list timeout_list; - - /* prepare the timeout handler */ - spin_lock(&timeout_data.timer_lock); - init_timer(&timeout_list); /* A 20-second timeout may seem rather long, but a LaCie * StudioDrive USB2 device takes 16+ seconds to get going * following a powerup or USB attach event. */ - timeout_list.expires = jiffies + 20 * HZ; - timeout_list.data = (unsigned long) &timeout_data; - timeout_list.function = usb_stor_timeout_handler; - add_timer(&timeout_list); - - result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), - request, requesttype, value, index, data, size); + /* Use usb_control_msg() because this is not a queued-command */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + request, requesttype, value, index, data, size, + 20*HZ); if (result < 0) goto Done; @@ -1415,41 +1381,30 @@ void usb_stor_reset_common(struct us_data *us, u8 request, u8 requesttype, schedule_timeout(HZ*6); set_current_state(TASK_RUNNING); + /* Use usb_clear_halt() because this is not a queued-command */ US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); - result = usb_stor_clear_halt(us, + result = usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); if (result < 0) goto Done; US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n"); - result = usb_stor_clear_halt(us, + result = usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out)); Done: - /* prevent the timer from coming back to haunt us */ - if (!del_timer(&timeout_list)) { - /* the handler has already started; wait for it to finish */ - spin_lock(&timeout_data.timer_lock); - /* change the abort into a timeout */ - if (result == -ENOENT) - result = -ETIMEDOUT; - } - /* return a result code based on the result of the control message */ - if (result >= 0) - US_DEBUGP("Soft reset done\n"); - else + if (result < 0) { US_DEBUGP("Soft reset failed: %d\n", result); - - if (result == -ETIMEDOUT) - us->srb->result = DID_TIME_OUT << 16; - else if (result == -ENOENT) - us->srb->result = DID_ABORT << 16; - else if (result < 0) us->srb->result = DID_ERROR << 16; - else + result = FAILED; + } else { + US_DEBUGP("Soft reset done\n"); us->srb->result = GOOD << 1; + result = SUCCESS; + } + return result; } /* This issues a CB[I] Reset to the device in question @@ -1463,10 +1418,9 @@ int usb_stor_CB_reset(struct us_data *us) memset(cmd, 0xFF, sizeof(cmd)); cmd[0] = SEND_DIAGNOSTIC; cmd[1] = 4; - usb_stor_reset_common(us, US_CBI_ADSC, + return usb_stor_reset_common(us, US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, cmd, sizeof(cmd)); - return (us->srb->result == GOOD << 1 ? SUCCESS : FAILED); } /* This issues a Bulk-only Reset to the device in question, including @@ -1476,8 +1430,7 @@ int usb_stor_Bulk_reset(struct us_data *us) { US_DEBUGP("Bulk reset requested\n"); - usb_stor_reset_common(us, US_BULK_RESET_REQUEST, + return usb_stor_reset_common(us, US_BULK_RESET_REQUEST, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, NULL, 0); - return (us->srb->result == GOOD << 1 ? SUCCESS : FAILED); } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 76d70eb5a9bf..a065df6da68d 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -361,65 +361,42 @@ static int usb_stor_control_thread(void * __us) BUG_ON(action != US_ACT_COMMAND); + /* lock the device pointers */ + down(&(us->dev_semaphore)); + /* reject the command if the direction indicator * is UNKNOWN */ if (us->srb->sc_data_direction == SCSI_DATA_UNKNOWN) { US_DEBUGP("UNKNOWN data direction\n"); us->srb->result = DID_ERROR << 16; - scsi_lock(host); - us->srb->scsi_done(us->srb); - us->srb = NULL; - scsi_unlock(host); - continue; } /* reject if target != 0 or if LUN is higher than * the maximum known LUN */ - if (us->srb->target && + else if (us->srb->target && !(us->flags & US_FL_SCM_MULT_TARG)) { US_DEBUGP("Bad target number (%d/%d)\n", us->srb->target, us->srb->lun); us->srb->result = DID_BAD_TARGET << 16; - - scsi_lock(host); - us->srb->scsi_done(us->srb); - us->srb = NULL; - scsi_unlock(host); - continue; } - if (us->srb->lun > us->max_lun) { + else if (us->srb->lun > us->max_lun) { US_DEBUGP("Bad LUN (%d/%d)\n", us->srb->target, us->srb->lun); us->srb->result = DID_BAD_TARGET << 16; - - scsi_lock(host); - us->srb->scsi_done(us->srb); - us->srb = NULL; - scsi_unlock(host); - continue; } /* handle those devices which can't do a START_STOP */ - if ((us->srb->cmnd[0] == START_STOP) && + else if ((us->srb->cmnd[0] == START_STOP) && (us->flags & US_FL_START_STOP)) { US_DEBUGP("Skipping START_STOP command\n"); us->srb->result = GOOD << 1; - - scsi_lock(host); - us->srb->scsi_done(us->srb); - us->srb = NULL; - scsi_unlock(host); - continue; } - /* lock the device pointers */ - down(&(us->dev_semaphore)); - /* our device has gone - pretend not ready */ - if (!test_bit(DEV_ATTACHED, &us->bitflags)) { + else if (!test_bit(DEV_ATTACHED, &us->bitflags)) { US_DEBUGP("Request is for removed device\n"); /* For REQUEST_SENSE, it's the data. But * for anything else, it should look like @@ -443,24 +420,25 @@ static int usb_stor_control_thread(void * __us) sizeof(usb_stor_sense_notready)); us->srb->result = CHECK_CONDITION << 1; } - } else { /* test_bit(DEV_ATTACHED, &us->bitflags) */ + } /* test_bit(DEV_ATTACHED, &us->bitflags) */ - /* Handle those devices which need us to fake - * their inquiry data */ - if ((us->srb->cmnd[0] == INQUIRY) && + /* Handle those devices which need us to fake + * their inquiry data */ + else if ((us->srb->cmnd[0] == INQUIRY) && (us->flags & US_FL_FIX_INQUIRY)) { - unsigned char data_ptr[36] = { - 0x00, 0x80, 0x02, 0x02, - 0x1F, 0x00, 0x00, 0x00}; + unsigned char data_ptr[36] = { + 0x00, 0x80, 0x02, 0x02, + 0x1F, 0x00, 0x00, 0x00}; - US_DEBUGP("Faking INQUIRY command\n"); - fill_inquiry_response(us, data_ptr, 36); - us->srb->result = GOOD << 1; - } else { - /* we've got a command, let's do it! */ - US_DEBUG(usb_stor_show_command(us->srb)); - us->proto_handler(us->srb, us); - } + US_DEBUGP("Faking INQUIRY command\n"); + fill_inquiry_response(us, data_ptr, 36); + us->srb->result = GOOD << 1; + } + + /* we've got a command, let's do it! */ + else { + US_DEBUG(usb_stor_show_command(us->srb)); + us->proto_handler(us->srb, us); } /* unlock the device pointers */ @@ -487,54 +465,114 @@ static int usb_stor_control_thread(void * __us) return 0; } -/* Set up the IRQ pipe and handler +/* Set up the URB, the usb_ctrlrequest, and the IRQ pipe and handler. + * ss->dev_semaphore should already be locked. * Note that this function assumes that all the data in the us_data * strucuture is current. This includes the ep_int field, which gives us * the endpoint for the interrupt. * Returns non-zero on failure, zero on success */ -static int usb_stor_allocate_irq(struct us_data *ss) +static int usb_stor_allocate_urbs(struct us_data *ss) { unsigned int pipe; int maxp; int result; - US_DEBUGP("Allocating IRQ for CBI transport\n"); - - /* lock access to the data structure */ - down(&(ss->irq_urb_sem)); - - /* allocate the URB */ - ss->irq_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ss->irq_urb) { - up(&(ss->irq_urb_sem)); - US_DEBUGP("couldn't allocate interrupt URB"); + /* allocate the URB we're going to use */ + US_DEBUGP("Allocating URB\n"); + ss->current_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ss->current_urb) { + US_DEBUGP("allocation failed\n"); return 1; } - /* calculate the pipe and max packet size */ - pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int->bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK); - maxp = usb_maxpacket(ss->pusb_dev, pipe, usb_pipeout(pipe)); - if (maxp > sizeof(ss->irqbuf)) - maxp = sizeof(ss->irqbuf); - - /* fill in the URB with our data */ - FILL_INT_URB(ss->irq_urb, ss->pusb_dev, pipe, ss->irqbuf, maxp, - usb_stor_CBI_irq, ss, ss->ep_int->bInterval); - - /* submit the URB for processing */ - result = usb_submit_urb(ss->irq_urb, GFP_KERNEL); - US_DEBUGP("usb_submit_urb() returns %d\n", result); - if (result) { - usb_free_urb(ss->irq_urb); - up(&(ss->irq_urb_sem)); + /* allocate the usb_ctrlrequest for control packets */ + US_DEBUGP("Allocating usb_ctrlrequest\n"); + ss->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); + if (!ss->dr) { + US_DEBUGP("allocation failed\n"); return 2; } - /* unlock the data structure and return success */ + /* allocate the IRQ URB, if it is needed */ + if (ss->protocol == US_PR_CBI) { + US_DEBUGP("Allocating IRQ for CBI transport\n"); + + /* lock access to the data structure */ + down(&(ss->irq_urb_sem)); + + /* allocate the URB */ + ss->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ss->irq_urb) { + up(&(ss->irq_urb_sem)); + US_DEBUGP("couldn't allocate interrupt URB"); + return 3; + } + + /* calculate the pipe and max packet size */ + pipe = usb_rcvintpipe(ss->pusb_dev, + ss->ep_int->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + maxp = usb_maxpacket(ss->pusb_dev, pipe, usb_pipeout(pipe)); + if (maxp > sizeof(ss->irqbuf)) + maxp = sizeof(ss->irqbuf); + + /* fill in the URB with our data */ + FILL_INT_URB(ss->irq_urb, ss->pusb_dev, pipe, ss->irqbuf, + maxp, usb_stor_CBI_irq, ss, ss->ep_int->bInterval); + + /* submit the URB for processing */ + result = usb_submit_urb(ss->irq_urb, GFP_KERNEL); + US_DEBUGP("usb_submit_urb() returns %d\n", result); + if (result) { + up(&(ss->irq_urb_sem)); + return 4; + } + + /* unlock the data structure */ + up(&(ss->irq_urb_sem)); + + } /* ss->protocol == US_PR_CBI */ + + return 0; /* success */ +} + +/* Deallocate the URB, the usb_ctrlrequest, and the IRQ pipe. + * ss->dev_semaphore must already be locked. + */ +static void usb_stor_deallocate_urbs(struct us_data *ss) +{ + int result; + + /* release the IRQ, if we have one */ + down(&(ss->irq_urb_sem)); + if (ss->irq_urb) { + US_DEBUGP("-- releasing irq URB\n"); + result = usb_unlink_urb(ss->irq_urb); + US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); + usb_free_urb(ss->irq_urb); + ss->irq_urb = NULL; + } up(&(ss->irq_urb_sem)); - return 0; + + /* free the usb_ctrlrequest buffer */ + if (ss->dr) { + kfree(ss->dr); + ss->dr = NULL; + } + + /* free up the main URB for this device */ + if (ss->current_urb) { + US_DEBUGP("-- releasing main URB\n"); + result = usb_unlink_urb(ss->current_urb); + US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); + usb_free_urb(ss->current_urb); + ss->current_urb = NULL; + } + + /* mark the device as gone */ + clear_bit(DEV_ATTACHED, &ss->bitflags); + usb_put_dev(ss->pusb_dev); + ss->pusb_dev = NULL; } /* Probe to see if a new device is actually a SCSI device */ @@ -712,13 +750,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum, USB_ENDPOINT_NUMBER_MASK; ss->ep_int = ep_int; - /* allocate an IRQ callback if one is needed */ - if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) - goto BadDevice; - - /* allocate the URB we're going to use */ - ss->current_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ss->current_urb) + /* allocate the URB, the usb_ctrlrequest, and the IRQ URB */ + if (usb_stor_allocate_urbs(ss)) goto BadDevice; /* Re-Initialize the device if it needs it */ @@ -741,11 +774,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum, memset(ss, 0, sizeof(struct us_data)); new_device = 1; - /* allocate the URB we're going to use */ - ss->current_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!ss->current_urb) - goto BadDevice; - /* Initialize the mutexes only when the struct is new */ init_completion(&(ss->notify)); init_MUTEX_LOCKED(&(ss->ip_waitq)); @@ -943,8 +971,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum, } US_DEBUGP("Protocol: %s\n", ss->protocol_name); - /* allocate an IRQ callback if one is needed */ - if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) + /* allocate the URB, the usb_ctrlrequest, and the IRQ URB */ + if (usb_stor_allocate_urbs(ss)) goto BadDevice; /* @@ -1020,26 +1048,11 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum, /* we come here if there are any problems */ BadDevice: US_DEBUGP("storage_probe() failed\n"); - down(&ss->irq_urb_sem); - if (ss->irq_urb) { - usb_unlink_urb(ss->irq_urb); - usb_free_urb(ss->irq_urb); - ss->irq_urb = NULL; - } - up(&ss->irq_urb_sem); - if (ss->current_urb) { - usb_unlink_urb(ss->current_urb); - usb_free_urb(ss->current_urb); - ss->current_urb = NULL; - } - - clear_bit(DEV_ATTACHED, &ss->bitflags); - ss->pusb_dev = NULL; + usb_stor_deallocate_urbs(ss); if (new_device) kfree(ss); else up(&ss->dev_semaphore); - usb_put_dev(dev); return NULL; } @@ -1047,7 +1060,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum, static void storage_disconnect(struct usb_device *dev, void *ptr) { struct us_data *ss = ptr; - int result; US_DEBUGP("storage_disconnect() called\n"); @@ -1057,33 +1069,8 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) return; } - /* lock access to the device data structure */ down(&(ss->dev_semaphore)); - - /* release the IRQ, if we have one */ - down(&(ss->irq_urb_sem)); - if (ss->irq_urb) { - US_DEBUGP("-- releasing irq URB\n"); - result = usb_unlink_urb(ss->irq_urb); - US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); - usb_free_urb(ss->irq_urb); - ss->irq_urb = NULL; - } - up(&(ss->irq_urb_sem)); - - /* free up the main URB for this device */ - US_DEBUGP("-- releasing main URB\n"); - result = usb_unlink_urb(ss->current_urb); - US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); - usb_free_urb(ss->current_urb); - ss->current_urb = NULL; - - /* mark the device as gone */ - usb_put_dev(ss->pusb_dev); - ss->pusb_dev = NULL; - clear_bit(DEV_ATTACHED, &ss->bitflags); - - /* unlock access to the device data structure */ + usb_stor_deallocate_urbs(ss); up(&(ss->dev_semaphore)); } diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index d0f1f24ded28..57f175ec2866 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -185,6 +185,7 @@ struct us_data { /* control and bulk communications data */ struct semaphore current_urb_sem; /* to protect irq_urb */ struct urb *current_urb; /* non-int USB requests */ + struct usb_ctrlrequest *dr; /* control requests */ /* the semaphore for sleeping the control thread */ struct semaphore sema; /* to sleep thread on */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index ae15d461fd9e..64fb0078a6cc 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -189,9 +189,6 @@ static struct usb_driver skel_driver = { name: "skeleton", probe: skel_probe, disconnect: skel_disconnect, - fops: &skel_fops, - minor: USB_SKEL_MINOR_BASE, - num_minors: MAX_DEVICES, id_table: skel_table, }; @@ -532,29 +529,18 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru } down (&minor_table_mutex); - retval = usb_register_dev (&skel_driver, 1, &minor); + retval = usb_register_dev (&skel_fops, USB_SKEL_MINOR_BASE, 1, &minor); if (retval) { - if (retval != -ENODEV) { - /* something prevented us from registering this driver */ - err ("Not able to get a minor for this device."); - goto exit; - } - /* we could not get a dynamic minor, so lets find one of our own */ - for (minor = 0; minor < MAX_DEVICES; ++minor) { - if (minor_table[minor] == NULL) - break; - } - if (minor >= MAX_DEVICES) { - err ("Too many devices plugged in, can not handle this device."); - goto exit; - } + /* something prevented us from registering this driver */ + err ("Not able to get a minor for this device."); + goto exit; } /* allocate memory for our device state and intialize it */ dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL); if (dev == NULL) { err ("Out of memory"); - goto exit; + goto exit_minor; } memset (dev, 0x00, sizeof (*dev)); minor_table[minor] = dev; @@ -628,6 +614,9 @@ error: skel_delete (dev); dev = NULL; +exit_minor: + usb_deregister_dev (1, minor); + exit: up (&minor_table_mutex); return dev; @@ -655,7 +644,7 @@ static void skel_disconnect(struct usb_device *udev, void *ptr) devfs_unregister (dev->devfs); /* give back our dynamic minor */ - usb_deregister_dev (&skel_driver, 1, minor); + usb_deregister_dev (1, minor); /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 43c5d84eb1ca..7fcb72dc8e03 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -954,7 +954,7 @@ static int notesize(struct memelfnote *en) int sz; sz = sizeof(struct elf_note); - sz += roundup(strlen(en->name), 4); + sz += roundup(strlen(en->name) + 1, 4); sz += roundup(en->datasz, 4); return sz; @@ -989,7 +989,7 @@ static int writenote(struct memelfnote *men, struct file *file) { struct elf_note en; - en.n_namesz = strlen(men->name); + en.n_namesz = strlen(men->name) + 1; en.n_descsz = men->datasz; en.n_type = men->type; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index a354c9fd4574..a5d332064abe 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -281,9 +281,13 @@ struct inode_operations coda_file_inode_operations = { static int coda_statfs(struct super_block *sb, struct statfs *buf) { int error; + + lock_kernel(); error = venus_statfs(sb, buf); + unlock_kernel(); + if (error) { /* fake something like AFS does */ buf->f_blocks = 9000000; diff --git a/fs/driverfs/inode.c b/fs/driverfs/inode.c index 2fd9126199b2..e6a098cdd04b 100644 --- a/fs/driverfs/inode.c +++ b/fs/driverfs/inode.c @@ -119,6 +119,9 @@ struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev) case S_IFDIR: inode->i_op = &driverfs_dir_inode_operations; inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; @@ -149,10 +152,8 @@ static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) lock_kernel(); dentry->d_op = &driverfs_dentry_dir_ops; res = driverfs_mknod(dir, dentry, mode | S_IFDIR, 0); - if (!res) { + if (!res) dir->i_nlink++; - dentry->d_inode->i_nlink++; - } unlock_kernel(); return res; } diff --git a/fs/ext3/super.c b/fs/ext3/super.c index f08897e90ad2..2e650dcb8b3a 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -500,7 +500,7 @@ static struct super_operations ext3_sops = { write_super: ext3_write_super, /* BKL not held. We take it. Needed? */ write_super_lockfs: ext3_write_super_lockfs, /* BKL not held. Take it */ unlockfs: ext3_unlockfs, /* BKL not held. We take it */ - statfs: ext3_statfs, /* BKL held */ + statfs: ext3_statfs, /* BKL not held. */ remount_fs: ext3_remount, /* BKL held */ }; diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index d937b64926e3..45886178fda3 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -98,7 +98,7 @@ vxfs_put_super(struct super_block *sbp) * Zero. * * Locking: - * We are under bkl and @sbp->s_lock. + * No locks held. * * Notes: * This is everything but complete... diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 1d906d3535bb..b8306f5f5db9 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -134,6 +134,8 @@ static unsigned count_bitmaps(struct super_block *s) int hpfs_statfs(struct super_block *s, struct statfs *buf) { + lock_kernel(); + /*if (s->s_hpfs_n_free == -1) {*/ s->s_hpfs_n_free = count_bitmaps(s); s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap); @@ -146,6 +148,9 @@ int hpfs_statfs(struct super_block *s, struct statfs *buf) buf->f_files = s->s_hpfs_dirband_size / 4; buf->f_ffree = s->s_hpfs_n_free_dnodes; buf->f_namelen = 254; + + unlock_kernel(); + return 0; } diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index e71bcbfc3caa..ad179ff4bbdc 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -383,7 +383,11 @@ int jffs_statfs(struct super_block *sb, struct statfs *buf) { struct jffs_control *c = (struct jffs_control *) sb->u.generic_sbp; - struct jffs_fmcontrol *fmc = c->fmc; + struct jffs_fmcontrol *fmc; + + lock_kernel(); + + fmc = c->fmc; D2(printk("jffs_statfs()\n")); @@ -401,6 +405,9 @@ jffs_statfs(struct super_block *sb, struct statfs *buf) buf->f_ffree = buf->f_bfree; /* buf->f_fsid = 0; */ buf->f_namelen = JFFS_MAX_NAME_LEN; + + unlock_kernel(); + return 0; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 211f62a027a1..d643d9540177 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -474,6 +474,8 @@ nfs_statfs(struct super_block *sb, struct statfs *buf) struct nfs_fsinfo res; int error; + lock_kernel(); + error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root->d_inode), &res); buf->f_type = NFS_SUPER_MAGIC; if (error < 0) @@ -491,11 +493,17 @@ nfs_statfs(struct super_block *sb, struct statfs *buf) if (res.namelen == 0 || res.namelen > server->namelen) res.namelen = server->namelen; buf->f_namelen = res.namelen; + + out: + unlock_kernel(); + return 0; + out_err: - printk("nfs_statfs: statfs error = %d\n", -error); + printk(KERN_WARNING "nfs_statfs: statfs error = %d\n", -error); buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; - return 0; + goto out; + } static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index e921ef9d19ff..9bd1acc0a5c2 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog @@ -28,6 +28,17 @@ ToDo: - Enable NFS exporting of NTFS. - Use fake inodes for address space i/o. +2.0.14 - Run list merging code cleanup, minor locking changes, typo fixes. + + - Change fs/ntfs/super.c::ntfs_statfs() to not rely on BKL by moving + the locking out of super.c::get_nr_free_mft_records() and taking and + dropping the mftbmp_lock rw_semaphore in ntfs_statfs() itself. + - Bring attribute run list merging code (fs/ntfs/attrib.c) in sync with + current userspace ntfs library code. This means that if a merge + fails the original run lists are always left unmodified instead of + being silently corrupted. + - Misc typo fixes. + 2.0.13 - Use iget5_locked() in preparation for fake inodes and small cleanups. - Remove nr_mft_bits and the now superfluous union with nr_mft_records diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index e6fe82353289..208c46697eb9 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \ mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o -EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.13\" +EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.14\" ifeq ($(CONFIG_NTFS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index c0033a70b8b9..09c9683474cd 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -27,338 +27,433 @@ /* Temporary helper functions -- might become macros */ /** - * rl_mm - run_list memmove + * ntfs_rl_mm - run_list memmove * * It is up to the caller to serialize access to the run list @base. */ -static inline void rl_mm(run_list_element *base, int dst, int src, int size) +static inline void ntfs_rl_mm(run_list_element *base, int dst, int src, + int size) { - if ((dst != src) && (size > 0)) - memmove (base + dst, base + src, size * sizeof (*base)); + if (likely((dst != src) && (size > 0))) + memmove(base + dst, base + src, size * sizeof (*base)); } /** - * rl_mc - run_list memory copy + * ntfs_rl_mc - run_list memory copy * * It is up to the caller to serialize access to the run lists @dstbase and * @srcbase. */ -static inline void rl_mc(run_list_element *dstbase, int dst, +static inline void ntfs_rl_mc(run_list_element *dstbase, int dst, run_list_element *srcbase, int src, int size) { - if (size > 0) - memcpy (dstbase+dst, srcbase+src, size * sizeof (*dstbase)); + if (likely(size > 0)) + memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); } /** * ntfs_rl_realloc - Reallocate memory for run_lists - * @orig: The original memory allocation - * @old: The number of run_lists in the original - * @new: The number of run_lists we need space for + * @rl: original run list + * @old_size: number of run list elements in the original run list @rl + * @new_size: number of run list elements we need space for * * As the run_lists grow, more memory will be required. To prevent the * kernel having to allocate and reallocate large numbers of small bits of * memory, this function returns and entire page of memory. * - * It is up to the caller to serialize access to the run list @orig. + * It is up to the caller to serialize access to the run list @rl. * * N.B. If the new allocation doesn't require a different number of pages in * memory, the function will return the original pointer. * - * Return: Pointer The newly allocated, or recycled, memory. - * - * Errors: -ENOMEM, Not enough memory to allocate run list array. - * -EINVAL, Invalid parameters were passed in. + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return -errno. The following error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EINVAL - Invalid parameters were passed in. */ -static inline run_list_element *ntfs_rl_realloc(run_list_element *orig, - int old, int new) +static inline run_list_element *ntfs_rl_realloc(run_list_element *rl, + int old_size, int new_size) { - run_list_element *nrl; + run_list_element *new_rl; - old = PAGE_ALIGN (old * sizeof (*orig)); - new = PAGE_ALIGN (new * sizeof (*orig)); - if (old == new) - return orig; + old_size = PAGE_ALIGN(old_size * sizeof(*rl)); + new_size = PAGE_ALIGN(new_size * sizeof(*rl)); + if (old_size == new_size) + return rl; - nrl = ntfs_malloc_nofs (new); - if (!nrl) - return ERR_PTR (-ENOMEM); + new_rl = ntfs_malloc_nofs(new_size); + if (unlikely(!new_rl)) + return ERR_PTR(-ENOMEM); - if (orig) { - memcpy (nrl, orig, min (old, new)); - ntfs_free (orig); + if (likely(rl != NULL)) { + if (unlikely(old_size > new_size)) + old_size = new_size; + memcpy(new_rl, rl, old_size); + ntfs_free(rl); } - return nrl; + return new_rl; } /** - * ntfs_rl_merge - Join together two run_lists - * @one: The first run_list and destination - * @two: The second run_list + * ntfs_are_rl_mergeable - test if two run lists can be joined together + * @dst: original run list + * @src: new run list to test for mergeability with @dst * - * If possible merge together two run_lists. For this, their VCNs and LCNs + * Test if two run lists can be joined together. For this, their VCNs and LCNs * must be adjacent. * - * It is up to the caller to serialize access to the run lists @one and @two. + * It is up to the caller to serialize access to the run lists @dst and @src. * - * Return: TRUE Success, the run_lists were merged - * FALSE Failure, the run_lists were not merged + * Return: TRUE Success, the run lists can be merged. + * FALSE Failure, the run lists cannot be merged. */ -static inline BOOL ntfs_rl_merge(run_list_element *one, run_list_element *two) +static inline BOOL ntfs_are_rl_mergeable(run_list_element *dst, + run_list_element *src) { - BUG_ON (!one || !two); + BUG_ON(!dst || !src); - if ((one->lcn < 0) || (two->lcn < 0)) /* Are we merging holes? */ + if ((dst->lcn < 0) || (src->lcn < 0)) /* Are we merging holes? */ return FALSE; - if ((one->lcn + one->length) != two->lcn) /* Are the runs contiguous? */ + if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */ return FALSE; - if ((one->vcn + one->length) != two->vcn) /* Are the runs misaligned? */ + if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */ return FALSE; - one->length += two->length; return TRUE; } /** - * ntfs_rl_append - Append a run_list after the given element - * @orig: The original run_list to be worked on. - * @osize: The number of elements in @orig (including end marker). - * @new: The run_list to be inserted. - * @nsize: The number of elements in @new (excluding end marker). - * @loc: Append the new run_list after this element in @orig. + * __ntfs_rl_merge - merge two run lists without testing if they can be merged + * @dst: original, destination run list + * @src: new run list to merge with @dst * - * Append a run_list after element @loc in @orig. Merge the right end of - * the new run_list, if necessary. Adjust the size of the hole before the - * appended run_list. + * Merge the two run lists, writing into the destination run list @dst. The + * caller must make sure the run lists can be merged or this will corrupt the + * destination run list. * - * It is up to the caller to serialize access to the run lists @orig and @new. + * It is up to the caller to serialize access to the run lists @dst and @src. + */ +static inline void __ntfs_rl_merge(run_list_element *dst, run_list_element *src) +{ + dst->length += src->length; +} + +/** + * ntfs_rl_merge - test if two run lists can be joined together and merge them + * @dst: original, destination run list + * @src: new run list to merge with @dst * - * Return: Pointer, The new, combined, run_list + * Test if two run lists can be joined together. For this, their VCNs and LCNs + * must be adjacent. If they can be merged, perform the merge, writing into + * the destination run list @dst. * - * Errors: -ENOMEM, Not enough memory to allocate run list array. - * -EINVAL, Invalid parameters were passed in. + * It is up to the caller to serialize access to the run lists @dst and @src. + * + * Return: TRUE Success, the run lists have been merged. + * FALSE Failure, the run lists cannot be merged and have not been + * modified. */ -static inline run_list_element *ntfs_rl_append(run_list_element *orig, - int osize, run_list_element *new, int nsize, int loc) +static inline BOOL ntfs_rl_merge(run_list_element *dst, run_list_element *src) +{ + BOOL merge = ntfs_are_rl_mergeable(dst, src); + + if (merge) + __ntfs_rl_merge(dst, src); + return merge; +} + +/** + * ntfs_rl_append - append a run list after a given element + * @dst: original run list to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: run list to be inserted into @dst + * @ssize: number of elements in @src (excluding end marker) + * @loc: append the new run list @src after this element in @dst + * + * Append the run list @src after element @loc in @dst. Merge the right end of + * the new run list, if necessary. Adjust the size of the hole before the + * appended run list. + * + * It is up to the caller to serialize access to the run lists @dst and @src. + * + * On success, return a pointer to the new, combined, run list. Note, both + * run lists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned run list + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both run lists are left unmodified. The following + * error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EINVAL - Invalid parameters were passed in. + */ +static inline run_list_element *ntfs_rl_append(run_list_element *dst, + int dsize, run_list_element *src, int ssize, int loc) { - run_list_element *res; BOOL right; + int magic; - BUG_ON (!orig || !new); + BUG_ON(!dst || !src); + + /* First, check if the right hand end needs merging. */ + right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); + + /* Space required: @dst size + @src size, less one if we merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); + if (IS_ERR(dst)) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original run lists. + */ /* First, merge the right hand end, if necessary. */ - right = ntfs_rl_merge (new + nsize - 1, orig + loc + 1); + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); - /* Space required: Orig size + New size, less one if we merged. */ - res = ntfs_rl_realloc (orig, osize, osize + nsize - right); - if (IS_ERR (res)) - return res; + magic = loc + ssize; - /* Move the tail of Orig out of the way, then copy in New. */ - rl_mm (res, loc + 1 + nsize, loc + 1 + right, osize - loc - 1 - right); - rl_mc (res, loc + 1, new, 0, nsize); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, magic + 1, loc + 1 + right, dsize - loc - 1 - right); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); /* Adjust the size of the preceding hole. */ - res[loc].length = res[loc+1].vcn - res[loc].vcn; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; /* We may have changed the length of the file, so fix the end marker */ - if (res[loc+nsize+1].lcn == LCN_ENOENT) - res[loc+nsize+1].vcn = res[loc+nsize].vcn + res[loc+nsize].length; + if (dst[magic + 1].lcn == LCN_ENOENT) + dst[magic + 1].vcn = dst[magic].vcn + dst[magic].length; - return res; + return dst; } /** - * ntfs_rl_insert - Insert a run_list into another - * @orig: The original run_list to be worked on. - * @osize: The number of elements in @orig (including end marker). - * @new: The run_list to be inserted. - * @nsize: The number of elements in @new (excluding end marker). - * @loc: Insert the new run_list before this element in @orig. - * - * Insert a run_list before element @loc in @orig. Merge the left end of - * the new run_list, if necessary. Adjust the size of the hole after the - * inserted run_list. - * - * It is up to the caller to serialize access to the run lists @orig and @new. - * - * Return: Pointer, The new, combined, run_list - * - * Errors: -ENOMEM, Not enough memory to allocate run list array. - * -EINVAL, Invalid parameters were passed in. + * ntfs_rl_insert - insert a run list into another + * @dst: original run list to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new run list to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: insert the new run list @src before this element in @dst + * + * Insert the run list @src before element @loc in the run list @dst. Merge the + * left end of the new run list, if necessary. Adjust the size of the hole + * after the inserted run list. + * + * It is up to the caller to serialize access to the run lists @dst and @src. + * + * On success, return a pointer to the new, combined, run list. Note, both + * run lists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned run list + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both run lists are left unmodified. The following + * error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EINVAL - Invalid parameters were passed in. */ -static inline run_list_element *ntfs_rl_insert(run_list_element *orig, - int osize, run_list_element *new, int nsize, int loc) +static inline run_list_element *ntfs_rl_insert(run_list_element *dst, + int dsize, run_list_element *src, int ssize, int loc) { - run_list_element *res; BOOL left = FALSE; BOOL disc = FALSE; /* Discontinuity */ BOOL hole = FALSE; /* Following a hole */ + int magic; - BUG_ON (!orig || !new); + BUG_ON(!dst || !src); - /* disc => Discontinuity between the end of Orig and the start of New. + /* disc => Discontinuity between the end of @dst and the start of @src. * This means we might need to insert a hole. - * hole => Orig ends with a hole or an unmapped region which we can + * hole => @dst ends with a hole or an unmapped region which we can * extend to match the discontinuity. */ - if (loc == 0) { - disc = (new[0].vcn > 0); - } else { - left = ntfs_rl_merge (orig + loc - 1, new); + if (loc == 0) + disc = (src[0].vcn > 0); + else { + s64 merged_length; + + left = ntfs_are_rl_mergeable(dst + loc - 1, src); + + merged_length = dst[loc - 1].length; + if (left) + merged_length += src->length; - disc = (new[0].vcn > (orig[loc-1].vcn + orig[loc-1].length)); + disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); if (disc) - hole = (orig[loc-1].lcn == LCN_HOLE); + hole = (dst[loc - 1].lcn == LCN_HOLE); } - /* Space required: Orig size + New size, less one if we merged, - * plus one if there was a discontinuity, less one for a trailing hole */ - res = ntfs_rl_realloc (orig, osize, osize + nsize - left + disc - hole); - if (IS_ERR (res)) - return res; + /* Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity, less one for a trailing hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc - hole); + if (IS_ERR(dst)) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original run list. + */ + + if (left) + __ntfs_rl_merge(dst + loc - 1, src); - /* Move the tail of Orig out of the way, then copy in New. */ - rl_mm (res, loc + nsize - left + disc - hole, loc, osize - loc); - rl_mc (res, loc + disc - hole, new, left, nsize - left); + magic = loc + ssize - left + disc - hole; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, magic, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc - hole, src, left, ssize - left); /* Adjust the VCN of the last run ... */ - if (res[loc+nsize-left+disc-hole].lcn <= LCN_HOLE) { - res[loc+nsize-left+disc-hole].vcn = - res[loc+nsize-left+disc-hole-1].vcn + - res[loc+nsize-left+disc-hole-1].length; - } + if (dst[magic].lcn <= LCN_HOLE) + dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length; /* ... and the length. */ - if ((res[loc+nsize-left+disc-hole].lcn == LCN_HOLE) || - (res[loc+nsize-left+disc-hole].lcn == LCN_RL_NOT_MAPPED)) { - res[loc+nsize-left+disc-hole].length = - res[loc+nsize-left+disc-hole+1].vcn - - res[loc+nsize-left+disc-hole].vcn; - } + if (dst[magic].lcn == LCN_HOLE || dst[magic].lcn == LCN_RL_NOT_MAPPED) + dst[magic].length = dst[magic + 1].vcn - dst[magic].vcn; /* Writing beyond the end of the file and there's a discontinuity. */ if (disc) { - if (hole) { - res[loc-1].length = res[loc].vcn - res[loc-1].vcn; - } else { + if (hole) + dst[loc - 1].length = dst[loc].vcn - dst[loc - 1].vcn; + else { if (loc > 0) { - res[loc].vcn = res[loc-1].vcn + - res[loc-1].length; - res[loc].length = res[loc+1].vcn - res[loc].vcn; + dst[loc].vcn = dst[loc - 1].vcn + + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - + dst[loc].vcn; } else { - res[loc].vcn = 0; - res[loc].length = res[loc+1].vcn; + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; } - res[loc].lcn = LCN_RL_NOT_MAPPED; + dst[loc].lcn = LCN_RL_NOT_MAPPED; } - if (res[loc+nsize-left+disc].lcn == LCN_ENOENT) - res[loc+nsize-left+disc].vcn = res[loc+nsize-left+disc-1].vcn + - res[loc+nsize-left+disc-1].length; - } + magic += hole; - return res; + if (dst[magic].lcn == LCN_ENOENT) + dst[magic].vcn = dst[magic - 1].vcn + + dst[magic - 1].length; + } + return dst; } /** - * ntfs_rl_replace - Overwrite a run_list element with another run_list - * @orig: The original run_list to be worked on. - * @osize: The number of elements in @orig (including end marker). - * @new: The run_list to be inserted. - * @nsize: The number of elements in @new (excluding end marker). - * @loc: Index of run_list @orig to overwrite with @new. - * - * Replace the run_list at @loc with @new. Merge the left and right ends of - * the inserted run_list, if necessary. - * - * It is up to the caller to serialize access to the run lists @orig and @new. - * - * Return: Pointer, The new, combined, run_list - * - * Errors: -ENOMEM, Not enough memory to allocate run list array. - * -EINVAL, Invalid parameters were passed in. + * ntfs_rl_replace - overwrite a run_list element with another run list + * @dst: original run list to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new run list to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in run list @dst to overwrite with @src + * + * Replace the run list element @dst at @loc with @src. Merge the left and + * right ends of the inserted run list, if necessary. + * + * It is up to the caller to serialize access to the run lists @dst and @src. + * + * On success, return a pointer to the new, combined, run list. Note, both + * run lists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned run list + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both run lists are left unmodified. The following + * error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EINVAL - Invalid parameters were passed in. */ -static inline run_list_element *ntfs_rl_replace(run_list_element *orig, - int osize, run_list_element *new, int nsize, int loc) +static inline run_list_element *ntfs_rl_replace(run_list_element *dst, + int dsize, run_list_element *src, int ssize, int loc) { - run_list_element *res; BOOL left = FALSE; BOOL right; + int magic; - BUG_ON (!orig || !new); + BUG_ON(!dst || !src); /* First, merge the left and right ends, if necessary. */ - right = ntfs_rl_merge (new + nsize - 1, orig + loc + 1); + right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); if (loc > 0) - left = ntfs_rl_merge (orig + loc - 1, new); + left = ntfs_are_rl_mergeable(dst + loc - 1, src); + + /* Allocate some space. We'll need less if the left, right, or both + * ends were merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left - right); + if (IS_ERR(dst)) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original run lists. + */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); - /* Allocate some space. We'll need less if the left, right - * or both ends were merged. */ - res = ntfs_rl_realloc (orig, osize, osize + nsize - left - right); - if (IS_ERR (res)) - return res; + /* FIXME: What does this mean? (AIA) */ + magic = loc + ssize - left; - /* Move the tail of Orig out of the way, then copy in New. */ - rl_mm (res, loc + nsize - left, loc + right + 1, - osize - loc - right - 1); - rl_mc (res, loc, new, left, nsize - left); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, magic, loc + right + 1, dsize - loc - right - 1); + ntfs_rl_mc(dst, loc, src, left, ssize - left); /* We may have changed the length of the file, so fix the end marker */ - if (res[loc+nsize-left].lcn == LCN_ENOENT) - res[loc+nsize-left].vcn = res[loc+nsize-left-1].vcn + - res[loc+nsize-left-1].length; - return res; + if (dst[magic].lcn == LCN_ENOENT) + dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length; + return dst; } /** - * ntfs_rl_split - Insert a run_list into the centre of a hole - * @orig: The original run_list to be worked on. - * @osize: The number of elements in @orig (including end marker). - * @new: The run_list to be inserted. - * @nsize: The number of elements in @new (excluding end marker). - * @loc: Index of run_list in @orig to split with @new. - * - * Split the run_list at @loc into two and insert @new. No merging of - * run_lists is necessary. Adjust the size of the holes either side. - * - * It is up to the caller to serialize access to the run lists @orig and @new. - * - * Return: Pointer, The new, combined, run_list - * - * Errors: -ENOMEM, Not enough memory to allocate run list array. - * -EINVAL, Invalid parameters were passed in. + * ntfs_rl_split - insert a run list into the centre of a hole + * @dst: original run list to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new run list to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in run list @dst at which to split and insert @src + * + * Split the run list @dst at @loc into two and insert @new in between the two + * fragments. No merging of run lists is necessary. Adjust the size of the + * holes either side. + * + * It is up to the caller to serialize access to the run lists @dst and @src. + * + * On success, return a pointer to the new, combined, run list. Note, both + * run lists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned run list + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both run lists are left unmodified. The following + * error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EINVAL - Invalid parameters were passed in. */ -static inline run_list_element *ntfs_rl_split(run_list_element *orig, int osize, - run_list_element *new, int nsize, int loc) +static inline run_list_element *ntfs_rl_split(run_list_element *dst, int dsize, + run_list_element *src, int ssize, int loc) { - run_list_element *res; - - BUG_ON (!orig || !new); + BUG_ON(!dst || !src); - /* Space required: Orig size + New size + One new hole. */ - res = ntfs_rl_realloc (orig, osize, osize + nsize + 1); - if (IS_ERR (res)) - return res; + /* Space required: @dst size + @src size + one new hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); + if (IS_ERR(dst)) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original run lists. + */ - /* Move the tail of Orig out of the way, then copy in New. */ - rl_mm (res, loc + 1 + nsize, loc, osize - loc); - rl_mc (res, loc + 1, new, 0, nsize); + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); - /* Adjust the size of the holes either size of New. */ - res[loc].length = res[loc+1].vcn - res[loc].vcn; - res[loc+nsize+1].vcn = res[loc+nsize].vcn + res[loc+nsize].length; - res[loc+nsize+1].length = res[loc+nsize+2].vcn - res[loc+nsize+1].vcn; + /* Adjust the size of the holes either size of @src. */ + dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; + dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; + dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; - return res; + return dst; } /** - * merge_run_lists - merge two run_lists into one - * @drl: The original run_list. - * @srl: The new run_list to be merge into @drl. + * ntfs_merge_run_lists - merge two run_lists into one + * @drl: original run list to be worked on + * @srl: new run list to be merged into @drl * - * First we sanity check the two run_lists to make sure that they are sensible - * and can be merged. The @srl run_list must be either after the @drl run_list - * or completely within a hole in @drl. + * First we sanity check the two run lists @srl and @drl to make sure that they + * are sensible and can be merged. The run list @srl must be either after the + * run list @drl or completely within a hole (or unmapped region) in @drl. * * It is up to the caller to serialize access to the run lists @drl and @srl. * @@ -366,25 +461,28 @@ static inline run_list_element *ntfs_rl_split(run_list_element *orig, int osize, * 1. When attribute lists are used and a further extent is being mapped. * 2. When new clusters are allocated to fill a hole or extend a file. * - * There are four possible ways @srl can be merged. It can be inserted at - * the beginning of a hole; split the hole in two; appended at the end of - * a hole; replace the whole hole. It can also be appended to the end of - * the run_list, which is just a variant of the insert case. - * - * N.B. Either, or both, of the input pointers may be freed if the function - * is successful. Only the returned pointer may be used. - * - * If the function fails, neither of the input run_lists may be safe. - * - * Return: Pointer, The resultant merged run_list. - * - * Errors: -ENOMEM, Not enough memory to allocate run list array. - * -EINVAL, Invalid parameters were passed in. - * -ERANGE, The run_lists overlap and cannot be merged. + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the run list, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, run list. Note, both + * run lists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned run list + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both run lists are left unmodified. The following + * error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EINVAL - Invalid parameters were passed in. + * -ERANGE - The run lists overlap and cannot be merged. */ -run_list_element *merge_run_lists(run_list_element *drl, run_list_element *srl) +run_list_element *ntfs_merge_run_lists(run_list_element *drl, + run_list_element *srl) { - run_list_element *nrl; /* New run list. */ int di, si; /* Current index into @[ds]rl. */ int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ int dins; /* Index into @drl at which to insert @srl. */ @@ -392,49 +490,49 @@ run_list_element *merge_run_lists(run_list_element *drl, run_list_element *srl) int dfinal, sfinal; /* The last index into @[ds]rl with lcn >= LCN_HOLE. */ int marker = 0; + VCN marker_vcn = 0; -#if 1 - ntfs_debug ("dst:"); - ntfs_debug_dump_runlist (drl); - ntfs_debug ("src:"); - ntfs_debug_dump_runlist (srl); +#ifdef DEBUG + ntfs_debug("dst:"); + ntfs_debug_dump_runlist(drl); + ntfs_debug("src:"); + ntfs_debug_dump_runlist(srl); #endif /* Check for silly calling... */ - if (unlikely (!srl)) + if (unlikely(!srl)) return drl; - if (unlikely (IS_ERR (srl) || IS_ERR (drl))) - return ERR_PTR (-EINVAL); + if (unlikely(IS_ERR(srl) || IS_ERR(drl))) + return ERR_PTR(-EINVAL); /* Check for the case where the first mapping is being done now. */ - if (unlikely (!drl)) { - nrl = srl; - + if (unlikely(!drl)) { + drl = srl; /* Complete the source run list if necessary. */ - if (unlikely (srl[0].vcn)) { + if (unlikely(drl[0].vcn)) { /* Scan to the end of the source run list. */ - for (send = 0; likely (srl[send].length); send++) + for (dend = 0; likely(drl[dend].length); dend++) ; - nrl = ntfs_rl_realloc (srl, send, send + 1); - if (!nrl) - return ERR_PTR (-ENOMEM); - - rl_mm (nrl, 1, 0, send); - nrl[0].vcn = 0; /* Add start element. */ - nrl[0].lcn = LCN_RL_NOT_MAPPED; - nrl[0].length = nrl[1].vcn; + drl = ntfs_rl_realloc(drl, dend, dend + 1); + if (IS_ERR(drl)) + return drl; + /* Insert start element at the front of the run list. */ + ntfs_rl_mm(drl, 1, 0, dend); + drl[0].vcn = 0; + drl[0].lcn = LCN_RL_NOT_MAPPED; + drl[0].length = drl[1].vcn; } goto finished; } si = di = 0; - /* Skip the unmapped start element(s) in each run_list if present. */ + /* Skip any unmapped start element(s) in the source run_list. */ while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) si++; - /* Can't have an entirely unmapped srl run_list. */ - BUG_ON (!srl[si].length); + /* Can't have an entirely unmapped source run list. */ + BUG_ON(!srl[si].length); /* Record the starting points. */ sstart = si; @@ -445,19 +543,16 @@ run_list_element *merge_run_lists(run_list_element *drl, run_list_element *srl) * appended to @drl. */ for (; drl[di].length; di++) { - if ((drl[di].vcn + drl[di].length) > srl[sstart].vcn) + if (drl[di].vcn + drl[di].length > srl[sstart].vcn) break; } dins = di; /* Sanity check for illegal overlaps. */ - if ((drl[di].vcn == srl[si].vcn) && - (drl[di].lcn >= 0) && - (srl[si].lcn >= 0)) { - ntfs_error (NULL, "Run lists overlap. Cannot merge! Returning " - "ERANGE."); - nrl = ERR_PTR (-ERANGE); - goto exit; + if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && + (srl[si].lcn >= 0)) { + ntfs_error(NULL, "Run lists overlap. Cannot merge!"); + return ERR_PTR(-ERANGE); } /* Scan to the end of both run lists in order to know their sizes. */ @@ -466,9 +561,8 @@ run_list_element *merge_run_lists(run_list_element *drl, run_list_element *srl) for (dend = di; drl[dend].length; dend++) ; - if (srl[send].lcn == LCN_ENOENT) { - marker = send; - } + if (srl[send].lcn == (LCN)LCN_ENOENT) + marker_vcn = srl[marker = send].vcn; /* Scan to the last element with lcn >= LCN_HOLE. */ for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) @@ -479,96 +573,142 @@ run_list_element *merge_run_lists(run_list_element *drl, run_list_element *srl) { BOOL start; BOOL finish; - int ds = dend + 1; /* Number of elements in drl & srl */ + int ds = dend + 1; /* Number of elements in drl & srl */ int ss = sfinal - sstart + 1; start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ - (srl[send-1].vcn + srl[send-1].length))); + (srl[send - 1].vcn + srl[send - 1].length))); /* Or we'll lose an end marker */ if (start && finish && (drl[dins].length == 0)) ss++; - if (marker && (drl[dins].vcn + drl[dins].length > srl[send-1].vcn)) + if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) finish = FALSE; - #if 0 ntfs_debug("dfinal = %i, dend = %i", dfinal, dend); ntfs_debug("sstart = %i, sfinal = %i, send = %i", sstart, sfinal, send); ntfs_debug("start = %i, finish = %i", start, finish); ntfs_debug("ds = %i, ss = %i, dins = %i", ds, ss, dins); #endif - if (start) + if (start) { if (finish) - nrl = ntfs_rl_replace (drl, ds, srl + sstart, ss, dins); + drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); else - nrl = ntfs_rl_insert (drl, ds, srl + sstart, ss, dins); - else + drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); + } else { if (finish) - nrl = ntfs_rl_append (drl, ds, srl + sstart, ss, dins); + drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); else - nrl = ntfs_rl_split (drl, ds, srl + sstart, ss, dins); - - if (marker && !IS_ERR(nrl)) { - for (ds = 0; nrl[ds].length; ds++) + drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); + } + if (IS_ERR(drl)) { + ntfs_error(NULL, "Merge failed."); + return drl; + } + ntfs_free(srl); + if (marker) { + ntfs_debug("Triggering marker code."); + for (ds = dend; drl[ds].length; ds++) ; - nrl = ntfs_rl_insert(nrl, ds + 1, srl + marker, 1, ds); + /* We only need to care if @srl ended after @drl. */ + if (drl[ds].vcn <= marker_vcn) { + int slots = 0; + + if (drl[ds].vcn == marker_vcn) { + ntfs_debug("Old marker = 0x%Lx, replacing with " + "LCN_ENOENT.\n", + (unsigned long long) + drl[ds].lcn); + drl[ds].lcn = (LCN)LCN_ENOENT; + goto finished; + } + /* + * We need to create an unmapped run list element in + * @drl or extend an existing one before adding the + * ENOENT terminator. + */ + if (drl[ds].lcn == (LCN)LCN_ENOENT) { + ds--; + slots = 1; + } + if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { + /* Add an unmapped run list element. */ + if (!slots) { + /* FIXME/TODO: We need to have the + * extra memory already! (AIA) */ + drl = ntfs_rl_realloc(drl, ds, ds + 2); + if (!drl) + goto critical_error; + slots = 2; + } + ds++; + /* Need to set vcn if it isn't set already. */ + if (slots != 1) + drl[ds].vcn = drl[ds - 1].vcn + + drl[ds - 1].length; + drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; + /* We now used up a slot. */ + slots--; + } + drl[ds].length = marker_vcn - drl[ds].vcn; + /* Finally add the ENOENT terminator. */ + ds++; + if (!slots) { + /* FIXME/TODO: We need to have the extra + * memory already! (AIA) */ + drl = ntfs_rl_realloc(drl, ds, ds + 1); + if (!drl) + goto critical_error; + } + drl[ds].vcn = marker_vcn; + drl[ds].lcn = (LCN)LCN_ENOENT; + drl[ds].length = (s64)0; + } } } - if (likely (!IS_ERR (nrl))) { - /* The merge was completed successfully. */ finished: - if (nrl != srl) - ntfs_free (srl); - /*ntfs_debug ("Done.");*/ - /*ntfs_debug ("Merged run list:");*/ - -#if 1 - ntfs_debug ("res:"); - ntfs_debug_dump_runlist (nrl); -#endif - } else { - ntfs_error (NULL, "Merge failed, returning error code %ld.", - -PTR_ERR (nrl)); - } -exit: - return nrl; + /* The merge was completed successfully. */ + ntfs_debug("Merged run list:"); + ntfs_debug_dump_runlist(drl); + return drl; + +critical_error: + /* Critical error! We cannot afford to fail here. */ + ntfs_error(NULL, "Critical error! Not enough memory."); + panic("NTFS: Cannot continue."); } /** * decompress_mapping_pairs - convert mapping pairs array to run list * @vol: ntfs volume on which the attribute resides * @attr: attribute record whose mapping pairs array to decompress - * @run_list: optional run list in which to insert @attr's run list - * - * Decompress the attribute @attr's mapping pairs array into a run_list and - * return the run list or -errno on error. If @run_list is not NULL then - * the mapping pairs array of @attr is decompressed and the run list inserted - * into the appropriate place in @run_list. If this is the case and the - * function returns success, the original pointer passed into @run_list is no - * longer valid. + * @old_rl: optional run list in which to insert @attr's run list * * It is up to the caller to serialize access to the run list @old_rl. * - * Check the return value for error with IS_ERR(ret_val). If this is FALSE, - * the function was successful, the return value is the new run list, and if - * an existing run list pointer was passed in, this is no longer valid. - * If IS_ERR(ret_val) returns true, there was an error, the return value is not - * a run_list pointer and the existing run list pointer if one was passed in - * has not been touched. In this case use PTR_ERR(ret_val) to obtain the error - * code. Following error codes are defined: - * -ENOMEM Not enough memory to allocate run list array. - * -EIO Corrupt run list. - * -EINVAL Invalid parameters were passed in. - * -ERANGE The two run lists overlap. + * Decompress the attribute @attr's mapping pairs array into a run list. On + * success, return the decompressed run list. + * + * If @old_rl is not NULL, decompressed run list is inserted into the + * appropriate place in @old_rl and the resultant, combined run list is + * returned. The original @old_rl is deallocated. + * + * On error, return -errno. @old_rl is left unmodified in that case. + * + * The following error codes are defined: + * -ENOMEM - Not enough memory to allocate run list array. + * -EIO - Corrupt run list. + * -EINVAL - Invalid parameters were passed in. + * -ERANGE - The two run lists overlap. * * FIXME: For now we take the conceptionally simplest approach of creating the * new run list disregarding the already existing one and then splicing the - * two into one if that is possible (we check for overlap and discard the new - * run list if overlap present and return error). + * two into one, if that is possible (we check for overlap and discard the new + * run list if overlap present before returning ERR_PTR(-ERANGE)). */ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, const ATTR_RECORD *attr, run_list_element *old_rl) @@ -576,12 +716,12 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, VCN vcn; /* Current vcn. */ LCN lcn; /* Current lcn. */ s64 deltaxcn; /* Change in [vl]cn. */ - run_list_element *rl = NULL; /* The output run_list. */ - run_list_element *rl2; /* Temporary run_list. */ + run_list_element *rl; /* The output run list. */ u8 *buf; /* Current position in mapping pairs array. */ u8 *attr_end; /* End of attribute. */ - int rlsize; /* Size of run_list buffer. */ - int rlpos; /* Current run_list position. */ + int rlsize; /* Size of run list buffer. */ + u16 rlpos; /* Current run list position in units of + run_list_elements. */ u8 b; /* Current byte offset in buf. */ #ifdef DEBUG @@ -602,14 +742,12 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, ntfs_error(vol->sb, "Corrupt attribute."); return ERR_PTR(-EIO); } - /* Current position in run_list array. */ + /* Current position in run list array. */ rlpos = 0; - /* Allocate first page. */ - rl = ntfs_malloc_nofs(PAGE_SIZE); + /* Allocate first page and set current run list size to one page. */ + rl = ntfs_malloc_nofs(rlsize = PAGE_SIZE); if (unlikely(!rl)) return ERR_PTR(-ENOMEM); - /* Current run_list buffer size in bytes. */ - rlsize = PAGE_SIZE; /* Insert unmapped starting element if necessary. */ if (vcn) { rl->vcn = (VCN)0; @@ -624,18 +762,20 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, * operates on whole pages only. */ if (((rlpos + 3) * sizeof(*old_rl)) > rlsize) { + run_list_element *rl2; + rl2 = ntfs_malloc_nofs(rlsize + (int)PAGE_SIZE); if (unlikely(!rl2)) { ntfs_free(rl); return ERR_PTR(-ENOMEM); } - memmove(rl2, rl, rlsize); + memcpy(rl2, rl, rlsize); ntfs_free(rl); rl = rl2; rlsize += PAGE_SIZE; } /* Enter the current vcn into the current run_list element. */ - (rl + rlpos)->vcn = vcn; + rl[rlpos].vcn = vcn; /* * Get the change in vcn, i.e. the run length in clusters. * Doing it this way ensures that we signextend negative values. @@ -664,10 +804,10 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, goto err_out; } /* - * Enter the current run length into the current run_list + * Enter the current run length into the current run list * element. */ - (rl + rlpos)->length = deltaxcn; + rl[rlpos].length = deltaxcn; /* Increment the current vcn by the current run length. */ vcn += deltaxcn; /* @@ -676,7 +816,7 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, * to LCN_HOLE. */ if (!(*buf & 0xf0)) - (rl + rlpos)->lcn = (LCN)LCN_HOLE; + rl[rlpos].lcn = (LCN)LCN_HOLE; else { /* Get the lcn change which really can be negative. */ u8 b2 = *buf & 0xf; @@ -709,7 +849,7 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, goto err_out; } /* Enter the current lcn into the run_list element. */ - (rl + rlpos)->lcn = lcn; + rl[rlpos].lcn = lcn; } /* Get to the next run_list element. */ rlpos++; @@ -729,7 +869,7 @@ mpa_err: "non-resident attribute."); goto err_out; } - /* Setup not mapped run_list element if this is the base extent. */ + /* Setup not mapped run list element if this is the base extent. */ if (!attr->_ANR(lowest_vcn)) { VCN max_cluster; @@ -742,13 +882,13 @@ mpa_err: * likely, there are more extents following this one. */ if (deltaxcn < --max_cluster) { - //RAR ntfs_debug("More extents to follow; deltaxcn = 0x%Lx, " - //RAR "max_cluster = 0x%Lx", - //RAR (long long)deltaxcn, - //RAR (long long)max_cluster); - (rl + rlpos)->vcn = vcn; - vcn += (rl + rlpos)->length = max_cluster - deltaxcn; - (rl + rlpos)->lcn = (LCN)LCN_RL_NOT_MAPPED; + ntfs_debug("More extents to follow; deltaxcn = 0x%Lx, " + "max_cluster = 0x%Lx", + (long long)deltaxcn, + (long long)max_cluster); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = max_cluster - deltaxcn; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; rlpos++; } else if (unlikely(deltaxcn > max_cluster)) { ntfs_error(vol->sb, "Corrupt attribute. deltaxcn = " @@ -757,25 +897,26 @@ mpa_err: (long long)max_cluster); goto mpa_err; } - (rl + rlpos)->lcn = (LCN)LCN_ENOENT; + rl[rlpos].lcn = (LCN)LCN_ENOENT; } else /* Not the base extent. There may be more extents to follow. */ - (rl + rlpos)->lcn = (LCN)LCN_RL_NOT_MAPPED; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; /* Setup terminating run_list element. */ - (rl + rlpos)->vcn = vcn; - (rl + rlpos)->length = (s64)0; - //RAR ntfs_debug("Mapping pairs array successfully decompressed."); - //RAR ntfs_debug_dump_runlist(rl); + rl[rlpos].vcn = vcn; + rl[rlpos].length = (s64)0; /* If no existing run list was specified, we are done. */ - if (!old_rl) + if (!old_rl) { + ntfs_debug("Mapping pairs array successfully decompressed:"); + ntfs_debug_dump_runlist(rl); return rl; + } /* Now combine the new and old run lists checking for overlaps. */ - rl2 = merge_run_lists(old_rl, rl); - if (likely(!IS_ERR(rl2))) - return rl2; + old_rl = ntfs_merge_run_lists(old_rl, rl); + if (likely(!IS_ERR(old_rl))) + return old_rl; ntfs_free(rl); ntfs_error(vol->sb, "Failed to merge run lists."); - return rl2; + return old_rl; io_error: ntfs_error(vol->sb, "Corrupt attribute."); err_out: diff --git a/fs/ntfs/mst.c b/fs/ntfs/mst.c index 9b502299788a..5a1477db2712 100644 --- a/fs/ntfs/mst.c +++ b/fs/ntfs/mst.c @@ -48,7 +48,7 @@ int post_read_mst_fixup(NTFS_RECORD *b, const u32 size) usa_ofs = le16_to_cpu(b->usa_ofs); /* Decrement usa_count to get number of fixups. */ usa_count = le16_to_cpu(b->usa_count) - 1; - /* Size and alignement checks. */ + /* Size and alignment checks. */ if ( size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || usa_ofs + (usa_count * 2) > size || @@ -132,7 +132,7 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size) usa_ofs = le16_to_cpu(b->usa_ofs); /* Decrement usa_count to get number of fixups. */ usa_count = le16_to_cpu(b->usa_count) - 1; - /* Size and alignement checks. */ + /* Size and alignment checks. */ if ( size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || usa_ofs + (usa_count * 2) > size || diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 94cc54668d87..68f7589e6987 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1077,7 +1077,7 @@ iput_mirr_err_out: * releases all inodes and memory belonging to the NTFS specific part of the * super block. */ -void ntfs_put_super(struct super_block *vfs_sb) +static void ntfs_put_super(struct super_block *vfs_sb) { ntfs_volume *vol = NTFS_SB(vfs_sb); @@ -1155,7 +1155,7 @@ void ntfs_put_super(struct super_block *vfs_sb) * Errors are ignored and we just return the number of free clusters we have * found. This means we return an underestimate on error. */ -s64 get_nr_free_clusters(ntfs_volume *vol) +static s64 get_nr_free_clusters(ntfs_volume *vol) { struct address_space *mapping = vol->lcnbmp_ino->i_mapping; filler_t *readpage = (filler_t*)mapping->a_ops->readpage; @@ -1227,7 +1227,7 @@ handle_partial_page: } /** - * get_nr_free_mft_records - return the number of free inodes on a volume + * __get_nr_free_mft_records - return the number of free inodes on a volume * @vol: ntfs volume for which to obtain free inode count * * Calculate the number of free mft records (inodes) on the mounted NTFS @@ -1235,8 +1235,10 @@ handle_partial_page: * * Errors are ignored and we just return the number of free inodes we have * found. This means we return an underestimate on error. + * + * NOTE: Caller must hold mftbmp_lock rw_semaphore for reading or writing. */ -unsigned long get_nr_free_mft_records(ntfs_volume *vol) +static unsigned long __get_nr_free_mft_records(ntfs_volume *vol) { struct address_space *mapping; filler_t *readpage; @@ -1247,7 +1249,6 @@ unsigned long get_nr_free_mft_records(ntfs_volume *vol) ntfs_debug("Entering."); /* Serialize accesses to the inode bitmap. */ - down_read(&vol->mftbmp_lock); mapping = &vol->mftbmp_mapping; readpage = (filler_t*)mapping->a_ops->readpage; /* @@ -1307,7 +1308,6 @@ handle_partial_page: } ntfs_debug("Finished reading $MFT/$BITMAP, last index = 0x%lx", index - 1); - up_read(&vol->mftbmp_lock); ntfs_debug("Exiting."); return nr_free; } @@ -1330,7 +1330,7 @@ handle_partial_page: * * Return 0 on success or -errno on error. */ -int ntfs_statfs(struct super_block *sb, struct statfs *sfs) +static int ntfs_statfs(struct super_block *sb, struct statfs *sfs) { ntfs_volume *vol = NTFS_SB(sb); s64 size; @@ -1354,10 +1354,12 @@ int ntfs_statfs(struct super_block *sb, struct statfs *sfs) size = 0LL; /* Free blocks avail to non-superuser, same as above on NTFS. */ sfs->f_bavail = sfs->f_bfree = size; + down_read(&vol->mftbmp_lock); /* Total file nodes in file system (at this moment in time). */ sfs->f_files = vol->mft_ino->i_size >> vol->mft_record_size_bits; /* Free file nodes in fs (based on current total count). */ - sfs->f_ffree = get_nr_free_mft_records(vol); + sfs->f_ffree = __get_nr_free_mft_records(vol); + up_read(&vol->mftbmp_lock); /* * File system id. This is extremely *nix flavour dependent and even * within Linux itself all fs do their own thing. I interpret this to diff --git a/fs/open.c b/fs/open.c index eec397a0efc2..3742bc8a28d4 100644 --- a/fs/open.c +++ b/fs/open.c @@ -30,9 +30,7 @@ int vfs_statfs(struct super_block *sb, struct statfs *buf) retval = -ENOSYS; if (sb->s_op && sb->s_op->statfs) { memset(buf, 0, sizeof(struct statfs)); - lock_kernel(); retval = sb->s_op->statfs(sb, buf); - unlock_kernel(); } } return retval; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 407948c4ad5b..f69f312c8afb 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -281,6 +281,8 @@ unsigned long qnx4_block_map( struct inode *inode, long iblock ) static int qnx4_statfs(struct super_block *sb, struct statfs *buf) { + lock_kernel(); + buf->f_type = sb->s_magic; buf->f_bsize = sb->s_blocksize; buf->f_blocks = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8; @@ -288,6 +290,8 @@ static int qnx4_statfs(struct super_block *sb, struct statfs *buf) buf->f_bavail = buf->f_bfree; buf->f_namelen = QNX4_NAME_MAX; + unlock_kernel(); + return 0; } diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 7d41fc6389f2..1ed057e963f2 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -102,6 +102,9 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev) case S_IFDIR: inode->i_op = &ramfs_dir_inode_operations; inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; break; case S_IFLNK: inode->i_op = &page_symlink_inode_operations; @@ -131,10 +134,8 @@ static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int d static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) { int retval = ramfs_mknod(dir, dentry, mode | S_IFDIR, 0); - if (!retval) { + if (!retval) dir->i_nlink++; - dentry->d_inode->i_nlink++; - } return retval; } diff --git a/fs/reiserfs/buffer2.c b/fs/reiserfs/buffer2.c index a078187b1922..a08c2a01d256 100644 --- a/fs/reiserfs/buffer2.c +++ b/fs/reiserfs/buffer2.c @@ -25,6 +25,13 @@ void wait_buffer_until_released (const struct buffer_head * bh) { int repeat_counter = 0; + /* + * FIXME! Temporary cludge until ReiserFS people tell what they + * actually are trying to protect against! + */ + if (1) + return; + while (atomic_read (&(bh->b_count)) > 1) { if ( !(++repeat_counter % 30000000) ) { diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 0d439b0b1508..2acd8dfe1c2a 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -602,7 +602,13 @@ out_no_server: static int smb_statfs(struct super_block *sb, struct statfs *buf) { - int result = smb_proc_dskattr(sb, buf); + int result; + + lock_kernel(); + + result = smb_proc_dskattr(sb, buf); + + unlock_kernel(); buf->f_type = SMB_SUPER_MAGIC; buf->f_namelen = SMB_MAXPATHLEN; diff --git a/fs/udf/super.c b/fs/udf/super.c index 1b1680ecfa03..783890fffe9b 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1726,6 +1726,8 @@ udf_put_super(struct super_block *sb) static int udf_statfs(struct super_block *sb, struct statfs *buf) { + lock_kernel(); + buf->f_type = UDF_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); @@ -1738,6 +1740,8 @@ udf_statfs(struct super_block *sb, struct statfs *buf) /* __kernel_fsid_t f_fsid */ buf->f_namelen = UDF_NAME_LEN; + unlock_kernel(); + return 0; } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 7a19f2c4fbf3..a046374ab085 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -959,6 +959,8 @@ int ufs_statfs (struct super_block * sb, struct statfs * buf) struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; + lock_kernel(); + uspi = sb->u.ufs_sb.s_uspi; usb1 = ubh_get_usb_first (USPI_UBH); @@ -972,6 +974,9 @@ int ufs_statfs (struct super_block * sb, struct statfs * buf) buf->f_files = uspi->s_ncg * uspi->s_ipg; buf->f_ffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree); buf->f_namelen = UFS_MAXNAMLEN; + + unlock_kernel(); + return 0; } diff --git a/include/linux/input.h b/include/linux/input.h index aa5e80d60330..76130b47a59a 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -2,7 +2,7 @@ #define _INPUT_H /* - * $Id: input.h,v 1.57 2002/01/02 11:59:56 vojtech Exp $ + * $Id: input.h,v 1.68 2002/05/31 10:35:49 fsirl Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik */ @@ -322,12 +322,13 @@ struct input_event { #define KEY_FINANCE 219 #define KEY_SPORT 220 #define KEY_SHOP 221 - -#define KEY_UNKNOWN 240 - +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 #define KEY_BRIGHTNESSDOWN 224 #define KEY_BRIGHTNESSUP 225 +#define KEY_UNKNOWN 240 + #define BTN_MISC 0x100 #define BTN_0 0x100 #define BTN_1 0x101 @@ -394,6 +395,10 @@ struct input_event { #define BTN_STYLUS 0x14b #define BTN_STYLUS2 0x14c +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + #define KEY_MAX 0x1ff /* @@ -514,23 +519,19 @@ struct input_event { * Structures used in ioctls to upload effects to a device * The first structures are not passed directly by using ioctls. * They are sub-structures of the actually sent structure (called ff_effect) - * - * Ranges: - * 0 <= __u16 <= 65535 - * -32767 <= __s16 <= +32767 ! Not -32768 for lower bound ! */ struct ff_replay { - __u16 length; /* Duration of an effect in ms. All other times are also expressed in ms */ - __u16 delay; /* Time to wait before to start playing an effect */ + __u16 length; /* Duration of an effect in ms. All other times are also expressed in ms */ + __u16 delay; /* Time to wait before to start playing an effect */ }; struct ff_trigger { - __u16 button; /* Number of button triggering an effect */ - __u16 interval; /* Time to wait before an effect can be re-triggered (ms) */ + __u16 button; /* Number of button triggering an effect */ + __u16 interval; /* Time to wait before an effect can be re-triggered (ms) */ }; -struct ff_shape { +struct ff_envelope { __u16 attack_length; /* Duration of attack (ms) */ __u16 attack_level; /* Level at beginning of attack */ __u16 fade_length; /* Duration of fade (ms) */ @@ -539,41 +540,56 @@ struct ff_shape { /* FF_CONSTANT */ struct ff_constant_effect { - __s16 level; /* Strength of effect. Negative values are OK */ - struct ff_shape shape; + __s16 level; /* Strength of effect. Negative values are OK */ + struct ff_envelope envelope; }; -/* FF_SPRING of FF_FRICTION */ -struct ff_interactive_effect { -/* Axis along which effect must be created. If null, the field named direction - * is used - * It is a bit array (ie to enable axes X and Y, use BIT(ABS_X) | BIT(ABS_Y) - * It overrides the value of ff_effect::direction, which is used only if - * axis == 0 - */ - __u16 axis; +/* FF_RAMP */ +struct ff_ramp_effect { + __s16 start_level; + __s16 end_level; + struct ff_envelope envelope; +}; +/* FF_SPRING of FF_FRICTION */ +struct ff_condition_effect { __u16 right_saturation; /* Max level when joystick is on the right */ - __u16 left_saturation; /* Max level when joystick in on the left */ + __u16 left_saturation; /* Max level when joystick in on the left */ __s16 right_coeff; /* Indicates how fast the force grows when the joystick moves to the right */ __s16 left_coeff; /* Same for left side */ - __u16 deadband; /* Size of area where no force is produced */ - __s16 center; /* Position of dead dead zone */ + __u16 deadband; /* Size of area where no force is produced */ + __s16 center; /* Position of dead zone */ }; /* FF_PERIODIC */ struct ff_periodic_effect { - __u16 waveform; /* Kind of wave (sine, square...) */ - __u16 period; /* in ms */ + __u16 waveform; /* Kind of wave (sine, square...) */ + __u16 period; /* in ms */ __s16 magnitude; /* Peak value */ - __s16 offset; /* Mean value of wave (roughly) */ + __s16 offset; /* Mean value of wave (roughly) */ __u16 phase; /* 'Horizontal' shift */ - struct ff_shape shape; + struct ff_envelope envelope; + +/* Only used if waveform == FF_CUSTOM */ + __u32 custom_len; /* Number of samples */ + __s16 *custom_data; /* Buffer of samples */ +/* Note: the data pointed by custom_data is copied by the driver. You can + * therefore dispose of the memory after the upload/update */ +}; + +/* FF_RUMBLE */ +/* Some rumble pads have two motors of different weight. + strong_magnitude represents the magnitude of the vibration generated + by the heavy motor. +*/ +struct ff_rumble_effect { + __u16 strong_magnitude; /* Magnitude of the heavy motor */ + __u16 weak_magnitude; /* Magnitude of the light one */ }; /* @@ -598,27 +614,14 @@ struct ff_effect { union { struct ff_constant_effect constant; + struct ff_ramp_effect ramp; struct ff_periodic_effect periodic; - struct ff_interactive_effect interactive; + struct ff_condition_effect condition[2]; /* One for each axis */ + struct ff_rumble_effect rumble; } u; }; /* - * Buttons that can trigger effects. Use for example FF_BTN(BTN_TRIGGER) to - * access the bitmap. - */ - -#define FF_BTN(x) ((x) - BTN_MISC + FF_BTN_OFFSET) -#define FF_BTN_OFFSET 0x00 - -/* - * Force feedback axis mappings. Use FF_ABS() to access the bitmap. - */ - -#define FF_ABS(x) ((x) + FF_ABS_OFFSET) -#define FF_ABS_OFFSET 0x40 - -/* * Force feedback effect types */ @@ -627,6 +630,9 @@ struct ff_effect { #define FF_CONSTANT 0x52 #define FF_SPRING 0x53 #define FF_FRICTION 0x54 +#define FF_DAMPER 0x55 +#define FF_INERTIA 0x56 +#define FF_RAMP 0x57 /* * Force feedback periodic effect types @@ -668,8 +674,6 @@ struct input_dev { char *name; char *phys; char *uniq; - int number; - unsigned short idbus; unsigned short idvendor; unsigned short idproduct; @@ -707,8 +711,6 @@ struct input_dev { int absfuzz[ABS_MAX + 1]; int absflat[ABS_MAX + 1]; - int only_one_writer; - int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); int (*accept)(struct input_dev *dev, struct file *file); diff --git a/include/linux/usb.h b/include/linux/usb.h index d31eb7e942b3..b1c39c99e75f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -104,6 +104,7 @@ #include <linux/interrupt.h> /* for in_interrupt() */ #include <linux/list.h> /* for struct list_head */ #include <linux/device.h> /* for struct device */ +#include <linux/fs.h> /* for struct file_operations */ static __inline__ void wait_ms(unsigned int ms) @@ -648,14 +649,6 @@ struct usb_device_id { * because its device has been (or is being) disconnected. The * handle passed is what was returned by probe(), or was provided * to usb_driver_claim_interface(). - * @fops: USB drivers can reuse some character device framework in - * the USB subsystem by providing a file operations vector and - * a minor number. - * @minor: Used with fops to simplify creating USB character devices. - * Such drivers have sixteen character devices, using the USB - * major number and starting with this minor number. - * @num_minors: Used with minor to specify how many minors are used by - * this driver. * @ioctl: Used for drivers that want to talk to userspace through * the "usbfs" filesystem. This lets devices provide ways to * expose information to user space regardless of where they @@ -694,11 +687,6 @@ struct usb_driver { ); struct list_head driver_list; - - struct file_operations *fops; - int minor; - int num_minors; - struct semaphore serialize; /* ioctl -- userspace apps can talk to drivers through usbfs */ @@ -722,13 +710,8 @@ extern struct bus_type usb_bus_type; extern int usb_register(struct usb_driver *); extern void usb_deregister(struct usb_driver *); -#ifndef CONFIG_USB_DYNAMIC_MINORS -static inline int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor) { return -ENODEV; } -static inline void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor) {} -#else -extern int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor); -extern void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor); -#endif +extern int usb_register_dev(struct file_operations *fops, int minor, int num_minors, int *start_minor); +extern void usb_deregister_dev(int num_minors, int start_minor); /* -------------------------------------------------------------------------- */ diff --git a/mm/mmap.c b/mm/mmap.c index 7c1badcd5155..384d1ff87f87 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1121,7 +1121,6 @@ void exit_mmap(struct mm_struct * mm) unsigned long end = mpnt->vm_end; mm->map_count--; - remove_shared_vm_struct(mpnt); unmap_page_range(tlb, mpnt, start, end); mpnt = mpnt->vm_next; } @@ -1148,6 +1147,7 @@ void exit_mmap(struct mm_struct * mm) */ while (mpnt) { struct vm_area_struct * next = mpnt->vm_next; + remove_shared_vm_struct(mpnt); if (mpnt->vm_ops) { if (mpnt->vm_ops->close) mpnt->vm_ops->close(mpnt); diff --git a/net/khttpd/Makefile b/net/khttpd/Makefile index 0de4262de3e5..5eebd65c3b01 100644 --- a/net/khttpd/Makefile +++ b/net/khttpd/Makefile @@ -19,4 +19,4 @@ $(obj)/rfc_time.o: $(obj)/times.h # Generated files $(obj)/times.h: $(obj)/make_times_h - $< >$@ + $(obj)/make_times_h >$@ |
