Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
nvec_ps2.c
Go to the documentation of this file.
1 /*
2  * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller
3  *
4  * Copyright (C) 2011 The AC100 Kernel Team <[email protected]>
5  *
6  * Authors: Pierre-Hugues Husson <[email protected]>
7  * Ilya Petrov <[email protected]>
8  * Marc Dietrich <[email protected]>
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', '\x06'}
25 #define STOP_STREAMING {'\x06', '\x04'}
26 #define SEND_COMMAND {'\x06', '\x01', '\xf4', '\x01'}
27 
28 #ifdef NVEC_PS2_DEBUG
29 #define NVEC_PHD(str, buf, len) \
30  print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \
31  16, 1, buf, len, false)
32 #else
33 #define NVEC_PHD(str, buf, len)
34 #endif
35 
36 static const unsigned char MOUSE_RESET[] = {'\x06', '\x01', '\xff', '\x03'};
37 
38 struct nvec_ps2 {
39  struct serio *ser_dev;
41  struct nvec_chip *nvec;
42 };
43 
44 static struct nvec_ps2 ps2_dev;
45 
46 static int ps2_startstreaming(struct serio *ser_dev)
47 {
48  unsigned char buf[] = START_STREAMING;
49  return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
50 }
51 
52 static void ps2_stopstreaming(struct serio *ser_dev)
53 {
54  unsigned char buf[] = STOP_STREAMING;
55  nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
56 }
57 
58 static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd)
59 {
60  unsigned char buf[] = SEND_COMMAND;
61 
62  buf[2] = cmd & 0xff;
63 
64  dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd);
65  return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
66 }
67 
68 static int nvec_ps2_notifier(struct notifier_block *nb,
69  unsigned long event_type, void *data)
70 {
71  int i;
72  unsigned char *msg = (unsigned char *)data;
73 
74  switch (event_type) {
75  case NVEC_PS2_EVT:
76  for (i = 0; i < msg[1]; i++)
77  serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0);
78  NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]);
79  return NOTIFY_STOP;
80 
81  case NVEC_PS2:
82  if (msg[2] == 1) {
83  for (i = 0; i < (msg[1] - 2); i++)
84  serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0);
85  NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2);
86  }
87 
88  else if (msg[1] != 2) /* !ack */
89  NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2);
90  return NOTIFY_STOP;
91  }
92 
93  return NOTIFY_DONE;
94 }
95 
96 static int __devinit nvec_mouse_probe(struct platform_device *pdev)
97 {
98  struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
99  struct serio *ser_dev;
100 
101  ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL);
102  if (ser_dev == NULL)
103  return -ENOMEM;
104 
105  ser_dev->id.type = SERIO_PS_PSTHRU;
106  ser_dev->write = ps2_sendcommand;
107  ser_dev->start = ps2_startstreaming;
108  ser_dev->stop = ps2_stopstreaming;
109 
110  strlcpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
111  strlcpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
112 
113  ps2_dev.ser_dev = ser_dev;
114  ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
115  ps2_dev.nvec = nvec;
116  nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
117 
118  serio_register_port(ser_dev);
119 
120  /* mouse reset */
121  nvec_write_async(nvec, MOUSE_RESET, 4);
122 
123  return 0;
124 }
125 
126 static int __devexit nvec_mouse_remove(struct platform_device *pdev)
127 {
128  serio_unregister_port(ps2_dev.ser_dev);
129 
130  return 0;
131 }
132 
133 #ifdef CONFIG_PM_SLEEP
134 static int nvec_mouse_suspend(struct device *dev)
135 {
136  struct platform_device *pdev = to_platform_device(dev);
137  struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
138 
139  /* disable mouse */
140  nvec_write_async(nvec, "\x06\xf4", 2);
141 
142  /* send cancel autoreceive */
143  nvec_write_async(nvec, "\x06\x04", 2);
144 
145  return 0;
146 }
147 
148 static int nvec_mouse_resume(struct device *dev)
149 {
150  struct platform_device *pdev = to_platform_device(dev);
151  struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
152 
153  ps2_startstreaming(ps2_dev.ser_dev);
154 
155  /* enable mouse */
156  nvec_write_async(nvec, "\x06\xf5", 2);
157 
158  return 0;
159 }
160 #endif
161 
162 static const SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend,
163  nvec_mouse_resume);
164 
165 static struct platform_driver nvec_mouse_driver = {
166  .probe = nvec_mouse_probe,
167  .remove = __devexit_p(nvec_mouse_remove),
168  .driver = {
169  .name = "nvec-mouse",
170  .owner = THIS_MODULE,
171  .pm = &nvec_mouse_pm_ops,
172  },
173 };
174 
175 module_platform_driver(nvec_mouse_driver);
176 
177 MODULE_DESCRIPTION("NVEC mouse driver");
178 MODULE_AUTHOR("Marc Dietrich <[email protected]>");
179 MODULE_LICENSE("GPL");