Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tsc40.c
Go to the documentation of this file.
1 /*
2  * TSC-40 serial touchscreen driver. It should be compatible with
3  * TSC-10 and 25.
4  *
5  * Author: Sebastian Andrzej Siewior <[email protected]>
6  * License: GPLv2 as published by the FSF.
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
12 #include <linux/input.h>
13 #include <linux/serio.h>
14 #include <linux/init.h>
15 
16 #define PACKET_LENGTH 5
17 struct tsc_ser {
18  struct input_dev *dev;
19  struct serio *serio;
21  unsigned char data[PACKET_LENGTH];
22  char phys[32];
23 };
24 
25 static void tsc_process_data(struct tsc_ser *ptsc)
26 {
27  struct input_dev *dev = ptsc->dev;
28  u8 *data = ptsc->data;
29  u32 x;
30  u32 y;
31 
32  x = ((data[1] & 0x03) << 8) | data[2];
33  y = ((data[3] & 0x03) << 8) | data[4];
34 
35  input_report_abs(dev, ABS_X, x);
36  input_report_abs(dev, ABS_Y, y);
37  input_report_key(dev, BTN_TOUCH, 1);
38 
39  input_sync(dev);
40 }
41 
42 static irqreturn_t tsc_interrupt(struct serio *serio,
43  unsigned char data, unsigned int flags)
44 {
45  struct tsc_ser *ptsc = serio_get_drvdata(serio);
46  struct input_dev *dev = ptsc->dev;
47 
48  ptsc->data[ptsc->idx] = data;
49  switch (ptsc->idx++) {
50  case 0:
51  if (unlikely((data & 0x3e) != 0x10)) {
52  dev_dbg(&serio->dev,
53  "unsynchronized packet start (0x%02x)\n", data);
54  ptsc->idx = 0;
55  } else if (!(data & 0x01)) {
56  input_report_key(dev, BTN_TOUCH, 0);
57  input_sync(dev);
58  ptsc->idx = 0;
59  }
60  break;
61 
62  case 1:
63  case 3:
64  if (unlikely(data & 0xfc)) {
65  dev_dbg(&serio->dev,
66  "unsynchronized data 0x%02x at offset %d\n",
67  data, ptsc->idx - 1);
68  ptsc->idx = 0;
69  }
70  break;
71 
72  case 4:
73  tsc_process_data(ptsc);
74  ptsc->idx = 0;
75  break;
76  }
77 
78  return IRQ_HANDLED;
79 }
80 
81 static int tsc_connect(struct serio *serio, struct serio_driver *drv)
82 {
83  struct tsc_ser *ptsc;
84  struct input_dev *input_dev;
85  int error;
86 
87  ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL);
88  input_dev = input_allocate_device();
89  if (!ptsc || !input_dev) {
90  error = -ENOMEM;
91  goto fail1;
92  }
93 
94  ptsc->serio = serio;
95  ptsc->dev = input_dev;
96  snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys);
97 
98  input_dev->name = "TSC-10/25/40 Serial TouchScreen";
99  input_dev->phys = ptsc->phys;
100  input_dev->id.bustype = BUS_RS232;
101  input_dev->id.vendor = SERIO_TSC40;
102  input_dev->id.product = 40;
103  input_dev->id.version = 0x0001;
104  input_dev->dev.parent = &serio->dev;
105 
106  input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
107  __set_bit(BTN_TOUCH, input_dev->keybit);
108  input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0);
109  input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0);
110 
111  serio_set_drvdata(serio, ptsc);
112 
113  error = serio_open(serio, drv);
114  if (error)
115  goto fail2;
116 
117  error = input_register_device(ptsc->dev);
118  if (error)
119  goto fail3;
120 
121  return 0;
122 
123 fail3:
124  serio_close(serio);
125 fail2:
126  serio_set_drvdata(serio, NULL);
127 fail1:
128  input_free_device(input_dev);
129  kfree(ptsc);
130  return error;
131 }
132 
133 static void tsc_disconnect(struct serio *serio)
134 {
135  struct tsc_ser *ptsc = serio_get_drvdata(serio);
136 
137  serio_close(serio);
138 
139  input_unregister_device(ptsc->dev);
140  kfree(ptsc);
141 
142  serio_set_drvdata(serio, NULL);
143 }
144 
145 static struct serio_device_id tsc_serio_ids[] = {
146  {
147  .type = SERIO_RS232,
148  .proto = SERIO_TSC40,
149  .id = SERIO_ANY,
150  .extra = SERIO_ANY,
151  },
152  { 0 }
153 };
154 MODULE_DEVICE_TABLE(serio, tsc_serio_ids);
155 
156 #define DRIVER_DESC "TSC-10/25/40 serial touchscreen driver"
157 
158 static struct serio_driver tsc_drv = {
159  .driver = {
160  .name = "tsc40",
161  },
162  .description = DRIVER_DESC,
163  .id_table = tsc_serio_ids,
164  .interrupt = tsc_interrupt,
165  .connect = tsc_connect,
166  .disconnect = tsc_disconnect,
167 };
168 
169 module_serio_driver(tsc_drv);
170 
171 MODULE_AUTHOR("Sebastian Andrzej Siewior <[email protected]>");
173 MODULE_LICENSE("GPL v2");