2005-10-29 00:19:05

by Alejandro Bonilla

[permalink] [raw]
Subject: Kernel Badness 2.6.14-Git

Hi,

I just pulled from Linus Tree and I'm getting this badness in dmesg.

Please let me know if it is too soon to start reporting this. 2.6.14 is
OK and does not output this.


ACPI: PCI Interrupt 0000:01:00.0[A] -> Link [LNKA] -> GSI 11 (level,
low) -> IRQ 11
[drm] Initialized radeon 1.17.0 20050311 on minor 0:
ACPI: CPU0 (power states: C1[C1] C2[C2] C3[C3])
ACPI: Processor [CPU] (supports 8 throttling states)
hdaps: IBM ThinkPad T42 detected.
hdaps: initial latch check good (0x01).
hdaps: device successfully initialized.
Badness in kref_get at lib/kref.c:32
[<c026c3f1>] kref_get+0x26/0x2d
[<c026bc45>] kobject_get+0x12/0x17
[<c02c9e02>] class_device_get+0x13/0x1a
[<c02c99bd>] class_device_add+0x5f/0x20a
[<c02c98b3>] class_device_initialize+0x15/0x1d
[<c02c9bf1>] class_device_create+0x73/0x95
[<e1289c0c>] evdev_connect+0xc0/0xdd [evdev]
[<c0324636>] input_register_device+0xe2/0x129
[<e080b0c9>] hdaps_init+0xc9/0x146 [hdaps]
[<e132e5ac>] hdaps_dmi_match_invert+0x0/0x21 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e5ac>] hdaps_dmi_match_invert+0x0/0x21 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e5ac>] hdaps_dmi_match_invert+0x0/0x21 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<c012e593>] sys_init_module+0xaa/0x1ac
[<c0102bc5>] syscall_call+0x7/0xb
Unable to handle kernel NULL pointer dereference at virtual address 00000008
printing eip:
c0180ba1
*pde = 00000000
Oops: 0000 [#1]
PREEMPT
Modules linked in: hdaps speedstep_centrino processor radeon pcmcia
usbhid ipw2200 ieee80211 ieee80211_crypt yenta_socket rsrc_nonstatic
e1000 pcmcia_core firmware_class i2c_i801 i810_audio ac97_codec
irtty_sir soundcore i2c_core sir_dev intel_agp shpchp pci_hotplug irda
ehci_hcd uhci_hcd rtc crc_ccitt pcspkr evdev mousedev unix
CPU: 0
EIP: 0060:[<c0180ba1>] Not tainted VLI
EFLAGS: 00010286 (2.6.14)
EIP is at create_dir+0xd/0x172
eax: 00000000 ebx: dde13d88 ecx: dedfd7f0 edx: 00000000
esi: dde13d88 edi: e13307d4 ebp: de735c9c esp: de735c74
ds: 007b es: 007b ss: 0068
Process modprobe (pid: 4012, threadinfo=de734000 task=dee85580)
Stack: dde13d88 dde13d88 dde13d88 e13307d4 dde13d88 c0180d7b dde13d88
00000000
dde13d8c de735c9c 00000000 00000000 c026b8f9 dde13d88 dde13d88
de734000
c026bad8 dde13d88 dde13d80 dde13df0 dde13df7 c02c99ef dde13d88
c042b800
Call Trace:
[<c0180d7b>] sysfs_create_dir+0x3e/0x59
[<c026b8f9>] create_dir+0x13/0x33
[<c026bad8>] kobject_add+0x83/0xa2
[<c02c99ef>] class_device_add+0x91/0x20a
[<c02c9bf1>] class_device_create+0x73/0x95
[<e1289c0c>] evdev_connect+0xc0/0xdd [evdev]
[<c0324636>] input_register_device+0xe2/0x129
[<e080b0c9>] hdaps_init+0xc9/0x146 [hdaps]
[<e132e5ac>] hdaps_dmi_match_invert+0x0/0x21 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e5ac>] hdaps_dmi_match_invert+0x0/0x21 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e5ac>] hdaps_dmi_match_invert+0x0/0x21 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<e132e593>] hdaps_dmi_match+0x0/0x19 [hdaps]
[<c012e593>] sys_init_module+0xaa/0x1ac
[<c0102bc5>] syscall_call+0x7/0xb
Code: d5 82 f9 ff e8 32 2e f8 ff 83 c4 10 ff 03 31 c0 89 5f 50 c7 47 48
ec 3b 41 c0 5b 5e 5f c3 55 57 56 53 56 8b 44 24 1c 8b 6c 24 24 <8b> 50
08 ff 4a 70 0f 88 b3 0a 00 00 31 c0 83 c9 ff 8b 7c 24 20
<6>Non-volatile memory driver v1.3
Unable to handle kernel NULL pointer dereference at virtual address 00000008
printing eip:
c0180ba1
*pde = 00000000
Oops: 0000 [#2]
PREEMPT
Modules linked in: joydev nvram hdaps speedstep_centrino processor
radeon pcmcia usbhid ipw2200 ieee80211 ieee80211_crypt yenta_socket
rsrc_nonstatic e1000 pcmcia_core firmware_class i2c_i801 i810_audio
ac97_codec irtty_sir soundcore i2c_core sir_dev intel_agp shpchp
pci_hotplug irda ehci_hcd uhci_hcd rtc crc_ccitt pcspkr evdev mousedev unix
CPU: 0
EIP: 0060:[<c0180ba1>] Not tainted VLI
EFLAGS: 00010286 (2.6.14)
EIP is at create_dir+0xd/0x172
eax: 00000000 ebx: dde13b88 ecx: dde13ca4 edx: 00000000
esi: dde13b88 edi: e13307d4 ebp: de735ed8 esp: de735eb0
ds: 007b es: 007b ss: 0068
Process modprobe (pid: 4045, threadinfo=de734000 task=dee85580)
Stack: dde13b88 dde13b88 dde13b88 e13307d4 dde13b88 c0180d7b dde13b88
00000000
dde13b8c de735ed8 00000000 00000000 c026b8f9 dde13b88 dde13b88
de734000
c026bad8 dde13b88 dde13b80 dde13bf0 dde13bf4 c02c99ef dde13b88
c042b800
Call Trace:
[<c0180d7b>] sysfs_create_dir+0x3e/0x59
[<c026b8f9>] create_dir+0x13/0x33
[<c026bad8>] kobject_add+0x83/0xa2
[<c02c99ef>] class_device_add+0x91/0x20a
[<c02c9bf1>] class_device_create+0x73/0x95
[<e13f4ca1>] joydev_connect+0x27a/0x297 [joydev]
[<c03247d1>] input_register_handler+0x75/0xc8
[<e134600a>] joydev_init+0xa/0xe [joydev]
[<c012e593>] sys_init_module+0xaa/0x1ac
[<c0102bc5>] syscall_call+0x7/0xb
Code: d5 82 f9 ff e8 32 2e f8 ff 83 c4 10 ff 03 31 c0 89 5f 50 c7 47 48
ec 3b 41 c0 5b 5e 5f c3 55 57 56 53 56 8b 44 24 1c 8b 6c 24 24 <8b> 50
08 ff 4a 70 0f 88 b3 0a 00 00 31 c0 83 c9 ff 8b 7c 24 20
<6>ibm_acpi: IBM ThinkPad ACPI Extras v0.11
ibm_acpi: http://ibm-acpi.sf.net/
ibm_acpi: dock device not present
kjournald starting. Commit interval 5 seconds


2005-10-29 03:17:35

by Greg KH

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Fri, Oct 28, 2005 at 06:18:57PM -0600, Alejandro Bonilla Beeche wrote:
> Hi,
>
> I just pulled from Linus Tree and I'm getting this badness in dmesg.
>
> Please let me know if it is too soon to start reporting this. 2.6.14 is
> OK and does not output this.

If you disable PNP does it go away?

Dmitry, any thoughts? This looks like the other reported issue.

thanks,

greg k-h

2005-11-01 03:21:16

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Friday 28 October 2005 22:17, Greg KH wrote:
> On Fri, Oct 28, 2005 at 06:18:57PM -0600, Alejandro Bonilla Beeche wrote:
> > Hi,
> >
> > I just pulled from Linus Tree and I'm getting this badness in dmesg.
> >
> > Please let me know if it is too soon to start reporting this. 2.6.14 is
> > OK and does not output this.
>
> If you disable PNP does it go away?
>
> Dmitry, any thoughts? This looks like the other reported issue.
>

I was looking and looking and the only thing I could come up with is
that we probably need to initialize input core earlier, before other
modules had a chance to use input interface so input class is fully
initialized. We don't need to have input/{ev|mouse|ts|joy}dev.o,
just input/input.o itself.

--
Dmitry

2005-11-01 07:51:36

by Greg KH

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Mon, Oct 31, 2005 at 10:21:12PM -0500, Dmitry Torokhov wrote:
> On Friday 28 October 2005 22:17, Greg KH wrote:
> > On Fri, Oct 28, 2005 at 06:18:57PM -0600, Alejandro Bonilla Beeche wrote:
> > > Hi,
> > >
> > > I just pulled from Linus Tree and I'm getting this badness in dmesg.
> > >
> > > Please let me know if it is too soon to start reporting this. 2.6.14 is
> > > OK and does not output this.
> >
> > If you disable PNP does it go away?
> >
> > Dmitry, any thoughts? This looks like the other reported issue.
> >
>
> I was looking and looking and the only thing I could come up with is
> that we probably need to initialize input core earlier, before other
> modules had a chance to use input interface so input class is fully
> initialized. We don't need to have input/{ev|mouse|ts|joy}dev.o,
> just input/input.o itself.

Then why not move this input driver into a different directory so it
doesn't cause this issue?

Robert?

2005-11-01 07:58:18

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Tuesday 01 November 2005 02:35, Greg KH wrote:
> On Mon, Oct 31, 2005 at 10:21:12PM -0500, Dmitry Torokhov wrote:
> > On Friday 28 October 2005 22:17, Greg KH wrote:
> > > On Fri, Oct 28, 2005 at 06:18:57PM -0600, Alejandro Bonilla Beeche wrote:
> > > > Hi,
> > > >
> > > > I just pulled from Linus Tree and I'm getting this badness in dmesg.
> > > >
> > > > Please let me know if it is too soon to start reporting this. 2.6.14 is
> > > > OK and does not output this.
> > >
> > > If you disable PNP does it go away?
> > >
> > > Dmitry, any thoughts? This looks like the other reported issue.
> > >
> >
> > I was looking and looking and the only thing I could come up with is
> > that we probably need to initialize input core earlier, before other
> > modules had a chance to use input interface so input class is fully
> > initialized. We don't need to have input/{ev|mouse|ts|joy}dev.o,
> > just input/input.o itself.
>
> Then why not move this input driver into a different directory so it
> doesn't cause this issue?
>

Can't we move just input.o closer to the top of drivers/Makefile? It is
kinda silly to have a subdirectory with only one file. And I would move
serio.o there as well.

--
Dmitry

2005-11-01 08:15:11

by Greg KH

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Tue, Nov 01, 2005 at 02:58:13AM -0500, Dmitry Torokhov wrote:
> On Tuesday 01 November 2005 02:35, Greg KH wrote:
> > On Mon, Oct 31, 2005 at 10:21:12PM -0500, Dmitry Torokhov wrote:
> > > On Friday 28 October 2005 22:17, Greg KH wrote:
> > > > On Fri, Oct 28, 2005 at 06:18:57PM -0600, Alejandro Bonilla Beeche wrote:
> > > > > Hi,
> > > > >
> > > > > I just pulled from Linus Tree and I'm getting this badness in dmesg.
> > > > >
> > > > > Please let me know if it is too soon to start reporting this. 2.6.14 is
> > > > > OK and does not output this.
> > > >
> > > > If you disable PNP does it go away?
> > > >
> > > > Dmitry, any thoughts? This looks like the other reported issue.
> > > >
> > >
> > > I was looking and looking and the only thing I could come up with is
> > > that we probably need to initialize input core earlier, before other
> > > modules had a chance to use input interface so input class is fully
> > > initialized. We don't need to have input/{ev|mouse|ts|joy}dev.o,
> > > just input/input.o itself.
> >
> > Then why not move this input driver into a different directory so it
> > doesn't cause this issue?
> >
>
> Can't we move just input.o closer to the top of drivers/Makefile? It is
> kinda silly to have a subdirectory with only one file. And I would move
> serio.o there as well.

I don't have a problem with this, try it out and see what breaks :)

thanks,

greg k-h

2005-11-01 14:11:06

by Robert Love

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:

> I don't have a problem with this, try it out and see what breaks :)

I don't mind moving the driver (as Greg suggested earlier) if needed,
but if Dmitry's idea to move input.o works, even better.

Robert Love


2005-11-01 14:45:54

by Alejandro Bonilla

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Tue, 01 Nov 2005 09:11:57 -0500, Robert Love wrote
> On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:
>
> > I don't have a problem with this, try it out and see what breaks :)
>
> I don't mind moving the driver (as Greg suggested earlier) if needed,
> but if Dmitry's idea to move input.o works, even better.
>

I can try the suggested solution if I'm told how to. ;-)

Thanks for looking into this.

.Alejandro

2005-11-01 15:20:37

by Bill Davidsen

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

Robert Love wrote:
> On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:
>
>
>>I don't have a problem with this, try it out and see what breaks :)
>
>
> I don't mind moving the driver (as Greg suggested earlier) if needed,
> but if Dmitry's idea to move input.o works, even better.

What about serio? Can that be used too early as well? Serial console?
--
-bill davidsen ([email protected])
"The secret to procrastination is to put things off until the
last possible moment - but no longer" -me

2005-11-01 15:28:08

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On 11/1/05, Bill Davidsen <[email protected]> wrote:
>
> What about serio? Can that be used too early as well? Serial console?

Serio seems to be in the right place for now but if pulling input.o to
the top works well I think I'll do the same with serio.

--
Dmitry

2005-11-01 16:38:53

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On 11/1/05, Alejandro Bonilla <[email protected]> wrote:
> On Tue, 01 Nov 2005 09:11:57 -0500, Robert Love wrote
> > On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:
> >
> > > I don't have a problem with this, try it out and see what breaks :)
> >
> > I don't mind moving the driver (as Greg suggested earlier) if needed,
> > but if Dmitry's idea to move input.o works, even better.
> >
>
> I can try the suggested solution if I'm told how to. ;-)
>

Could you try the attached (I did compile it but didn't try to boot).

--
Dmitry


Attachments:
(No filename) (542.00 B)
input-move-core-earlier.patch (1.32 kB)
Download all attachments

2005-11-01 17:22:40

by Alejandro Bonilla

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Tue, 1 Nov 2005 11:38:51 -0500, Dmitry Torokhov wrote
> On 11/1/05, Alejandro Bonilla <[email protected]> wrote:
> > On Tue, 01 Nov 2005 09:11:57 -0500, Robert Love wrote
> > > On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:
> > >
> > > > I don't have a problem with this, try it out and see what breaks :)
> > >
> > > I don't mind moving the driver (as Greg suggested earlier) if needed,
> > > but if Dmitry's idea to move input.o works, even better.
> > >
> >
> > I can try the suggested solution if I'm told how to. ;-)
> >
>
> Could you try the attached (I did compile it but didn't try to boot).

LD drivers/video/built-in.o
LD drivers/w1/built-in.o
LD drivers/built-in.o
ld: drivers/input/input.o: No such file: No such file or directory
make[2]: *** [drivers/built-in.o] Error 1
make[1]: *** [drivers] Error 2
make[1]: Leaving directory `/root/linux-2.6'
make: *** [stamp-build] Error 2

I did a little fetch for Linus tree before patching, but it downloaded just a
couple of changes, I'm now running a kernel that I compiled like 2 hours ago
after some major fetch from Linus. I think this error is because of the patch?

.Alejandro

> --
> Dmitry


2005-11-01 18:00:47

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On 11/1/05, Alejandro Bonilla <[email protected]> wrote:
> On Tue, 1 Nov 2005 11:38:51 -0500, Dmitry Torokhov wrote
> > On 11/1/05, Alejandro Bonilla <[email protected]> wrote:
> > > On Tue, 01 Nov 2005 09:11:57 -0500, Robert Love wrote
> > > > On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:
> > > >
> > > > > I don't have a problem with this, try it out and see what breaks :)
> > > >
> > > > I don't mind moving the driver (as Greg suggested earlier) if needed,
> > > > but if Dmitry's idea to move input.o works, even better.
> > > >
> > >
> > > I can try the suggested solution if I'm told how to. ;-)
> > >
> >
> > Could you try the attached (I did compile it but didn't try to boot).
>
> LD drivers/video/built-in.o
> LD drivers/w1/built-in.o
> LD drivers/built-in.o
> ld: drivers/input/input.o: No such file: No such file or directory
> make[2]: *** [drivers/built-in.o] Error 1
> make[1]: *** [drivers] Error 2
> make[1]: Leaving directory `/root/linux-2.6'
> make: *** [stamp-build] Error 2
>
> I did a little fetch for Linus tree before patching, but it downloaded just a
> couple of changes, I'm now running a kernel that I compiled like 2 hours ago
> after some major fetch from Linus. I think this error is because of the patch?
>

Yes it is. Apparently I had input.o already compiled so it built for
me. Hmm, need to study Linux build system now...

--
Dmitry

2005-11-03 04:38:07

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Kernel Badness 2.6.14-Git

On Tuesday 01 November 2005 12:22, Alejandro Bonilla wrote:
> On Tue, 1 Nov 2005 11:38:51 -0500, Dmitry Torokhov wrote
> > On 11/1/05, Alejandro Bonilla <[email protected]> wrote:
> > > On Tue, 01 Nov 2005 09:11:57 -0500, Robert Love wrote
> > > > On Tue, 2005-11-01 at 00:14 -0800, Greg KH wrote:
> > > >
> > > > > I don't have a problem with this, try it out and see what breaks :)
> > > >
> > > > I don't mind moving the driver (as Greg suggested earlier) if needed,
> > > > but if Dmitry's idea to move input.o works, even better.
> > > >
> > >
> > > I can try the suggested solution if I'm told how to. ;-)
> > >
> >
> > Could you try the attached (I did compile it but didn't try to boot).
>
> LD drivers/video/built-in.o
> LD drivers/w1/built-in.o
> LD drivers/built-in.o
> ld: drivers/input/input.o: No such file: No such file or directory
> make[2]: *** [drivers/built-in.o] Error 1
> make[1]: *** [drivers] Error 2
> make[1]: Leaving directory `/root/linux-2.6'
> make: *** [stamp-build] Error 2
>
> I did a little fetch for Linus tree before patching, but it downloaded just a
> couple of changes, I'm now running a kernel that I compiled like 2 hours ago
> after some major fetch from Linus. I think this error is because of the patch?
>

Ok, I gave up on trying moving just input.o into drivers/Makefile. Here is
a more straightfoward patch that created drivers/input/core/ and moves
input.o there.

Could you please try it?

Thanks!

--
Dmitry

Input: register input core early

Signed-off-by: Dmitry Torokhov <[email protected]>
---

drivers/input/input.c | 923 --------------------------------------------
drivers/Makefile | 4
drivers/input/Makefile | 1
drivers/input/core/Makefile | 6
drivers/input/core/input.c | 923 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 933 insertions(+), 924 deletions(-)

Index: work/drivers/Makefile
===================================================================
--- work.orig/drivers/Makefile
+++ work/drivers/Makefile
@@ -5,6 +5,10 @@
# Rewritten to use lists instead of if-statements.
#

+# we need to register input core early so input class is fully
+# initialized befpre anyone tries to use it
+obj-$(CONFIG_INPUT) += input/core/
+
obj-$(CONFIG_PCI) += pci/ usb/
obj-$(CONFIG_PARISC) += parisc/
obj-y += video/
Index: work/drivers/input/Makefile
===================================================================
--- work.orig/drivers/input/Makefile
+++ work/drivers/input/Makefile
@@ -4,7 +4,6 @@

# Each configuration option enables a list of files.

-obj-$(CONFIG_INPUT) += input.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
Index: work/drivers/input/core/Makefile
===================================================================
--- /dev/null
+++ work/drivers/input/core/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the input core.
+#
+
+obj-$(CONFIG_INPUT) += input.o
+
Index: work/drivers/input/core/input.c
===================================================================
--- /dev/null
+++ work/drivers/input/core/input.c
@@ -0,0 +1,923 @@
+/*
+ * The input core
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/kobject_uevent.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
+MODULE_DESCRIPTION("Input core");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(input_allocate_device);
+EXPORT_SYMBOL(input_register_device);
+EXPORT_SYMBOL(input_unregister_device);
+EXPORT_SYMBOL(input_register_handler);
+EXPORT_SYMBOL(input_unregister_handler);
+EXPORT_SYMBOL(input_grab_device);
+EXPORT_SYMBOL(input_release_device);
+EXPORT_SYMBOL(input_open_device);
+EXPORT_SYMBOL(input_close_device);
+EXPORT_SYMBOL(input_accept_process);
+EXPORT_SYMBOL(input_flush_device);
+EXPORT_SYMBOL(input_event);
+EXPORT_SYMBOL_GPL(input_class);
+
+#define INPUT_DEVICES 256
+
+static LIST_HEAD(input_dev_list);
+static LIST_HEAD(input_handler_list);
+
+static struct input_handler *input_table[8];
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct input_handle *handle;
+
+ if (type > EV_MAX || !test_bit(type, dev->evbit))
+ return;
+
+ add_input_randomness(type, code, value);
+
+ switch (type) {
+
+ case EV_SYN:
+ switch (code) {
+ case SYN_CONFIG:
+ if (dev->event) dev->event(dev, type, code, value);
+ break;
+
+ case SYN_REPORT:
+ if (dev->sync) return;
+ dev->sync = 1;
+ break;
+ }
+ break;
+
+ case EV_KEY:
+
+ if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
+ return;
+
+ if (value == 2)
+ break;
+
+ change_bit(code, dev->key);
+
+ if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
+ dev->repeat_key = code;
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+ }
+
+ break;
+
+ case EV_SW:
+
+ if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
+ return;
+
+ change_bit(code, dev->sw);
+
+ break;
+
+ case EV_ABS:
+
+ if (code > ABS_MAX || !test_bit(code, dev->absbit))
+ return;
+
+ if (dev->absfuzz[code]) {
+ if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+ return;
+
+ if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+ (value < dev->abs[code] + dev->absfuzz[code]))
+ value = (dev->abs[code] * 3 + value) >> 2;
+
+ if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+ (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+ value = (dev->abs[code] + value) >> 1;
+ }
+
+ if (dev->abs[code] == value)
+ return;
+
+ dev->abs[code] = value;
+ break;
+
+ case EV_REL:
+
+ if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
+ return;
+
+ break;
+
+ case EV_MSC:
+
+ if (code > MSC_MAX || !test_bit(code, dev->mscbit))
+ return;
+
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_LED:
+
+ if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
+ return;
+
+ change_bit(code, dev->led);
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_SND:
+
+ if (code > SND_MAX || !test_bit(code, dev->sndbit))
+ return;
+
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_REP:
+
+ if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
+
+ dev->rep[code] = value;
+ if (dev->event) dev->event(dev, type, code, value);
+
+ break;
+
+ case EV_FF:
+ if (dev->event) dev->event(dev, type, code, value);
+ break;
+ }
+
+ if (type != EV_SYN)
+ dev->sync = 0;
+
+ if (dev->grab)
+ dev->grab->handler->event(dev->grab, type, code, value);
+ else
+ list_for_each_entry(handle, &dev->h_list, d_node)
+ if (handle->open)
+ handle->handler->event(handle, type, code, value);
+}
+
+static void input_repeat_key(unsigned long data)
+{
+ struct input_dev *dev = (void *) data;
+
+ if (!test_bit(dev->repeat_key, dev->key))
+ return;
+
+ input_event(dev, EV_KEY, dev->repeat_key, 2);
+ input_sync(dev);
+
+ if (dev->rep[REP_PERIOD])
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
+}
+
+int input_accept_process(struct input_handle *handle, struct file *file)
+{
+ if (handle->dev->accept)
+ return handle->dev->accept(handle->dev, file);
+
+ return 0;
+}
+
+int input_grab_device(struct input_handle *handle)
+{
+ if (handle->dev->grab)
+ return -EBUSY;
+
+ handle->dev->grab = handle;
+ return 0;
+}
+
+void input_release_device(struct input_handle *handle)
+{
+ if (handle->dev->grab == handle)
+ handle->dev->grab = NULL;
+}
+
+int input_open_device(struct input_handle *handle)
+{
+ struct input_dev *dev = handle->dev;
+ int err;
+
+ err = down_interruptible(&dev->sem);
+ if (err)
+ return err;
+
+ handle->open++;
+
+ if (!dev->users++ && dev->open)
+ err = dev->open(dev);
+
+ if (err)
+ handle->open--;
+
+ up(&dev->sem);
+
+ return err;
+}
+
+int input_flush_device(struct input_handle* handle, struct file* file)
+{
+ if (handle->dev->flush)
+ return handle->dev->flush(handle->dev, file);
+
+ return 0;
+}
+
+void input_close_device(struct input_handle *handle)
+{
+ struct input_dev *dev = handle->dev;
+
+ input_release_device(handle);
+
+ down(&dev->sem);
+
+ if (!--dev->users && dev->close)
+ dev->close(dev);
+ handle->open--;
+
+ up(&dev->sem);
+}
+
+static void input_link_handle(struct input_handle *handle)
+{
+ list_add_tail(&handle->d_node, &handle->dev->h_list);
+ list_add_tail(&handle->h_node, &handle->handler->h_list);
+}
+
+#define MATCH_BIT(bit, max) \
+ for (i = 0; i < NBITS(max); i++) \
+ if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
+ break; \
+ if (i != NBITS(max)) \
+ continue;
+
+static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
+{
+ int i;
+
+ for (; id->flags || id->driver_info; id++) {
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
+ if (id->id.bustype != dev->id.bustype)
+ continue;
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
+ if (id->id.vendor != dev->id.vendor)
+ continue;
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
+ if (id->id.product != dev->id.product)
+ continue;
+
+ if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
+ if (id->id.version != dev->id.version)
+ continue;
+
+ MATCH_BIT(evbit, EV_MAX);
+ MATCH_BIT(keybit, KEY_MAX);
+ MATCH_BIT(relbit, REL_MAX);
+ MATCH_BIT(absbit, ABS_MAX);
+ MATCH_BIT(mscbit, MSC_MAX);
+ MATCH_BIT(ledbit, LED_MAX);
+ MATCH_BIT(sndbit, SND_MAX);
+ MATCH_BIT(ffbit, FF_MAX);
+ MATCH_BIT(swbit, SW_MAX);
+
+ return id;
+ }
+
+ return NULL;
+}
+
+static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, int max)
+{
+ int i;
+ int len = 0;
+
+ for (i = NBITS(max) - 1; i > 0; i--)
+ if (bitmap[i])
+ break;
+
+ for (; i >= 0; i--)
+ len += snprintf(buf + len, max(buf_size - len, 0),
+ "%lx%s", bitmap[i], i > 0 ? " " : "");
+ return len;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static struct proc_dir_entry *proc_bus_input_dir;
+static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
+static int input_devices_state;
+
+static inline void input_wakeup_procfs_readers(void)
+{
+ input_devices_state++;
+ wake_up(&input_devices_poll_wait);
+}
+
+static unsigned int input_devices_poll(struct file *file, poll_table *wait)
+{
+ int state = input_devices_state;
+ poll_wait(file, &input_devices_poll_wait, wait);
+ if (state != input_devices_state)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+#define SPRINTF_BIT(ev, bm) \
+ do { \
+ len += sprintf(buf + len, "B: %s=", #ev); \
+ len += input_print_bitmap(buf + len, INT_MAX, \
+ dev->bm##bit, ev##_MAX); \
+ len += sprintf(buf + len, "\n"); \
+ } while (0)
+
+#define TEST_AND_SPRINTF_BIT(ev, bm) \
+ do { \
+ if (test_bit(EV_##ev, dev->evbit)) \
+ SPRINTF_BIT(ev, bm); \
+ } while (0)
+
+static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+{
+ struct input_dev *dev;
+ struct input_handle *handle;
+ const char *path;
+
+ off_t at = 0;
+ int len, cnt = 0;
+
+ list_for_each_entry(dev, &input_dev_list, node) {
+
+ path = dev->dynalloc ? kobject_get_path(&dev->cdev.kobj, GFP_KERNEL) : NULL;
+
+ len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
+ dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
+
+ len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
+ len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
+ len += sprintf(buf + len, "S: Sysfs=%s\n", path ? path : "");
+ len += sprintf(buf + len, "H: Handlers=");
+
+ list_for_each_entry(handle, &dev->h_list, d_node)
+ len += sprintf(buf + len, "%s ", handle->name);
+
+ len += sprintf(buf + len, "\n");
+
+ SPRINTF_BIT(EV, ev);
+ TEST_AND_SPRINTF_BIT(KEY, key);
+ TEST_AND_SPRINTF_BIT(REL, rel);
+ TEST_AND_SPRINTF_BIT(ABS, abs);
+ TEST_AND_SPRINTF_BIT(MSC, msc);
+ TEST_AND_SPRINTF_BIT(LED, led);
+ TEST_AND_SPRINTF_BIT(SND, snd);
+ TEST_AND_SPRINTF_BIT(FF, ff);
+ TEST_AND_SPRINTF_BIT(SW, sw);
+
+ len += sprintf(buf + len, "\n");
+
+ at += len;
+
+ if (at >= pos) {
+ if (!*start) {
+ *start = buf + (pos - (at - len));
+ cnt = at - pos;
+ } else cnt += len;
+ buf += len;
+ if (cnt >= count)
+ break;
+ }
+
+ kfree(path);
+ }
+
+ if (&dev->node == &input_dev_list)
+ *eof = 1;
+
+ return (count > cnt) ? cnt : count;
+}
+
+static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+{
+ struct input_handler *handler;
+
+ off_t at = 0;
+ int len = 0, cnt = 0;
+ int i = 0;
+
+ list_for_each_entry(handler, &input_handler_list, node) {
+
+ if (handler->fops)
+ len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
+ i++, handler->name, handler->minor);
+ else
+ len = sprintf(buf, "N: Number=%d Name=%s\n",
+ i++, handler->name);
+
+ at += len;
+
+ if (at >= pos) {
+ if (!*start) {
+ *start = buf + (pos - (at - len));
+ cnt = at - pos;
+ } else cnt += len;
+ buf += len;
+ if (cnt >= count)
+ break;
+ }
+ }
+ if (&handler->node == &input_handler_list)
+ *eof = 1;
+
+ return (count > cnt) ? cnt : count;
+}
+
+static struct file_operations input_fileops;
+
+static int __init input_proc_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ proc_bus_input_dir = proc_mkdir("input", proc_bus);
+ if (!proc_bus_input_dir)
+ return -ENOMEM;
+
+ proc_bus_input_dir->owner = THIS_MODULE;
+
+ entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
+ if (!entry)
+ goto fail1;
+
+ entry->owner = THIS_MODULE;
+ input_fileops = *entry->proc_fops;
+ entry->proc_fops = &input_fileops;
+ entry->proc_fops->poll = input_devices_poll;
+
+ entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
+ if (!entry)
+ goto fail2;
+
+ entry->owner = THIS_MODULE;
+
+ return 0;
+
+ fail2: remove_proc_entry("devices", proc_bus_input_dir);
+ fail1: remove_proc_entry("input", proc_bus);
+ return -ENOMEM;
+}
+
+static void input_proc_exit(void)
+{
+ remove_proc_entry("devices", proc_bus_input_dir);
+ remove_proc_entry("handlers", proc_bus_input_dir);
+ remove_proc_entry("input", proc_bus);
+}
+
+#else /* !CONFIG_PROC_FS */
+static inline void input_wakeup_procfs_readers(void) { }
+static inline int input_proc_init(void) { return 0; }
+static inline void input_proc_exit(void) { }
+#endif
+
+#define INPUT_DEV_STRING_ATTR_SHOW(name) \
+static ssize_t input_dev_show_##name(struct class_device *dev, char *buf) \
+{ \
+ struct input_dev *input_dev = to_input_dev(dev); \
+ int retval; \
+ \
+ retval = down_interruptible(&input_dev->sem); \
+ if (retval) \
+ return retval; \
+ \
+ retval = sprintf(buf, "%s\n", input_dev->name ? input_dev->name : ""); \
+ \
+ up(&input_dev->sem); \
+ \
+ return retval; \
+} \
+static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL);
+
+INPUT_DEV_STRING_ATTR_SHOW(name);
+INPUT_DEV_STRING_ATTR_SHOW(phys);
+INPUT_DEV_STRING_ATTR_SHOW(uniq);
+
+static struct attribute *input_dev_attrs[] = {
+ &class_device_attr_name.attr,
+ &class_device_attr_phys.attr,
+ &class_device_attr_uniq.attr,
+ NULL
+};
+
+static struct attribute_group input_dev_group = {
+ .attrs = input_dev_attrs,
+};
+
+#define INPUT_DEV_ID_ATTR(name) \
+static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf) \
+{ \
+ struct input_dev *input_dev = to_input_dev(dev); \
+ return sprintf(buf, "%04x\n", input_dev->id.name); \
+} \
+static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL);
+
+INPUT_DEV_ID_ATTR(bustype);
+INPUT_DEV_ID_ATTR(vendor);
+INPUT_DEV_ID_ATTR(product);
+INPUT_DEV_ID_ATTR(version);
+
+static struct attribute *input_dev_id_attrs[] = {
+ &class_device_attr_bustype.attr,
+ &class_device_attr_vendor.attr,
+ &class_device_attr_product.attr,
+ &class_device_attr_version.attr,
+ NULL
+};
+
+static struct attribute_group input_dev_id_attr_group = {
+ .name = "id",
+ .attrs = input_dev_id_attrs,
+};
+
+#define INPUT_DEV_CAP_ATTR(ev, bm) \
+static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf) \
+{ \
+ struct input_dev *input_dev = to_input_dev(dev); \
+ return input_print_bitmap(buf, PAGE_SIZE, input_dev->bm##bit, ev##_MAX);\
+} \
+static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL);
+
+INPUT_DEV_CAP_ATTR(EV, ev);
+INPUT_DEV_CAP_ATTR(KEY, key);
+INPUT_DEV_CAP_ATTR(REL, rel);
+INPUT_DEV_CAP_ATTR(ABS, abs);
+INPUT_DEV_CAP_ATTR(MSC, msc);
+INPUT_DEV_CAP_ATTR(LED, led);
+INPUT_DEV_CAP_ATTR(SND, snd);
+INPUT_DEV_CAP_ATTR(FF, ff);
+INPUT_DEV_CAP_ATTR(SW, sw);
+
+static struct attribute *input_dev_caps_attrs[] = {
+ &class_device_attr_ev.attr,
+ &class_device_attr_key.attr,
+ &class_device_attr_rel.attr,
+ &class_device_attr_abs.attr,
+ &class_device_attr_msc.attr,
+ &class_device_attr_led.attr,
+ &class_device_attr_snd.attr,
+ &class_device_attr_ff.attr,
+ &class_device_attr_sw.attr,
+ NULL
+};
+
+static struct attribute_group input_dev_caps_attr_group = {
+ .name = "capabilities",
+ .attrs = input_dev_caps_attrs,
+};
+
+static void input_dev_release(struct class_device *class_dev)
+{
+ struct input_dev *dev = to_input_dev(class_dev);
+
+ kfree(dev);
+ module_put(THIS_MODULE);
+}
+
+/*
+ * Input hotplugging interface - loading event handlers based on
+ * device bitfields.
+ */
+static int input_add_hotplug_bm_var(char **envp, int num_envp, int *cur_index,
+ char *buffer, int buffer_size, int *cur_len,
+ const char *name, unsigned long *bitmap, int max)
+{
+ if (*cur_index >= num_envp - 1)
+ return -ENOMEM;
+
+ envp[*cur_index] = buffer + *cur_len;
+
+ *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name);
+ if (*cur_len > buffer_size)
+ return -ENOMEM;
+
+ *cur_len += input_print_bitmap(buffer + *cur_len,
+ max(buffer_size - *cur_len, 0),
+ bitmap, max) + 1;
+ if (*cur_len > buffer_size)
+ return -ENOMEM;
+
+ (*cur_index)++;
+ return 0;
+}
+
+#define INPUT_ADD_HOTPLUG_VAR(fmt, val...) \
+ do { \
+ int err = add_hotplug_env_var(envp, num_envp, &i, \
+ buffer, buffer_size, &len, \
+ fmt, val); \
+ if (err) \
+ return err; \
+ } while (0)
+
+#define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max) \
+ do { \
+ int err = input_add_hotplug_bm_var(envp, num_envp, &i, \
+ buffer, buffer_size, &len, \
+ name, bm, max); \
+ if (err) \
+ return err; \
+ } while (0)
+
+static int input_dev_hotplug(struct class_device *cdev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ struct input_dev *dev = to_input_dev(cdev);
+ int i = 0;
+ int len = 0;
+
+ INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x",
+ dev->id.bustype, dev->id.vendor,
+ dev->id.product, dev->id.version);
+ if (dev->name)
+ INPUT_ADD_HOTPLUG_VAR("NAME=\"%s\"", dev->name);
+ if (dev->phys)
+ INPUT_ADD_HOTPLUG_VAR("PHYS=\"%s\"", dev->phys);
+ if (dev->phys)
+ INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq);
+
+ INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX);
+ if (test_bit(EV_KEY, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX);
+ if (test_bit(EV_REL, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX);
+ if (test_bit(EV_ABS, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX);
+ if (test_bit(EV_MSC, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX);
+ if (test_bit(EV_LED, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("LED=", dev->ledbit, LED_MAX);
+ if (test_bit(EV_SND, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("SND=", dev->sndbit, SND_MAX);
+ if (test_bit(EV_FF, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX);
+ if (test_bit(EV_SW, dev->evbit))
+ INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX);
+
+ envp[i] = NULL;
+
+ return 0;
+}
+
+struct class input_class = {
+ .name = "input",
+ .release = input_dev_release,
+ .hotplug = input_dev_hotplug,
+};
+
+struct input_dev *input_allocate_device(void)
+{
+ struct input_dev *dev;
+
+ dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
+ if (dev) {
+ dev->dynalloc = 1;
+ dev->cdev.class = &input_class;
+ class_device_initialize(&dev->cdev);
+ INIT_LIST_HEAD(&dev->h_list);
+ INIT_LIST_HEAD(&dev->node);
+ }
+
+ return dev;
+}
+
+static void input_register_classdevice(struct input_dev *dev)
+{
+ static atomic_t input_no = ATOMIC_INIT(0);
+ const char *path;
+
+ __module_get(THIS_MODULE);
+
+ dev->dev = dev->cdev.dev;
+
+ snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
+ "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
+
+ path = kobject_get_path(&dev->cdev.class->subsys.kset.kobj, GFP_KERNEL);
+ printk(KERN_INFO "input: %s as %s/%s\n",
+ dev->name ? dev->name : "Unspecified device",
+ path ? path : "", dev->cdev.class_id);
+ kfree(path);
+
+ class_device_add(&dev->cdev);
+ sysfs_create_group(&dev->cdev.kobj, &input_dev_group);
+ sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
+ sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
+}
+
+void input_register_device(struct input_dev *dev)
+{
+ struct input_handle *handle;
+ struct input_handler *handler;
+ struct input_device_id *id;
+
+ set_bit(EV_SYN, dev->evbit);
+
+ init_MUTEX(&dev->sem);
+
+ /*
+ * If delay and period are pre-set by the driver, then autorepeating
+ * is handled by the driver itself and we don't do it in input.c.
+ */
+
+ init_timer(&dev->timer);
+ if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
+ dev->timer.data = (long) dev;
+ dev->timer.function = input_repeat_key;
+ dev->rep[REP_DELAY] = 250;
+ dev->rep[REP_PERIOD] = 33;
+ }
+
+ INIT_LIST_HEAD(&dev->h_list);
+ list_add_tail(&dev->node, &input_dev_list);
+
+ if (dev->dynalloc)
+ input_register_classdevice(dev);
+
+ list_for_each_entry(handler, &input_handler_list, node)
+ if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+ if ((id = input_match_device(handler->id_table, dev)))
+ if ((handle = handler->connect(handler, dev, id)))
+ input_link_handle(handle);
+
+
+ input_wakeup_procfs_readers();
+}
+
+void input_unregister_device(struct input_dev *dev)
+{
+ struct list_head * node, * next;
+
+ if (!dev) return;
+
+ del_timer_sync(&dev->timer);
+
+ list_for_each_safe(node, next, &dev->h_list) {
+ struct input_handle * handle = to_handle(node);
+ list_del_init(&handle->d_node);
+ list_del_init(&handle->h_node);
+ handle->handler->disconnect(handle);
+ }
+
+ list_del_init(&dev->node);
+
+ if (dev->dynalloc) {
+ sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
+ sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
+ class_device_unregister(&dev->cdev);
+ }
+
+ input_wakeup_procfs_readers();
+}
+
+void input_register_handler(struct input_handler *handler)
+{
+ struct input_dev *dev;
+ struct input_handle *handle;
+ struct input_device_id *id;
+
+ if (!handler) return;
+
+ INIT_LIST_HEAD(&handler->h_list);
+
+ if (handler->fops != NULL)
+ input_table[handler->minor >> 5] = handler;
+
+ list_add_tail(&handler->node, &input_handler_list);
+
+ list_for_each_entry(dev, &input_dev_list, node)
+ if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+ if ((id = input_match_device(handler->id_table, dev)))
+ if ((handle = handler->connect(handler, dev, id)))
+ input_link_handle(handle);
+
+ input_wakeup_procfs_readers();
+}
+
+void input_unregister_handler(struct input_handler *handler)
+{
+ struct list_head * node, * next;
+
+ list_for_each_safe(node, next, &handler->h_list) {
+ struct input_handle * handle = to_handle_h(node);
+ list_del_init(&handle->h_node);
+ list_del_init(&handle->d_node);
+ handler->disconnect(handle);
+ }
+
+ list_del_init(&handler->node);
+
+ if (handler->fops != NULL)
+ input_table[handler->minor >> 5] = NULL;
+
+ input_wakeup_procfs_readers();
+}
+
+static int input_open_file(struct inode *inode, struct file *file)
+{
+ struct input_handler *handler = input_table[iminor(inode) >> 5];
+ struct file_operations *old_fops, *new_fops = NULL;
+ int err;
+
+ /* No load-on-demand here? */
+ if (!handler || !(new_fops = fops_get(handler->fops)))
+ return -ENODEV;
+
+ /*
+ * That's _really_ odd. Usually NULL ->open means "nothing special",
+ * not "no device". Oh, well...
+ */
+ if (!new_fops->open) {
+ fops_put(new_fops);
+ return -ENODEV;
+ }
+ old_fops = file->f_op;
+ file->f_op = new_fops;
+
+ err = new_fops->open(inode, file);
+
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ fops_put(old_fops);
+ return err;
+}
+
+static struct file_operations input_fops = {
+ .owner = THIS_MODULE,
+ .open = input_open_file,
+};
+
+static int __init input_init(void)
+{
+ int err;
+
+ err = class_register(&input_class);
+ if (err) {
+ printk(KERN_ERR "input: unable to register input_dev class\n");
+ return err;
+ }
+
+ err = input_proc_init();
+ if (err)
+ goto fail1;
+
+ err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
+ if (err) {
+ printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
+ goto fail2;
+ }
+
+ return 0;
+
+ fail2: input_proc_exit();
+ fail1: class_unregister(&input_class);
+ return err;
+}
+
+static void __exit input_exit(void)
+{
+ input_proc_exit();
+ unregister_chrdev(INPUT_MAJOR, "input");
+ class_unregister(&input_class);
+}
+
+subsys_initcall(input_init);
+module_exit(input_exit);
Index: work/drivers/input/input.c
===================================================================
--- work.orig/drivers/input/input.c
+++ /dev/null
@@ -1,923 +0,0 @@
-/*
- * The input core
- *
- * Copyright (c) 1999-2002 Vojtech Pavlik
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/random.h>
-#include <linux/major.h>
-#include <linux/proc_fs.h>
-#include <linux/kobject_uevent.h>
-#include <linux/interrupt.h>
-#include <linux/poll.h>
-#include <linux/device.h>
-
-MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
-MODULE_DESCRIPTION("Input core");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(input_allocate_device);
-EXPORT_SYMBOL(input_register_device);
-EXPORT_SYMBOL(input_unregister_device);
-EXPORT_SYMBOL(input_register_handler);
-EXPORT_SYMBOL(input_unregister_handler);
-EXPORT_SYMBOL(input_grab_device);
-EXPORT_SYMBOL(input_release_device);
-EXPORT_SYMBOL(input_open_device);
-EXPORT_SYMBOL(input_close_device);
-EXPORT_SYMBOL(input_accept_process);
-EXPORT_SYMBOL(input_flush_device);
-EXPORT_SYMBOL(input_event);
-EXPORT_SYMBOL_GPL(input_class);
-
-#define INPUT_DEVICES 256
-
-static LIST_HEAD(input_dev_list);
-static LIST_HEAD(input_handler_list);
-
-static struct input_handler *input_table[8];
-
-void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
- struct input_handle *handle;
-
- if (type > EV_MAX || !test_bit(type, dev->evbit))
- return;
-
- add_input_randomness(type, code, value);
-
- switch (type) {
-
- case EV_SYN:
- switch (code) {
- case SYN_CONFIG:
- if (dev->event) dev->event(dev, type, code, value);
- break;
-
- case SYN_REPORT:
- if (dev->sync) return;
- dev->sync = 1;
- break;
- }
- break;
-
- case EV_KEY:
-
- if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
- return;
-
- if (value == 2)
- break;
-
- change_bit(code, dev->key);
-
- if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
- dev->repeat_key = code;
- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
- }
-
- break;
-
- case EV_SW:
-
- if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
- return;
-
- change_bit(code, dev->sw);
-
- break;
-
- case EV_ABS:
-
- if (code > ABS_MAX || !test_bit(code, dev->absbit))
- return;
-
- if (dev->absfuzz[code]) {
- if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
- (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
- return;
-
- if ((value > dev->abs[code] - dev->absfuzz[code]) &&
- (value < dev->abs[code] + dev->absfuzz[code]))
- value = (dev->abs[code] * 3 + value) >> 2;
-
- if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
- (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
- value = (dev->abs[code] + value) >> 1;
- }
-
- if (dev->abs[code] == value)
- return;
-
- dev->abs[code] = value;
- break;
-
- case EV_REL:
-
- if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
- return;
-
- break;
-
- case EV_MSC:
-
- if (code > MSC_MAX || !test_bit(code, dev->mscbit))
- return;
-
- if (dev->event) dev->event(dev, type, code, value);
-
- break;
-
- case EV_LED:
-
- if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
- return;
-
- change_bit(code, dev->led);
- if (dev->event) dev->event(dev, type, code, value);
-
- break;
-
- case EV_SND:
-
- if (code > SND_MAX || !test_bit(code, dev->sndbit))
- return;
-
- if (dev->event) dev->event(dev, type, code, value);
-
- break;
-
- case EV_REP:
-
- if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
-
- dev->rep[code] = value;
- if (dev->event) dev->event(dev, type, code, value);
-
- break;
-
- case EV_FF:
- if (dev->event) dev->event(dev, type, code, value);
- break;
- }
-
- if (type != EV_SYN)
- dev->sync = 0;
-
- if (dev->grab)
- dev->grab->handler->event(dev->grab, type, code, value);
- else
- list_for_each_entry(handle, &dev->h_list, d_node)
- if (handle->open)
- handle->handler->event(handle, type, code, value);
-}
-
-static void input_repeat_key(unsigned long data)
-{
- struct input_dev *dev = (void *) data;
-
- if (!test_bit(dev->repeat_key, dev->key))
- return;
-
- input_event(dev, EV_KEY, dev->repeat_key, 2);
- input_sync(dev);
-
- if (dev->rep[REP_PERIOD])
- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
-}
-
-int input_accept_process(struct input_handle *handle, struct file *file)
-{
- if (handle->dev->accept)
- return handle->dev->accept(handle->dev, file);
-
- return 0;
-}
-
-int input_grab_device(struct input_handle *handle)
-{
- if (handle->dev->grab)
- return -EBUSY;
-
- handle->dev->grab = handle;
- return 0;
-}
-
-void input_release_device(struct input_handle *handle)
-{
- if (handle->dev->grab == handle)
- handle->dev->grab = NULL;
-}
-
-int input_open_device(struct input_handle *handle)
-{
- struct input_dev *dev = handle->dev;
- int err;
-
- err = down_interruptible(&dev->sem);
- if (err)
- return err;
-
- handle->open++;
-
- if (!dev->users++ && dev->open)
- err = dev->open(dev);
-
- if (err)
- handle->open--;
-
- up(&dev->sem);
-
- return err;
-}
-
-int input_flush_device(struct input_handle* handle, struct file* file)
-{
- if (handle->dev->flush)
- return handle->dev->flush(handle->dev, file);
-
- return 0;
-}
-
-void input_close_device(struct input_handle *handle)
-{
- struct input_dev *dev = handle->dev;
-
- input_release_device(handle);
-
- down(&dev->sem);
-
- if (!--dev->users && dev->close)
- dev->close(dev);
- handle->open--;
-
- up(&dev->sem);
-}
-
-static void input_link_handle(struct input_handle *handle)
-{
- list_add_tail(&handle->d_node, &handle->dev->h_list);
- list_add_tail(&handle->h_node, &handle->handler->h_list);
-}
-
-#define MATCH_BIT(bit, max) \
- for (i = 0; i < NBITS(max); i++) \
- if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
- break; \
- if (i != NBITS(max)) \
- continue;
-
-static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
-{
- int i;
-
- for (; id->flags || id->driver_info; id++) {
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
- if (id->id.bustype != dev->id.bustype)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
- if (id->id.vendor != dev->id.vendor)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
- if (id->id.product != dev->id.product)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
- if (id->id.version != dev->id.version)
- continue;
-
- MATCH_BIT(evbit, EV_MAX);
- MATCH_BIT(keybit, KEY_MAX);
- MATCH_BIT(relbit, REL_MAX);
- MATCH_BIT(absbit, ABS_MAX);
- MATCH_BIT(mscbit, MSC_MAX);
- MATCH_BIT(ledbit, LED_MAX);
- MATCH_BIT(sndbit, SND_MAX);
- MATCH_BIT(ffbit, FF_MAX);
- MATCH_BIT(swbit, SW_MAX);
-
- return id;
- }
-
- return NULL;
-}
-
-static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, int max)
-{
- int i;
- int len = 0;
-
- for (i = NBITS(max) - 1; i > 0; i--)
- if (bitmap[i])
- break;
-
- for (; i >= 0; i--)
- len += snprintf(buf + len, max(buf_size - len, 0),
- "%lx%s", bitmap[i], i > 0 ? " " : "");
- return len;
-}
-
-#ifdef CONFIG_PROC_FS
-
-static struct proc_dir_entry *proc_bus_input_dir;
-static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
-static int input_devices_state;
-
-static inline void input_wakeup_procfs_readers(void)
-{
- input_devices_state++;
- wake_up(&input_devices_poll_wait);
-}
-
-static unsigned int input_devices_poll(struct file *file, poll_table *wait)
-{
- int state = input_devices_state;
- poll_wait(file, &input_devices_poll_wait, wait);
- if (state != input_devices_state)
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-#define SPRINTF_BIT(ev, bm) \
- do { \
- len += sprintf(buf + len, "B: %s=", #ev); \
- len += input_print_bitmap(buf + len, INT_MAX, \
- dev->bm##bit, ev##_MAX); \
- len += sprintf(buf + len, "\n"); \
- } while (0)
-
-#define TEST_AND_SPRINTF_BIT(ev, bm) \
- do { \
- if (test_bit(EV_##ev, dev->evbit)) \
- SPRINTF_BIT(ev, bm); \
- } while (0)
-
-static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
-{
- struct input_dev *dev;
- struct input_handle *handle;
- const char *path;
-
- off_t at = 0;
- int len, cnt = 0;
-
- list_for_each_entry(dev, &input_dev_list, node) {
-
- path = dev->dynalloc ? kobject_get_path(&dev->cdev.kobj, GFP_KERNEL) : NULL;
-
- len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
- dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
-
- len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
- len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
- len += sprintf(buf + len, "S: Sysfs=%s\n", path ? path : "");
- len += sprintf(buf + len, "H: Handlers=");
-
- list_for_each_entry(handle, &dev->h_list, d_node)
- len += sprintf(buf + len, "%s ", handle->name);
-
- len += sprintf(buf + len, "\n");
-
- SPRINTF_BIT(EV, ev);
- TEST_AND_SPRINTF_BIT(KEY, key);
- TEST_AND_SPRINTF_BIT(REL, rel);
- TEST_AND_SPRINTF_BIT(ABS, abs);
- TEST_AND_SPRINTF_BIT(MSC, msc);
- TEST_AND_SPRINTF_BIT(LED, led);
- TEST_AND_SPRINTF_BIT(SND, snd);
- TEST_AND_SPRINTF_BIT(FF, ff);
- TEST_AND_SPRINTF_BIT(SW, sw);
-
- len += sprintf(buf + len, "\n");
-
- at += len;
-
- if (at >= pos) {
- if (!*start) {
- *start = buf + (pos - (at - len));
- cnt = at - pos;
- } else cnt += len;
- buf += len;
- if (cnt >= count)
- break;
- }
-
- kfree(path);
- }
-
- if (&dev->node == &input_dev_list)
- *eof = 1;
-
- return (count > cnt) ? cnt : count;
-}
-
-static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
-{
- struct input_handler *handler;
-
- off_t at = 0;
- int len = 0, cnt = 0;
- int i = 0;
-
- list_for_each_entry(handler, &input_handler_list, node) {
-
- if (handler->fops)
- len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
- i++, handler->name, handler->minor);
- else
- len = sprintf(buf, "N: Number=%d Name=%s\n",
- i++, handler->name);
-
- at += len;
-
- if (at >= pos) {
- if (!*start) {
- *start = buf + (pos - (at - len));
- cnt = at - pos;
- } else cnt += len;
- buf += len;
- if (cnt >= count)
- break;
- }
- }
- if (&handler->node == &input_handler_list)
- *eof = 1;
-
- return (count > cnt) ? cnt : count;
-}
-
-static struct file_operations input_fileops;
-
-static int __init input_proc_init(void)
-{
- struct proc_dir_entry *entry;
-
- proc_bus_input_dir = proc_mkdir("input", proc_bus);
- if (!proc_bus_input_dir)
- return -ENOMEM;
-
- proc_bus_input_dir->owner = THIS_MODULE;
-
- entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
- if (!entry)
- goto fail1;
-
- entry->owner = THIS_MODULE;
- input_fileops = *entry->proc_fops;
- entry->proc_fops = &input_fileops;
- entry->proc_fops->poll = input_devices_poll;
-
- entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
- if (!entry)
- goto fail2;
-
- entry->owner = THIS_MODULE;
-
- return 0;
-
- fail2: remove_proc_entry("devices", proc_bus_input_dir);
- fail1: remove_proc_entry("input", proc_bus);
- return -ENOMEM;
-}
-
-static void input_proc_exit(void)
-{
- remove_proc_entry("devices", proc_bus_input_dir);
- remove_proc_entry("handlers", proc_bus_input_dir);
- remove_proc_entry("input", proc_bus);
-}
-
-#else /* !CONFIG_PROC_FS */
-static inline void input_wakeup_procfs_readers(void) { }
-static inline int input_proc_init(void) { return 0; }
-static inline void input_proc_exit(void) { }
-#endif
-
-#define INPUT_DEV_STRING_ATTR_SHOW(name) \
-static ssize_t input_dev_show_##name(struct class_device *dev, char *buf) \
-{ \
- struct input_dev *input_dev = to_input_dev(dev); \
- int retval; \
- \
- retval = down_interruptible(&input_dev->sem); \
- if (retval) \
- return retval; \
- \
- retval = sprintf(buf, "%s\n", input_dev->name ? input_dev->name : ""); \
- \
- up(&input_dev->sem); \
- \
- return retval; \
-} \
-static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL);
-
-INPUT_DEV_STRING_ATTR_SHOW(name);
-INPUT_DEV_STRING_ATTR_SHOW(phys);
-INPUT_DEV_STRING_ATTR_SHOW(uniq);
-
-static struct attribute *input_dev_attrs[] = {
- &class_device_attr_name.attr,
- &class_device_attr_phys.attr,
- &class_device_attr_uniq.attr,
- NULL
-};
-
-static struct attribute_group input_dev_group = {
- .attrs = input_dev_attrs,
-};
-
-#define INPUT_DEV_ID_ATTR(name) \
-static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf) \
-{ \
- struct input_dev *input_dev = to_input_dev(dev); \
- return sprintf(buf, "%04x\n", input_dev->id.name); \
-} \
-static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL);
-
-INPUT_DEV_ID_ATTR(bustype);
-INPUT_DEV_ID_ATTR(vendor);
-INPUT_DEV_ID_ATTR(product);
-INPUT_DEV_ID_ATTR(version);
-
-static struct attribute *input_dev_id_attrs[] = {
- &class_device_attr_bustype.attr,
- &class_device_attr_vendor.attr,
- &class_device_attr_product.attr,
- &class_device_attr_version.attr,
- NULL
-};
-
-static struct attribute_group input_dev_id_attr_group = {
- .name = "id",
- .attrs = input_dev_id_attrs,
-};
-
-#define INPUT_DEV_CAP_ATTR(ev, bm) \
-static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf) \
-{ \
- struct input_dev *input_dev = to_input_dev(dev); \
- return input_print_bitmap(buf, PAGE_SIZE, input_dev->bm##bit, ev##_MAX);\
-} \
-static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL);
-
-INPUT_DEV_CAP_ATTR(EV, ev);
-INPUT_DEV_CAP_ATTR(KEY, key);
-INPUT_DEV_CAP_ATTR(REL, rel);
-INPUT_DEV_CAP_ATTR(ABS, abs);
-INPUT_DEV_CAP_ATTR(MSC, msc);
-INPUT_DEV_CAP_ATTR(LED, led);
-INPUT_DEV_CAP_ATTR(SND, snd);
-INPUT_DEV_CAP_ATTR(FF, ff);
-INPUT_DEV_CAP_ATTR(SW, sw);
-
-static struct attribute *input_dev_caps_attrs[] = {
- &class_device_attr_ev.attr,
- &class_device_attr_key.attr,
- &class_device_attr_rel.attr,
- &class_device_attr_abs.attr,
- &class_device_attr_msc.attr,
- &class_device_attr_led.attr,
- &class_device_attr_snd.attr,
- &class_device_attr_ff.attr,
- &class_device_attr_sw.attr,
- NULL
-};
-
-static struct attribute_group input_dev_caps_attr_group = {
- .name = "capabilities",
- .attrs = input_dev_caps_attrs,
-};
-
-static void input_dev_release(struct class_device *class_dev)
-{
- struct input_dev *dev = to_input_dev(class_dev);
-
- kfree(dev);
- module_put(THIS_MODULE);
-}
-
-/*
- * Input hotplugging interface - loading event handlers based on
- * device bitfields.
- */
-static int input_add_hotplug_bm_var(char **envp, int num_envp, int *cur_index,
- char *buffer, int buffer_size, int *cur_len,
- const char *name, unsigned long *bitmap, int max)
-{
- if (*cur_index >= num_envp - 1)
- return -ENOMEM;
-
- envp[*cur_index] = buffer + *cur_len;
-
- *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name);
- if (*cur_len > buffer_size)
- return -ENOMEM;
-
- *cur_len += input_print_bitmap(buffer + *cur_len,
- max(buffer_size - *cur_len, 0),
- bitmap, max) + 1;
- if (*cur_len > buffer_size)
- return -ENOMEM;
-
- (*cur_index)++;
- return 0;
-}
-
-#define INPUT_ADD_HOTPLUG_VAR(fmt, val...) \
- do { \
- int err = add_hotplug_env_var(envp, num_envp, &i, \
- buffer, buffer_size, &len, \
- fmt, val); \
- if (err) \
- return err; \
- } while (0)
-
-#define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max) \
- do { \
- int err = input_add_hotplug_bm_var(envp, num_envp, &i, \
- buffer, buffer_size, &len, \
- name, bm, max); \
- if (err) \
- return err; \
- } while (0)
-
-static int input_dev_hotplug(struct class_device *cdev, char **envp,
- int num_envp, char *buffer, int buffer_size)
-{
- struct input_dev *dev = to_input_dev(cdev);
- int i = 0;
- int len = 0;
-
- INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x",
- dev->id.bustype, dev->id.vendor,
- dev->id.product, dev->id.version);
- if (dev->name)
- INPUT_ADD_HOTPLUG_VAR("NAME=\"%s\"", dev->name);
- if (dev->phys)
- INPUT_ADD_HOTPLUG_VAR("PHYS=\"%s\"", dev->phys);
- if (dev->phys)
- INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq);
-
- INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX);
- if (test_bit(EV_KEY, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX);
- if (test_bit(EV_REL, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX);
- if (test_bit(EV_ABS, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX);
- if (test_bit(EV_MSC, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX);
- if (test_bit(EV_LED, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("LED=", dev->ledbit, LED_MAX);
- if (test_bit(EV_SND, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("SND=", dev->sndbit, SND_MAX);
- if (test_bit(EV_FF, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX);
- if (test_bit(EV_SW, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX);
-
- envp[i] = NULL;
-
- return 0;
-}
-
-struct class input_class = {
- .name = "input",
- .release = input_dev_release,
- .hotplug = input_dev_hotplug,
-};
-
-struct input_dev *input_allocate_device(void)
-{
- struct input_dev *dev;
-
- dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
- if (dev) {
- dev->dynalloc = 1;
- dev->cdev.class = &input_class;
- class_device_initialize(&dev->cdev);
- INIT_LIST_HEAD(&dev->h_list);
- INIT_LIST_HEAD(&dev->node);
- }
-
- return dev;
-}
-
-static void input_register_classdevice(struct input_dev *dev)
-{
- static atomic_t input_no = ATOMIC_INIT(0);
- const char *path;
-
- __module_get(THIS_MODULE);
-
- dev->dev = dev->cdev.dev;
-
- snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
- "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
-
- path = kobject_get_path(&dev->cdev.class->subsys.kset.kobj, GFP_KERNEL);
- printk(KERN_INFO "input: %s as %s/%s\n",
- dev->name ? dev->name : "Unspecified device",
- path ? path : "", dev->cdev.class_id);
- kfree(path);
-
- class_device_add(&dev->cdev);
- sysfs_create_group(&dev->cdev.kobj, &input_dev_group);
- sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
- sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
-}
-
-void input_register_device(struct input_dev *dev)
-{
- struct input_handle *handle;
- struct input_handler *handler;
- struct input_device_id *id;
-
- set_bit(EV_SYN, dev->evbit);
-
- init_MUTEX(&dev->sem);
-
- /*
- * If delay and period are pre-set by the driver, then autorepeating
- * is handled by the driver itself and we don't do it in input.c.
- */
-
- init_timer(&dev->timer);
- if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
- dev->timer.data = (long) dev;
- dev->timer.function = input_repeat_key;
- dev->rep[REP_DELAY] = 250;
- dev->rep[REP_PERIOD] = 33;
- }
-
- INIT_LIST_HEAD(&dev->h_list);
- list_add_tail(&dev->node, &input_dev_list);
-
- if (dev->dynalloc)
- input_register_classdevice(dev);
-
- list_for_each_entry(handler, &input_handler_list, node)
- if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
- if ((id = input_match_device(handler->id_table, dev)))
- if ((handle = handler->connect(handler, dev, id)))
- input_link_handle(handle);
-
-
- input_wakeup_procfs_readers();
-}
-
-void input_unregister_device(struct input_dev *dev)
-{
- struct list_head * node, * next;
-
- if (!dev) return;
-
- del_timer_sync(&dev->timer);
-
- list_for_each_safe(node, next, &dev->h_list) {
- struct input_handle * handle = to_handle(node);
- list_del_init(&handle->d_node);
- list_del_init(&handle->h_node);
- handle->handler->disconnect(handle);
- }
-
- list_del_init(&dev->node);
-
- if (dev->dynalloc) {
- sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
- sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
- class_device_unregister(&dev->cdev);
- }
-
- input_wakeup_procfs_readers();
-}
-
-void input_register_handler(struct input_handler *handler)
-{
- struct input_dev *dev;
- struct input_handle *handle;
- struct input_device_id *id;
-
- if (!handler) return;
-
- INIT_LIST_HEAD(&handler->h_list);
-
- if (handler->fops != NULL)
- input_table[handler->minor >> 5] = handler;
-
- list_add_tail(&handler->node, &input_handler_list);
-
- list_for_each_entry(dev, &input_dev_list, node)
- if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
- if ((id = input_match_device(handler->id_table, dev)))
- if ((handle = handler->connect(handler, dev, id)))
- input_link_handle(handle);
-
- input_wakeup_procfs_readers();
-}
-
-void input_unregister_handler(struct input_handler *handler)
-{
- struct list_head * node, * next;
-
- list_for_each_safe(node, next, &handler->h_list) {
- struct input_handle * handle = to_handle_h(node);
- list_del_init(&handle->h_node);
- list_del_init(&handle->d_node);
- handler->disconnect(handle);
- }
-
- list_del_init(&handler->node);
-
- if (handler->fops != NULL)
- input_table[handler->minor >> 5] = NULL;
-
- input_wakeup_procfs_readers();
-}
-
-static int input_open_file(struct inode *inode, struct file *file)
-{
- struct input_handler *handler = input_table[iminor(inode) >> 5];
- struct file_operations *old_fops, *new_fops = NULL;
- int err;
-
- /* No load-on-demand here? */
- if (!handler || !(new_fops = fops_get(handler->fops)))
- return -ENODEV;
-
- /*
- * That's _really_ odd. Usually NULL ->open means "nothing special",
- * not "no device". Oh, well...
- */
- if (!new_fops->open) {
- fops_put(new_fops);
- return -ENODEV;
- }
- old_fops = file->f_op;
- file->f_op = new_fops;
-
- err = new_fops->open(inode, file);
-
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- fops_put(old_fops);
- return err;
-}
-
-static struct file_operations input_fops = {
- .owner = THIS_MODULE,
- .open = input_open_file,
-};
-
-static int __init input_init(void)
-{
- int err;
-
- err = class_register(&input_class);
- if (err) {
- printk(KERN_ERR "input: unable to register input_dev class\n");
- return err;
- }
-
- err = input_proc_init();
- if (err)
- goto fail1;
-
- err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
- if (err) {
- printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
- goto fail2;
- }
-
- return 0;
-
- fail2: input_proc_exit();
- fail1: class_unregister(&input_class);
- return err;
-}
-
-static void __exit input_exit(void)
-{
- input_proc_exit();
- unregister_chrdev(INPUT_MAJOR, "input");
- class_unregister(&input_class);
-}
-
-subsys_initcall(input_init);
-module_exit(input_exit);