summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJames Simmons <jsimmons@heisenberg.transvirtual.com>2002-07-07 22:56:15 -0700
committerJames Simmons <jsimmons@heisenberg.transvirtual.com>2002-07-07 22:56:15 -0700
commit8ef1bf6df837a5f92e2d8d50ca26c77998602ff4 (patch)
tree4b08ab0d040f739937b98081fae5cc7aa1b39c64 /drivers
parentaf5c826ce279f0cf9f87ce7543e94b0d3b83a644 (diff)
parenta321a55fcbb2c21eb7bc8b7d4b294eefaea9065c (diff)
Merge http://fbdev.bkbits.net/fbdev-2.5
into heisenberg.transvirtual.com:/tmp/fbdev-2.5
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acorn/char/Makefile13
-rw-r--r--drivers/acorn/char/defkeymap-acorn.c_shipped (renamed from drivers/acorn/char/defkeymap-acorn.c)0
-rw-r--r--drivers/char/Makefile20
-rw-r--r--drivers/char/defkeymap.c_shipped262
-rw-r--r--drivers/char/qtronixmap.c_shipped265
-rw-r--r--drivers/ide/probe.c12
-rw-r--r--drivers/input/Config.help26
-rw-r--r--drivers/input/Config.in14
-rw-r--r--drivers/input/Makefile6
-rw-r--r--drivers/input/evbug.c99
-rw-r--r--drivers/input/evdev.c18
-rw-r--r--drivers/input/gameport/Config.help12
-rw-r--r--drivers/input/gameport/Config.in2
-rw-r--r--drivers/input/gameport/Makefile3
-rw-r--r--drivers/input/gameport/fm801-gp.c162
-rw-r--r--drivers/input/gameport/gameport.c28
-rw-r--r--drivers/input/gameport/pcigame.c199
-rw-r--r--drivers/input/gameport/vortex.c185
-rw-r--r--drivers/input/input.c14
-rw-r--r--drivers/input/joydev.c10
-rw-r--r--drivers/input/joystick/Config.help29
-rw-r--r--drivers/input/joystick/Config.in4
-rw-r--r--drivers/input/joystick/Makefile6
-rw-r--r--drivers/input/joystick/adi.c4
-rw-r--r--drivers/input/joystick/db9.c12
-rw-r--r--drivers/input/joystick/gamecon.c6
-rw-r--r--drivers/input/joystick/guillemot.c285
-rw-r--r--drivers/input/joystick/iforce.c1224
-rw-r--r--drivers/input/joystick/iforce/Makefile42
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c543
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c543
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c314
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c166
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c214
-rw-r--r--drivers/input/joystick/iforce/iforce.h194
-rw-r--r--drivers/input/joystick/joydump.c152
-rw-r--r--drivers/input/joystick/magellan.c2
-rw-r--r--drivers/input/joystick/twidjoy.c264
-rw-r--r--drivers/input/keybdev.c4
-rw-r--r--drivers/input/keyboard/Config.help66
-rw-r--r--drivers/input/keyboard/Config.in18
-rw-r--r--drivers/input/keyboard/Makefile16
-rw-r--r--drivers/input/keyboard/amikbd.c144
-rw-r--r--drivers/input/keyboard/atkbd.c565
-rw-r--r--drivers/input/keyboard/maple_keyb.c190
-rw-r--r--drivers/input/keyboard/ps2serkbd.c298
-rw-r--r--drivers/input/keyboard/sunkbd.c318
-rw-r--r--drivers/input/keyboard/xtkbd.c157
-rw-r--r--drivers/input/mouse/Config.help87
-rw-r--r--drivers/input/mouse/Config.in24
-rw-r--r--drivers/input/mouse/Makefile18
-rw-r--r--drivers/input/mouse/amimouse.c147
-rw-r--r--drivers/input/mouse/inport.c193
-rw-r--r--drivers/input/mouse/logibm.c182
-rw-r--r--drivers/input/mouse/maplemouse.c137
-rw-r--r--drivers/input/mouse/pc110pad.c163
-rw-r--r--drivers/input/mouse/psmouse.c652
-rw-r--r--drivers/input/mouse/rpcmouse.c111
-rw-r--r--drivers/input/mouse/sermouse.c299
-rw-r--r--drivers/input/mousedev.c27
-rw-r--r--drivers/input/power.c180
-rw-r--r--drivers/input/serio/Config.help47
-rw-r--r--drivers/input/serio/Config.in12
-rw-r--r--drivers/input/serio/Makefile4
-rw-r--r--drivers/input/serio/ct82c710.c212
-rw-r--r--drivers/input/serio/i8042.c717
-rw-r--r--drivers/input/serio/i8042.h128
-rw-r--r--drivers/input/serio/parkbd.c203
-rw-r--r--drivers/input/serio/rpckbd.c117
-rw-r--r--drivers/input/touchscreen/Config.help16
-rw-r--r--drivers/input/touchscreen/Config.in7
-rw-r--r--drivers/input/touchscreen/Makefile11
-rw-r--r--drivers/input/touchscreen/gunze.c178
-rw-r--r--drivers/input/tsdev.c443
-rw-r--r--drivers/isdn/capi/capifs.c17
-rw-r--r--drivers/message/fusion/Makefile6
-rw-r--r--drivers/net/hamradio/soundmodem/Makefile2
-rw-r--r--drivers/scsi/ips.c4
-rw-r--r--drivers/tc/Makefile13
-rw-r--r--drivers/tc/lk201-map.c_shipped265
-rw-r--r--drivers/usb/class/bluetty.c5
-rw-r--r--drivers/usb/class/printer.c23
-rw-r--r--drivers/usb/core/Makefile6
-rw-r--r--drivers/usb/core/file.c182
-rw-r--r--drivers/usb/core/inode.c19
-rw-r--r--drivers/usb/core/usb.c233
-rw-r--r--drivers/usb/host/Config.help43
-rw-r--r--drivers/usb/host/Config.in20
-rw-r--r--drivers/usb/host/ohci-dbg.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c12
-rw-r--r--drivers/usb/host/uhci-hcd.c38
-rw-r--r--drivers/usb/host/usb-uhci-hcd.c18
-rw-r--r--drivers/usb/host/usb-uhci-q.c58
-rw-r--r--drivers/usb/image/mdc800.c9
-rw-r--r--drivers/usb/image/scanner.c19
-rw-r--r--drivers/usb/input/aiptek.c5
-rw-r--r--drivers/usb/input/hiddev.c22
-rw-r--r--drivers/usb/media/dabusb.c16
-rw-r--r--drivers/usb/misc/auerswald.c25
-rw-r--r--drivers/usb/misc/brlvger.c22
-rw-r--r--drivers/usb/misc/rio500.c18
-rw-r--r--drivers/usb/net/pegasus.c279
-rw-r--r--drivers/usb/net/pegasus.h17
-rw-r--r--drivers/usb/net/rtl8150.c49
-rw-r--r--drivers/usb/storage/transport.c101
-rw-r--r--drivers/usb/storage/usb.c253
-rw-r--r--drivers/usb/storage/usb.h1
-rw-r--r--drivers/usb/usb-skeleton.c29
108 files changed, 10868 insertions, 2408 deletions
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, &param, 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, &param, ATKBD_CMD_EX_ENABLE))
+ return 4;
+
+/*
+ * Try to set the set we want.
+ */
+
+ param = atkbd_set;
+ if (atkbd_command(atkbd, &param, 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, &param, 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, &param, 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(&param, 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(&param, 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(&param, I8042_CMD_AUX_LOOP) || param != 0xa5)
+ return -1;
+
+/*
+ * External connection test - filters out AT-soldered PS/2 i8042's
+ */
+
+ if (i8042_command(&param, I8042_CMD_AUX_TEST) || param)
+ return -1;
+
+/*
+ * Bit assignment test - filters out PS/2 i8042's in AT mode
+ */
+
+ if (i8042_command(&param, I8042_CMD_AUX_DISABLE))
+ return -1;
+
+ if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS))
+ return -1;
+
+ if (i8042_command(&param, I8042_CMD_AUX_TEST) || param) {
+
+/*
+ * We've got an old AMI i8042 with 'Bad Cache' commands.
+ */
+
+ i8042_command(&param, I8042_CMD_AUX_ENABLE);
+ return -1;
+ }
+
+ if (i8042_command(&param, I8042_CMD_AUX_ENABLE))
+ return -1;
+
+ if (i8042_command(&param, 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 (&regs->intrstatus)
- & readl (&regs->intrenable))) == 0) {
+
+ /* cardbus/... hardware gone before remove() */
+ } else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
+ ohci->disabled++;
+ err ("%s device removed!", hcd->self.bus_name);
+ return;
+
+ /* interrupt for some other device? */
+ } else if ((ints &= readl (&regs->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) {