1 /*
2  * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller
3  *
4  * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
5  *
6  * Authors:  Pierre-Hugues Husson <phhusson@free.fr>
7  *           Ilya Petrov <ilya.muromec@gmail.com>
8  *           Marc Dietrich <marvin24@gmx.de>
9  *
10  * This file is subject to the terms and conditions of the GNU General Public
11  * License.  See the file "COPYING" in the main directory of this archive
12  * for more details.
13  *
14  */
15 
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include <linux/serio.h>
19 #include <linux/delay.h>
20 #include <linux/platform_device.h>
21 
22 #include "nvec.h"
23 
24 #define START_STREAMING	{'\x06', '\x03', '\x04'}
25 #define STOP_STREAMING	{'\x06', '\x04'}
26 #define SEND_COMMAND	{'\x06', '\x01', '\xf4', '\x01'}
27 
28 static const unsigned char MOUSE_RESET[] = {'\x06', '\x01', '\xff', '\x03'};
29 
30 struct nvec_ps2 {
31 	struct serio *ser_dev;
32 	struct notifier_block notifier;
33 	struct nvec_chip *nvec;
34 };
35 
36 static struct nvec_ps2 ps2_dev;
37 
ps2_startstreaming(struct serio * ser_dev)38 static int ps2_startstreaming(struct serio *ser_dev)
39 {
40 	unsigned char buf[] = START_STREAMING;
41 	return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
42 }
43 
ps2_stopstreaming(struct serio * ser_dev)44 static void ps2_stopstreaming(struct serio *ser_dev)
45 {
46 	unsigned char buf[] = STOP_STREAMING;
47 	nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
48 }
49 
ps2_sendcommand(struct serio * ser_dev,unsigned char cmd)50 static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd)
51 {
52 	unsigned char buf[] = SEND_COMMAND;
53 
54 	buf[2] = cmd & 0xff;
55 
56 	dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd);
57 	return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
58 }
59 
nvec_ps2_notifier(struct notifier_block * nb,unsigned long event_type,void * data)60 static int nvec_ps2_notifier(struct notifier_block *nb,
61 			     unsigned long event_type, void *data)
62 {
63 	int i;
64 	unsigned char *msg = (unsigned char *)data;
65 
66 	switch (event_type) {
67 	case NVEC_PS2_EVT:
68 		for (i = 0; i < msg[1]; i++)
69 			serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0);
70 		return NOTIFY_STOP;
71 
72 	case NVEC_PS2:
73 		if (msg[2] == 1)
74 			for (i = 0; i < (msg[1] - 2); i++)
75 				serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0);
76 		else if (msg[1] != 2) {	/* !ack */
77 			print_hex_dump(KERN_WARNING, "unhandled mouse event: ",
78 				DUMP_PREFIX_NONE, 16, 1,
79 				msg, msg[1] + 2, true);
80 		}
81 
82 		return NOTIFY_STOP;
83 	}
84 
85 	return NOTIFY_DONE;
86 }
87 
nvec_mouse_probe(struct platform_device * pdev)88 static int __devinit nvec_mouse_probe(struct platform_device *pdev)
89 {
90 	struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
91 	struct serio *ser_dev = kzalloc(sizeof(struct serio), GFP_KERNEL);
92 
93 	ser_dev->id.type = SERIO_8042;
94 	ser_dev->write = ps2_sendcommand;
95 	ser_dev->open = ps2_startstreaming;
96 	ser_dev->close = ps2_stopstreaming;
97 
98 	strlcpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
99 	strlcpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
100 
101 	ps2_dev.ser_dev = ser_dev;
102 	ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
103 	ps2_dev.nvec = nvec;
104 	nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
105 
106 	serio_register_port(ser_dev);
107 
108 	/* mouse reset */
109 	nvec_write_async(nvec, MOUSE_RESET, 4);
110 
111 	return 0;
112 }
113 
114 static struct platform_driver nvec_mouse_driver = {
115 	.probe  = nvec_mouse_probe,
116 	.driver = {
117 		.name = "nvec-mouse",
118 		.owner = THIS_MODULE,
119 	},
120 };
121 
nvec_mouse_init(void)122 static int __init nvec_mouse_init(void)
123 {
124 	return platform_driver_register(&nvec_mouse_driver);
125 }
126 
127 module_init(nvec_mouse_init);
128 
129 MODULE_DESCRIPTION("NVEC mouse driver");
130 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
131 MODULE_LICENSE("GPL");
132