ckfwq/linux-3.0.4/drivers/input/touchscreen/tsdev.c

373 lines
9.0 KiB
C
Executable File

/*
* GSC3280 SoC ADC(SPI0) Controller Driver
*
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
* Author: Davied, apple_guet@126.com
*
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/major.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#ifdef CONFIG_GSC3280_TS_DEV_DEBUG
#define DBG(msg...) do { \
printk(KERN_INFO msg); \
} while (0)
#else
#define DBG(msg...) do { } while(0)
#endif
#define TSDEV_MINOR_BASE 128
#define TSDEV_MINOR_MAX 32
/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
#define TSDEV_MINOR_MASK ((TSDEV_MINOR_MAX >> 1) - 1) //15
#define TSDEV_BUFFER_SIZE 64
struct ts_dev {
char name[8];
u8 exist;
u8 minor;
int open;
volatile short x;
volatile short y;
volatile short z;
struct device dev;
struct list_head list;
struct input_handle handle;
};
struct ts_event {
short z;
short x;
short y;
short msec;
};
struct tsdev_client {
volatile u8 head;
volatile u8 tail;
spinlock_t lock;
struct ts_dev *tsdev;
struct list_head node;
struct fasync_struct *fasync;
struct ts_event event[TSDEV_BUFFER_SIZE];
};
#ifndef CONFIG_INPUT_TSDEV_SCREEN_X
#define CONFIG_INPUT_TSDEV_SCREEN_X 800
#endif
#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
#define CONFIG_INPUT_TSDEV_SCREEN_Y 480
#endif
static const int gXres = CONFIG_INPUT_TSDEV_SCREEN_X;
static const int gYres = CONFIG_INPUT_TSDEV_SCREEN_Y;
static struct ts_dev *tsdev_table[TSDEV_MINOR_MAX >> 1];
static DECLARE_WAIT_QUEUE_HEAD(ts_wait_queue);
static int tsdev_fasync(int fd, struct file *filp, int on)
{
int ret = 0;
struct tsdev_client *client = filp->private_data;
ret = fasync_helper(fd, filp, on, &client->fasync);
return ret < 0 ? ret : 0;
}
static int tsdev_open(struct inode *inode, struct file *filp)
{
struct tsdev_client *client = NULL;
int ret = 0, i = iminor(inode) - TSDEV_MINOR_BASE;
if ((i >= TSDEV_MINOR_MAX) || (!tsdev_table[i & TSDEV_MINOR_MASK])) {
DBG("!!!!tsdev minor error!\n");
return -ENODEV;
}
client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL);
if (!client) {
DBG("!!!!kzalloc error!\n");
return -ENOMEM;
}
i &= TSDEV_MINOR_MASK;
client->tsdev = tsdev_table[i];
list_add_tail(&client->node, &client->tsdev->list);
client->head = client->tail = 0;
spin_lock_init(&client->lock);
filp->private_data = client;
if (!client->tsdev->open++)
if (client->tsdev->exist)
ret = input_open_device(&client->tsdev->handle);
return ret;
}
static void tsdev_free(struct device *dev)
{
struct ts_dev *ts = container_of(dev, struct ts_dev, dev);
input_put_device(ts->handle.dev);
tsdev_table[ts->minor] = NULL;
kfree(ts);
}
static int tsdev_release(struct inode *inode, struct file *filp)
{
struct tsdev_client *client = filp->private_data;
tsdev_fasync(-1, filp, 0);
list_del(&client->node);
if (!--client->tsdev->open) {
if (client->tsdev->exist)
input_close_device(&client->tsdev->handle);
else
tsdev_free(&(client->tsdev->dev));
}
kfree(client);
return 0;
}
static ssize_t tsdev_read(struct file *filp, char __user *buffer, size_t count, loff_t * ppos)
{
int ret = 0;
unsigned long flags = 0;
struct tsdev_client *client = filp->private_data;
if ((client->head == client->tail) && (filp->f_flags & O_NONBLOCK) ) {
DBG("no data or O_NONBLOCK error!!!!\n");
return -EAGAIN;
}
ret = wait_event_interruptible(ts_wait_queue, (client->head != client->tail));
if (ret)
return ret;
spin_lock_irqsave(&client->lock, flags);
while ((client->head != client->tail) && (ret + sizeof (struct ts_event) <= count)) {
if (copy_to_user (buffer + ret, client->event + client->tail, sizeof (struct ts_event))) {
spin_unlock_irqrestore(&client->lock, flags);
return ret;
}
client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
ret += sizeof (struct ts_event);
}
spin_unlock_irqrestore(&client->lock, flags);
return ret;
}
/* No kernel lock - fine */
static unsigned int tsdev_poll(struct file *filp, poll_table * wait)
{
unsigned long flags = 0;
struct tsdev_client *client = filp->private_data;
poll_wait(filp, &ts_wait_queue, wait);
spin_lock_irqsave(&client->lock, flags);
if (client->head != client->tail) {
spin_unlock_irqrestore(&client->lock, flags);
return POLLIN | POLLRDNORM;
}
spin_unlock_irqrestore(&client->lock, flags);
return 0;
}
struct file_operations tsdev_fops = {
.owner = THIS_MODULE,
.open = tsdev_open,
.release = tsdev_release,
.read = tsdev_read,
.poll = tsdev_poll,
.fasync = tsdev_fasync,
};
static void tsdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
unsigned long flags = 0;
struct tsdev_client *client = NULL;
struct ts_dev *ts = handle->private;
switch (type) {
case EV_ABS:
switch (code) {
case ABS_X:
ts->x = value;
break;
case ABS_Y:
ts->y = value;
break;
case ABS_PRESSURE:
ts->z = value;
break;
}
break;
case EV_REL:
switch (code) {
case REL_X:
ts->x += value;
if (ts->x < 0)
ts->x = 0;
else if (ts->x > gXres)
ts->x = gXres;
break;
case REL_Y:
ts->y += value;
if (ts->y < 0)
ts->y = 0;
else if (ts->y > gYres)
ts->y = gYres;
break;
}
break;
case EV_KEY:
if ((code == BTN_TOUCH) || (code == BTN_MOUSE)) {
switch (value) {
case 0:
ts->z = 0;
break;
case 1:
ts->z = 1;
break;
}
}
break;
}
if (type != EV_SYN)
return;
list_for_each_entry(client, &ts->list, node) {
if (client) {
spin_lock_irqsave(&client->lock, flags);
client->event[client->head].z = ts->z;
client->event[client->head].x = ts->x;
client->event[client->head].y = ts->y;
client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
spin_unlock_irqrestore(&client->lock, flags);
}
}
wake_up_interruptible(&ts_wait_queue);
}
static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
int minor = 0, ret = 0;
struct ts_dev *ts = NULL;
for (minor = 0; ((minor < (TSDEV_MINOR_MAX >> 1)) && tsdev_table[minor]); minor++) {
;
}
if (minor >= (TSDEV_MINOR_MAX >> 1)) {
DBG("!!!!You have too many touchscreens!\n");
return -EBUSY;
}
ts = kzalloc(sizeof(struct ts_dev), GFP_KERNEL);
if (!ts) {
DBG("!!!!kmalloc error!\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&ts->list);
dev_set_name(&ts->dev, "ts%d", minor);
ts->exist = 1;
ts->minor = minor;
ts->handle.dev = input_get_device(dev);
ts->handle.name = dev_name(&ts->dev);;
ts->handle.handler = handler;
ts->handle.private = ts;
ts->dev.class = &input_class;
if (dev) {
ts->dev.parent = &dev->dev;
}
ts->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
ts->dev.release = tsdev_free;
device_initialize(&ts->dev);
ret = input_register_handle(&ts->handle);
if (ret) {
DBG("!!!!register handler tsdev error!\n");
return -EFAULT;
}
tsdev_table[minor] = ts;
ret = device_add(&ts->dev);
if (ret) {
DBG("!!!!add tsdev class error!\n");
return -EFAULT;
}
return 0;
}
static void tsdev_disconnect(struct input_handle *handle)
{
struct ts_dev *ts = handle->private;
ts->exist = 0;
device_del(&ts->dev);
if (ts->minor != (TSDEV_MINOR_MAX >> 1))
input_unregister_handle(&ts->handle);
put_device(&ts->dev);
}
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_MASK(EV_KEY) | BIT_MASK(EV_REL) },
.keybit = {[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT)},
.relbit = {BIT_MASK(REL_X) | BIT_MASK(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_MASK(EV_KEY) | BIT_MASK(EV_ABS)},
.keybit = {[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH)},
.absbit = {BIT_MASK(ABS_X) | BIT_MASK(ABS_Y)},
},/* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = {BIT_MASK(EV_ABS)},
.absbit = {BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE)},
},/* A tablet like device with several gradations of pressure */
{},/* 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)
{
DBG("####################\n");
input_register_handler(&tsdev_handler);
DBG("gXres = %d, gYres = %d\n", gXres, gYres);
printk(KERN_INFO "gsc3280 ts dev init\n");
DBG("####################\n");
return 0;
}
static void __exit tsdev_exit(void)
{
input_unregister_handler(&tsdev_handler);
}
module_init(tsdev_init);
module_exit(tsdev_exit);
MODULE_AUTHOR("Davied <apple_guet@126.com>");
MODULE_DESCRIPTION("Input driver to loongson gsc3280 touchscreen");
MODULE_LICENSE("GPL");