📄 ads7843.c
字号:
/* * linux/drivers/misc/ads7843-ts.c * * Copyright (C) 2001 Russell King, All Rights Reserved. * * 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. * * 21-Jan-2002 <jco@ict.es> : * * Added support for synchronous A/D mode. This mode is useful to * avoid noise induced in the touchpanel by the LCD, provided that * the ads7843 has a valid LCD sync signal routed to its ADCSYNC pin. * It is important to note that the signal connected to the ADCSYNC * pin should provide pulses even when the LCD is blanked, otherwise * a pen touch needed to unblank the LCD will never be read. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/sched.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/pm.h>#include <linux/miscdevice.h>#include <linux/poll.h>#include <asm/dma.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include "ads7843.h"/* * Hardware Pins */#define TS_CLK AT91C_PIO_PD1#define TS_CS AT91C_PIO_PD5#define TS_DIN AT91C_PIO_PD2#define TS_DOUT AT91C_PIO_PD3/* * This structure is nonsense - millisecs is not very useful * since the field size is too small. Also, we SHOULD NOT * be exposing jiffies to user space directly. */struct ts_event { u16 pressure; u16 x; u16 y; u16 pad; struct timeval stamp; };#define NR_EVENTS 16struct ads7843_ts { // int ads7843_fd; wait_queue_head_t irq_wait; struct semaphore sem; struct completion init_exit; struct task_struct *rtask; int use_count; struct fasync_struct *fasync; wait_queue_head_t read_wait; u8 evt_head; u8 evt_tail; struct ts_event events[NR_EVENTS]; int restart:1; };static struct ads7843_ts spi_ts;static int ads7843_ts_startup(struct ads7843_ts *ts);static void ads7843_ts_shutdown(struct ads7843_ts *ts);static void ads7843_ts_irq(int, int, void *);#define ads7843_ts_evt_pending(ts) ((volatile u8)(ts)->evt_head != (ts)->evt_tail)#define ads7843_ts_evt_get(ts) ((ts)->events + (ts)->evt_tail)#define ads7843_ts_evt_pull(ts) ((ts)->evt_tail = ((ts)->evt_tail + 1) & (NR_EVENTS - 1))#define ads7843_ts_evt_clear(ts) ((ts)->evt_head = (ts)->evt_tail = 0)static unsigned short ads7843_transfer(unsigned char cmd){ int i; unsigned char bit; unsigned short read_data = 0; /* CS# Low */ AT91_SYS->PIOD_CODR = TS_CS; udelay(10); /* Transform CMD */ for(i=1; i<=8; i++){ /* CMD Output */ bit = ( cmd >> (8-i) ) & 0x01 ; if( bit ) AT91_SYS->PIOD_SODR = TS_DIN; else AT91_SYS->PIOD_CODR = TS_DIN; udelay(5); /* CLK High */ AT91_SYS->PIOD_SODR = TS_CLK; udelay(10); /* CLK Low */ AT91_SYS->PIOD_CODR = TS_CLK; udelay(5); } /* Waiting for busy */ udelay(20); /* Read DATA */ for(i=7; i>=0; i--){ /* CLK High */ AT91_SYS->PIOD_SODR = TS_CLK; udelay(10); /* CLK Low */ AT91_SYS->PIOD_CODR = TS_CLK; /* Data in */ if( (AT91_SYS->PIOD_PDSR & TS_DOUT) != 0 ) read_data |= 1 << i; udelay(10); } /* CS# High */ AT91_SYS->PIOD_SODR = TS_CS; udelay(50); return read_data;}static inline void ads7843_ts_evt_add(struct ads7843_ts *ts, u16 pressure, u16 x, u16 y){ int next_head; next_head = (ts->evt_head + 1) & (NR_EVENTS - 1); if (next_head != ts->evt_tail) { ts->events[ts->evt_head].pressure = pressure; ts->events[ts->evt_head].x = x; ts->events[ts->evt_head].y = y; do_gettimeofday(&ts->events[ts->evt_head].stamp); ts->evt_head = next_head; if (ts->fasync) kill_fasync(&ts->fasync, SIGIO, POLL_IN); wake_up_interruptible(&ts->read_wait); }}static inline void ads7843_ts_event_release(struct ads7843_ts *ts){ ads7843_ts_evt_add(ts, 0, 0, 0);}/* * User space driver interface. */static ssize_tads7843_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos){ DECLARE_WAITQUEUE(wait, current); struct ads7843_ts *ts = filp->private_data; char *ptr = buffer; int err = 0; add_wait_queue(&ts->read_wait, &wait); while (count >= sizeof(struct ts_event)) { err = -ERESTARTSYS; if (signal_pending(current)) break; if (ads7843_ts_evt_pending(ts)) { struct ts_event *evt = ads7843_ts_evt_get(ts); err = copy_to_user(ptr, evt, sizeof(struct ts_event)); ads7843_ts_evt_pull(ts); if (err) break; ptr += sizeof(struct ts_event); count -= sizeof(struct ts_event); continue; } set_current_state(TASK_INTERRUPTIBLE); err = -EAGAIN; if (filp->f_flags & O_NONBLOCK) break; schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&ts->read_wait, &wait); return ptr == buffer ? err : ptr - buffer;}static unsigned int ads7843_ts_poll(struct file *filp, poll_table *wait){ struct ads7843_ts *ts = filp->private_data; int ret = 0; poll_wait(filp, &ts->read_wait, wait); if (ads7843_ts_evt_pending(ts)) ret = POLLIN | POLLRDNORM; return ret;}static int ads7843_ts_fasync(int fd, struct file *filp, int on){ struct ads7843_ts *ts = filp->private_data; return fasync_helper(fd, filp, on, &ts->fasync);}static int ads7843_ts_open(struct inode *inode, struct file *filp){ struct ads7843_ts *ts = &spi_ts; int ret = 0; ret = ads7843_ts_startup(ts); if (ret == 0) filp->private_data = ts; return ret;}/* * Release touchscreen resources. Disable IRQs. */static int ads7843_ts_release(struct inode *inode, struct file *filp){ struct ads7843_ts *ts = filp->private_data; down(&ts->sem); ads7843_ts_fasync(-1, filp, 0); ads7843_ts_shutdown(ts); up(&ts->sem); return 0;}static struct file_operations ads7843_fops = { owner: THIS_MODULE, read: ads7843_ts_read, poll: ads7843_ts_poll, open: ads7843_ts_open, release: ads7843_ts_release, fasync: ads7843_ts_fasync,};/* * The official ads7843 touchscreen is a miscdevice: * 10 char Non-serial mice, misc features * 14 = /dev/touchscreen/ads7843 touchscreen */static struct miscdevice ads7843_ts_dev = { minor: 14, name: "touchscreen", fops: &ads7843_fops,};static inline int ads7843_ts_register(struct ads7843_ts *ts){ init_waitqueue_head(&ts->read_wait); return misc_register(&ads7843_ts_dev);}static inline void ads7843_ts_deregister(struct ads7843_ts *ts){ misc_deregister(&ads7843_ts_dev);}/* * Switch to X position mode and measure Y plate. We switch the plate * configuration in pressure mode, then switch to position mode. This * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise. */static void ads7843_ts_read_pos(struct ads7843_ts *ts, struct ts_event *event){ int i; unsigned char cmd[2]; unsigned short data[2]; cmd[0] = MEASURE_8BIT_X; cmd[1] = MEASURE_8BIT_Y; for(i=0; i<2; i++){ data[i] = ads7843_transfer(cmd[i]); } event->x = data[0]; event->y = data[1]; event->pressure = 100; printk("read pos: x = %x, y = %x\n", event->x, event->y);}/* * This is a RT kernel thread that handles the ADC accesses. */static int ads7843_thread(void *ptr){ struct ads7843_ts *ts = ptr; struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); int valid; ts->rtask = tsk; daemonize(); reparent_to_init(); strcpy(tsk->comm, "ktsd"); tsk->tty = NULL; /* * We could run as a real-time thread. However, thus far * this doesn't seem to be necessary. */// tsk->policy = SCHED_FIFO;// tsk->rt_priority = 1; /* only want to receive SIGKILL */ spin_lock_irq(&tsk->sigmask_lock); siginitsetinv(&tsk->blocked, sigmask(SIGKILL)); recalc_sigpending(tsk); spin_unlock_irq(&tsk->sigmask_lock); complete(&ts->init_exit); valid = 0; add_wait_queue(&ts->irq_wait, &wait); for (;;) { struct ts_event event; unsigned int x, y, p; unsigned long val; signed long timeout; ts->restart = 0; ads7843_ts_read_pos(ts, &event); x = event.x; y = event.y; p = event.pressure; set_task_state(tsk, TASK_UNINTERRUPTIBLE); schedule_timeout(HZ / 100); if (signal_pending(tsk)) break; val = AT91_SYS->PIOD_PDSR; if (val & 0x01) { set_task_state(tsk, TASK_INTERRUPTIBLE); AT91_SYS->PIOD_IER = (1 << 0); /* * If we spat out a valid sample set last time, * spit out a "pen off" sample here. */ if (valid) { ads7843_ts_event_release(ts); valid = 0; } timeout = MAX_SCHEDULE_TIMEOUT; } else { /* * Filtering is policy. Policy belongs in user * space. We therefore leave it to user space * to do any filtering they please. */ if (!ts->restart) { ads7843_ts_evt_add(ts, p, x, y); valid = 1; } set_task_state(tsk, TASK_INTERRUPTIBLE); timeout = HZ / 100; } schedule_timeout(timeout); if (signal_pending(tsk)) break; } remove_wait_queue(&ts->irq_wait, &wait); ts->rtask = NULL; ads7843_ts_evt_clear(ts); complete_and_exit(&ts->init_exit, 0);}/* * We only detect touch screen _touches_ with this interrupt * handler, and even then we just schedule our task. */static void ads7843_ts_irq(int pio, int pin, void *id){ struct ads7843_ts *ts = id; /* Disable ts interrupt */ AT91_SYS->PIOD_IDR = (1 << 0); wake_up(&ts->irq_wait);}static int ads7843_ts_startup(struct ads7843_ts *ts){ int ret = 0; if (down_interruptible(&ts->sem)) return -EINTR; if (ts->use_count++ != 0) goto out; if (ts->rtask) panic("ads7843: rtask running?"); init_waitqueue_head(&ts->irq_wait); ret = at91rm9200_gpio_request_irq(AT91C_ID_PIOD, 0, 0, ads7843_ts_irq, ts); if (ret < 0) { goto out; } init_completion(&ts->init_exit); ret = kernel_thread(ads7843_thread, ts, 0); if (ret >= 0) { wait_for_completion(&ts->init_exit); ret = 0; up(&ts->sem); return ret; } else { at91rm9200_gpio_free_irq(AT91C_ID_PIOD, 0, ts); } out: printk("out\n"); if (ret) ts->use_count--; up(&ts->sem); return ret;}/* * Release touchscreen resources. Disable IRQs. */static void ads7843_ts_shutdown(struct ads7843_ts *ts){ if (--ts->use_count == 0) { if (ts->rtask) { send_sig(SIGKILL, ts->rtask, 1); wait_for_completion(&ts->init_exit); } at91rm9200_gpio_free_irq(AT91C_ID_PIOD, 0, ts); }}/* * Initialisation. */static int __init ads7843_ts_init(void){ struct ads7843_ts *ts = &spi_ts; printk("Driver for ADS7843 Load\n"); /* hardware init */ AT91_SYS->PIOD_PER = TS_CS | TS_CLK | TS_DIN | TS_DOUT; AT91_SYS->PIOD_OER = TS_CS | TS_CLK | TS_DIN; AT91_SYS->PIOD_ODR = TS_DOUT; AT91_SYS->PIOD_SODR = TS_CS; AT91_SYS->PIOD_CODR = TS_CLK | TS_DIN; AT91_SYS->PIOD_PER = AT91C_PIO_PD0 | AT91C_PIO_PD4; AT91_SYS->PIOD_ODR = AT91C_PIO_PD0 | AT91C_PIO_PD4; init_MUTEX(&ts->sem); return ads7843_ts_register(ts);}static void __exit ads7843_ts_exit(void){ struct ads7843_ts *ts = &spi_ts; ads7843_ts_deregister(ts);}module_init(ads7843_ts_init);module_exit(ads7843_ts_exit);MODULE_AUTHOR("Bears H <yellowyel@arm.linux.org.uk>");MODULE_DESCRIPTION("ads7843 touchscreen driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -