触摸屏驱动1+1
触摸屏驱动解析
by good02xaut
开发触摸屏驱动,最好的范例莫过于mc68328digi.c的实现。在没有看到原文之前,我把其中用到的结构解析一下。
1。struct ts_pen_info
该结构是触摸屏的核心数据结构。用户程序和驱动程序的交互就是通过该数据结构完成的。结构体里面的x,y坐标和状态是gui中事件驱动的原始数据源。
2。环形队列
gui程序通过read完成对ts_pen_info 的提取。而在内核中维护了一个环形队列,只要队列不为空,将立即返回数据给应用程序。
3。中断驱动机制
触摸屏是输入设备,因此使用的是中断驱动机制。只要有触摸事件发生,即向环形队列里面填充一项。
4。定时器的必要性
触摸屏的中断处理函数必然启动一个定时器。定时器的使用是为了检测出Drag操作。当按下触摸屏一直没有松开时,中断只会相应一次。这和触发方式关系不大,不是沿触发和电平触发的问题。主要是触摸屏的中断处理函数没有处理到松开是不会开放中断的。在这段时间内,就是通过定时器不停的启动,检测触摸屏的新坐标的。
5。驱动
触摸屏采用结构的驱动。
这5点是触摸屏驱动的核心,理解这些,阅读和编写自己的触摸屏驱动都会游刃有余。后面给出程序的源码,供有兴趣的朋友研究:)
mc68328digi.c解析1
文件说明部分,给出了硬件的接线方式
/*----------------------------------------------------------------------------*/
/* mc68328digi.c,v 1.2 /02/12 11:14:10 pney Exp
*
* linux/drivers/char/mc68328digi.c - Touch screen driver.
*
* Author: Philippe Ney
* (C) SMARTDATA <
*
* This program is free ; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Thanks to:
* Kenneth Albanowski for is first work on a touch screen driver.
* Alessandro Rubini for is "Linux drivers" book.
* Ori Pomerantz for is "Linux Kernel " guide.
*
* Updates:
* -03-07 Pascal bauermeister
* - Adapted to work ok on xcopilot; yet untested on real Palm.
* - Added check for version in ioctl()
*
* -03-21 Pascal bauermeister
* - bypass real hw uninits for xcopilot. Now xcopilot is
* happy. It even no longer generates pen irq while this
* irq is disabled (I'd like to understand why, so that
* I can do the same in mc68328digi_init() !).
*
* -10-23 Daniel Potts
* - for uCdimm/68VZ328. I use SPI 2 which maps to the
* same addresses as for 68EZ328 and requries less changes to
* the code. For CS I use PF_A22; CSD0/CAS0 is not made
* available off the uCdimm.
* PF1 (/IRQ5) is already used on uCdimm by the ethernet
* controller. We use /IRQ6 instead (PD7).
* - kill_fasync and request_irq fixes for 2.4.x () .
*
*
* : -EZ
* Burr-Brown Touch Screen Controller
* Rikei REW-A1 Touch Screen
*
* OS: uClinux version 2.0.38.1pre3
*
* Connectivity: Burr-Brown
* PENIRQ ----> PF1 & PD4 (with a )
* BUSY ----> PF5
* CS ----> PB4
* DIN ----> PE0
* DOUT ----> PE1
* DCLK ----> PE2
*
* : /PENIRQ ----+----[ ]---- EZ328: PD4
* |
* +----------------------- EZ328: /IRQ5
*
* States graph:
*
* ELAPSED & PEN_UP
* (re-init) +------+
* +------------------------>| IDLE |<------+
* | +------+ |
* | | | ELAPSED
* | +------------------------+ | (re-init)
* | | PEN_IRQ |
* | | (disable pen_irq) |
* | | (enable & set timer) |
* | | |
* | \/ |
* +------+
mc68328digi.c解析2
头文件和全局变量
#include
/* We're doing kernel work */
#include /* Specifically, a */
#include /* We want interrupts */
#include /* for _register() and _deregister() */
#include /* for struct 'file_operations' */
#include /* for timeout interrupts */
#include /* for HZ. HZ = 100 and the timer step is 1/100 */
#include /* for jiffies definition. jiffies is incremented
* once for each clock tick; thus it's incremented
* HZ times per secondes.*/
#include /* for verify_area */
#include /* for kmalloc */
#include
#include /* For IRQ_MACHSPEC */
/*----------------------------------------------------------------------------*/
#if defined(CONFIG_M68328)
/* These are missing in MC68328.h. I took them from MC68EZ328.h temporarily,
* but did not dare modifying MC68328.h yet (don't want to break things and
* have no time now to do regression testings)
* - Pascal Bauermeister
*/
#define ICR_ADDR 0xfffff302
#define ICR_POL5 0x0080 /* Polarity for IRQ5 */
#define IMR_MIRQ5 (1 << IRQ5_IRQ_NUM) /* Mask IRQ5 */
#define IRQ5_IRQ_NUM 20 /* IRQ5 */
#define PBPUEN BYTE_REF(PBPUEN_ADDR)
#define PBPUEN_ADDR 0xfffff40a /* B Pull-Up enable reg */
#define PB_CSD0_CAS0 0x10 /* Use CSD0/CAS0 as PB[4] */
#define PDSEL BYTE_REF(PDSEL_ADDR)
#define PDSEL_ADDR 0xfffff41b /* D Register */
#define PD_IRQ1 0x10 /* Use IRQ1 as PD[4] */
#define PF_A22 0x20 /* Use A22 as PF[5] */
#define PF_IRQ5 0x02 /* Use IRQ5 as PF[1] */
#define SPIMCONT WORD_REF(SPIMCONT_ADDR)
#define SPIMCONT_ADDR 0xfffff802
#define SPIMCONT_SPIMEN SPIMCONT_RSPIMEN
#elif defined(CONFIG_M68EZ328)
# include /* bus, and irq definition of EZ */
#elif defined(CONFIG_M68VZ328)
# include
#else
# error "mc68328digi: a known machine not defined."
#endif
#include
/*----------------------------------------------------------------------------*/
/* Debug */
#if 0
#if 0
# define DPRINTK printk("%0: [%03d,%03d] ", __file__, \
current_pos.x, current_pos.y); \
printk
#else
# define DPRINTK ____
static void ____(const * fmt, ...) {}
#endif
#endif
/*----------------------------------------------------------------------------*/
static const * __file__ = __FILE__;
/*----------------------------------------------------------------------------*//*
* Kernel compatibility.
* Kernel > 2.2.3, include/linux/version.h contain a macro for KERNEL_VERSION
*/
#include
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
/*
* Conditional compilation. LINUX_VERSION_CODE is the code of this version
* (as per KERNEL_VERSION)
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
#include /* for put_user */
#include /* for polling fnct */
#endif
/*
* time limit
* Used for a whole conversion (x & y) and to clock the Burr-Brown to fall the
* BUSY signal down (otherwise, pen cannot generate irq anymore).
* If this limit run out, an error is generated. In the first case, the driver
* could recover his normal state. In the second, the BUSY signal cannot be
* fallen down and the have to be reseted.
* This limit is defined as approximatively the double of the deglitch one.
*/
#define CONV_TIME_LIMIT 50 /* ms */
/*
* Length of the data structure
*/
#define DATA_LENGTH sizeof(struct ts_pen_info)
/*
* Size of the buffer for the event queue
*/
#define TS_BUF_SIZE 32*DATA_LENGTH
/*
* Minor for the touch screen . Major is 10 because of the
* type of the driver.
*/
#define MC68328DIGI_MINOR 9
/*
* SPIMCONT fields for this app (not defined in asm/MC68EZ328.h).
*/
#define SPIMCONT_DATARATE ( 7 <#define SPIMCONT_BITCOUNT (15<< 0) /* 16 bits transfert */
/*
* SPIMCONT used values.
*/
#define SPIMCONT_INIT (SPIMCONT_DATARATE | \
SPIMCONT_ENABLE | \
SPIMCONT_XCH *0 | \
SPIMCONT_IRQ *0 | \
SPIMCONT_IRQEN | \
SPIMCONT_
mc68328digi.c解析3
/*----------------------------------------------------------------------------*/
/* Init & release functions --------------------------------------------------*/
static void init_ts_state(void) {
DISABLE_SPIM_IRQ; /* Disable SPIM interrupt */
ts_drv_state = TS_DRV_IDLE;
state_counter = 0;
ENABLE_PEN_IRQ; /* Enable interrupt IRQ5 */
}
static void init_ts_pen_s(void) {
ts_pen.x = 0;ts_pen.y = 0;
ts_pen.dx = 0;ts_pen.dy = 0;
ts_pen.event = EV_PEN_UP;
ts_pen.state &= 0;ts_pen.state |= ST_PEN_UP;
ts_pen_prev.x = 0;ts_pen_prev.y = 0;
ts_pen_prev.dx = 0;ts_pen_prev.dy = 0;
ts_pen_prev.event = EV_PEN_UP;
ts_pen_prev.state &= 0;ts_pen_prev.state |= ST_PEN_UP;
new_pen_state = 0;
ev_counter = 0;
do_gettimeofday(&first_tick);
}
static void init_ts_timer(struct timer_list *timer) {
init_timer(timer);
timer->function = (void *)handle_timeout;
}
static void release_ts_timer(struct timer_list *timer) {
del_timer(timer);
}
/*
* Set default s for the params of the driver.
*/
static void init_ts_settings(void) {
current_params.version = MC68328DIGI_VERSION;
current_params.version_req = 0;
current_params.x_ratio_num = 1;
current_params.x_ratio_den = 1;
current_params.y_ratio_num = 1;
current_params.y_ratio_den = 1;
current_params.x_offset = 0;
current_params.y_offset = 0;
current_params.xy_swap = 0;
current_params.x_min = 0;
current_params.x_max = ;
current_params.y_min = 0;
current_params.y_max = ;
current_params.mv_thrs = 50;
current_params.follow_thrs = 5; /* to eliminate the conversion noise */
current_params.sample_ms = 20;
current_params.deglitch_on = 1;
current_params.event_queue_on = 1;
sample_ticks = TICKS(current_params.sample_ms);
}
static void init_ts_hardware(void) {
/* Set bit 2 of F for interrupt (IRQ5 for pen_irq) */
#if 0
PENIRQ_PUEN |= PEN_MASK; /* F as pull-up */
#endif
PENIRQ_DIR &= ~PEN_MASK; /* PF1 as input */
PENIRQ_SEL &= ~PEN_MASK; /* PF1 internal chip function is connected
*/
#if 0
/* Set polarity of IRQ5 as low (interrupt occure when signal is low) */
ICR &= ~ICR_PENPOL;
#endif
/* Init stuff for E. The 3 LSB are multiplexed with the SPIM */
PESEL &= ~PESEL_MASK;
/* Set bit 4 of B for the CARD_SELECT signal of as . */
#ifndef CONFIG_XCOPILOT_BUGS
CS_PUEN |= CS_MASK; /* B as pull-up */
CS_DIR |= CS_MASK; /* PB4 as */
CS_DATA |= CS_MASK; /* PB4 inactive (=high) */
CS_SEL |= CS_MASK; /* PB4 as I/O not dedicated */
#endif
/* Set bit 5 of F for the BUSY signal of as input */
PFPUEN |= BUSY_MASK; /* F as pull-up */
PFDIR &= ~BUSY_MASK; /* PF5 as input */
PFSEL |= BUSY_MASK; /* PF5 I/O function pin is connected */
#ifdef USE_PENIRQ_PULLUP
/* Set bit 4 of D for the pen irq pull-up of as . */
PDDIR |= PEN_MASK_2; /* PD4 as */
PDDATA |= PEN_MASK_2; /* PD4 up (pull-up) */
PDPUEN |= PEN_MASK_2; /* PD4 as pull-up */
#ifndef CONFIG_XCOPILOT_BUGS
PDSEL |= PEN_MASK_2; /* PD4 I/O function pin is connected */
#endif
#endif
}
/*
* Reset bits used in each register to their default .
*/
static void release_ts_hardware(void) {
/* Register default */
#ifndef CONFIG_XCOPILOT_BUGS
PESEL |= PESEL_MASK; /* PESEL 0xFF */
CS_DIR &= ~CS_MASK; /* PBDIR 0x00 */
CS_DATA &= ~CS_MASK; /* PBDATA 0x00 */
CS_PUEN |= CS_MASK; /* PBPUEN 0xFF */
CS_SEL |= CS_MASK; /* PBSEL 0xFF
mc68328digi.c解析4
/*----------------------------------------------------------------------------*/
/* flow functions ------------------------------------------------------------*/
/*
* Set timer irq to now + delay ticks.
*/
static void set_timer_irq(struct timer_list *timer,int delay) {
del_timer(timer);
timer->expires = jiffies + delay;
add_timer(timer);
}
/*
* Set and SPIM parameters for transfer.
*/
static void set_SPIM_transfert(void) {
/* drive I/O 1 and I/O 2 LOW to reverse bias the PENIRQ
* of the
*/
#ifdef USE_PENIRQ_PULLUP
PDDATA &= ~PEN_MASK_2; /* Pull down I/0 2 of PENIRQ */
#endif
PENIRQ_DIR |= PEN_MASK; /* I/O 1 of PENIRQ as */
PENIRQ_DATA &= ~PEN_MASK; /* Pull down I/O 2 of PENIRQ */
/* Initiate selection and parameters for using the Burr-Brown
* and the SPIM.
*/
CARD_SELECT; /* . */
SPIM_ENABLE; /* Enable SPIM */
SPIM_INIT; /* Set SPIM parameters */
}
/*
* Clock the Burr-Brown to fall the AD_BUSY.
* With a 'start' bit and PD1,PD0 = 00 to enable PENIRQ.
* Used for the first pen irq after booting. And when error occures during
* conversion that need an initialisation.
*/
static void fall_BUSY_enable_PENIRQ(void) {
SET_SPIMDATA(SPIMDATA_NOP);
SPIMCONT |= SPIMCONT_XCH; /* initiate exchange */
}
/*
* Release transfer settings.
*/
static void release_SPIM_transfert(void) {
SPIM_DISABLE;
CARD_DESELECT;
#ifdef USE_PENIRQ_PULLUP
PDDATA |= PEN_MASK_2; /* Pull up I/0 2 of PENIRQ */
#endif
PENIRQ_DIR &= ~PEN_MASK; /* I/O 1 of PENIRQ as input */
PENIRQ_DATA |= PEN_MASK; /* Pull down I/O 2 of PENIRQ */
}
/*
* Ask for an X conversion.
*/
static void ask_x_conv(void) {
SET_SPIMDATA(SPIMDATA_ASKX);
SPIMCONT |= SPIMCONT_XCH; /* initiate exchange */
}
/*
* Ask for an Y conversion.
*/
static void ask_y_conv(void) {
SET_SPIMDATA(SPIMDATA_ASKY);
SPIMCONT |= SPIMCONT_XCH; /* initiate exchange */
}
/*
* Get the X conversion. Re-enable the PENIRQ.
*/
static void read_x_conv(void) {
current_pos.x = (SPIMDATA & 0x7FF8) >> 3;
/*
* The first clock is used to fall the BUSY and the following 15 clks
* to transfert the 12 bits of the conversion, MSB first.
* The result will then be in the SPIM register in the bits 14 to 3.
*/
SET_SPIMDATA(SPIMDATA_NOP); /* nop with a start bit to re-enable */
SPIMCONT |= SPIMCONT_XCH; /* the pen irq. */
rescale_xpos(¤t_pos.x);
}
/*
* Get the Y result. Clock the Burr-Brown to fall the BUSY.
*/
static void read_y_conv(void) {
current_pos.y = (SPIMDATA & 0x7FF8) >> 3;
/* Same remark as upper. */
SET_SPIMDATA(SPIMDATA_NULL); /* No start bit to fall the BUSY without */
SPIMCONT |= SPIMCONT_XCH; /* initiating another BUSY... */
rescale_ypos(¤t_pos.y);
}
/*
* Get one from the queue buffer.
* AND the with 'TS_BUF_SIZE -1' to have the loopback
*/
static unsigned get__from_queue(void) {
unsigned int result;
result = queue->buf[queue->tail];
queue->tail = (queue->tail + 1) & (TS_BUF_SIZE - 1);
return result;
}
/*
* Write one event in the queue buffer.
* if there is place for an event = the cannot be just one event
* length after the queue.
*/
static void put_in_queue( *in, int len) {
unsigned = queue->;
unsigned max = (queue->tail - len) & (TS_BUF_SIZE - 1);
int i;
if( != max) /* There is place for the event */
for(i=0;i queue->buf[] = in[i];
++;
&= (TS_BUF_SIZE - 1);
}
//else printk("%0: Queue is full !!!!\n", __file__);
queue-> = ;
}
/*
* if queue is empty.
*/
static in int queue_empty(void) {
return queue-> == queue->tail;
}
/*
* if the delta move of the pen is enough big to say that this is a really
* move and not due to noise.
*/
static int is_moving(void) {
int threshold;
int dx, dy;
threshold=((ts_pen_prev.event & EV_PEN_MOVE) > 0 ?
current_params.follow_thrs : current_params.mv_thrs);
dx = current_pos.x-ts_pen_prev.x;
dy = current_pos.y-ts_pen_prev.y;
if(dx < 0) dx = -dx; /* abs() */
if(dy < 0) dy = -dy;
return (dx > threshold ? 1 :
(dy > threshold ? 1 : 0));
}
static void copy_info(void) {
ts_pen_prev.x = ts_pen.x;
ts_pen_prev.y = ts_pen.y;
ts_pen_prev.dx = ts_pen.dx;
ts_pen_prev.dy = ts_pen.dy;
ts_pen_prev.event = ts_pen.event;
ts_pen_prev.state = ts_pen.state;
ts_pen_prev.ev_no = ts_pen.ev_no;
ts_pen_prev.ev_time = ts_pen.ev_time;
}
unsigned set_ev_time(void) {
struct timeval now;
do_gettimeofday(&now);
return (now.tv_sec -first_tick.tv_sec )* +
(now.tv_usec-first_tick.tv_usec)/;
}
static void cause_event(int conv) {
(conv) {
case CONV_ERROR: /* error occure during conversion */
ts_pen.state &= 0; /* clear */
ts_pen.state |= ST_PEN_UP; /* recover a stable state for the drv. */
ts_pen.state |= ST_PEN_ERROR; /* tell that the state is due to an error */
ts_
mc68328digi.c解析5
中断函数,一个是触摸屏中断,另一个是timer
/*----------------------------------------------------------------------------*/
/* Interrupt functions -------------------------------------------------------*/
/*
* pen irq.
*/
static void handle_pen_irq(int irq, void *dev_id, struct pt_regs *regs) {
unsigned flags;
int bypass_initial_timer = 0;
/* if unwanted interrupts come, trash them */
if(!device_open)
return;
PENIRQ_DATA &= ~PEN_MASK;
#ifdef CONFIG_XCOPILOT_BUGS
if(IMR&IMR_MPEN) {
return;
}
#endif
save_flags(flags); /* disable interrupts */
cli();
(ts_drv_state) {
case TS_DRV_IDLE:
DISABLE_PEN_IRQ;
ts_drv_state++; /* update driver state */
if(current_params.deglitch_on)
set_timer_irq(&ts_wake_time,sample_ticks);
else
bypass_initial_timer = 1;
break;
default: /* Error */
/* PENIRQ is enable then just init the driver
* Its not necessary to pull down the busy signal
*/
init_ts_state();
break;
}
restore_flags(flags); /* Enable interrupts */
/* if deglitching is off, we haven't started the deglitching timeout
* so we do the inital sampling immediately:
*/
if(bypass_initial_timer)
handle_timeout();
}
/*
* timer irq.
*/
static void handle_timeout(void) {
unsigned flags;
/* if unwanted interrupts come, trash them */
if(!device_open)
return;
save_flags(flags); /* disable interrupts */
cli();
(ts_drv_state) {
case TS_DRV_IDLE: /* Error in the idle state of the driver */
printk("%s: Error in the idle state of the driver\n", __file__);
goto treat_error;
case TS_DRV_ASKX: /* Error in release SPIM */
printk("%s: Error in release SPIM\n", __file__);
goto treat_error;
case TS_DRV_ASKY: /* Error in ask Y coord */
printk("%s: Error in ask Y coord\n", __file__);
goto treat_error;
case TS_DRV_READX: /* Error in read X coord */
printk("%s: Error in read X coord\n", __file__);
goto treat_error;
case TS_DRV_READY: /* Error in read Y coord */
printk("%s: Error in read Y coord\n", __file__);
goto treat_error;
treat_error:
/* Force re-enable of PENIRQ because of an abnormal termination. Limit this
* initialization with a timer to define a more FATAL error ...
*/
set_timer_irq(&ts_wake_time,TICKS(CONV_TIME_LIMIT));
ts_drv_state = TS_DRV_ERROR;
CLEAR_SPIM_IRQ; /* Consume residual irq from spim */
ENABLE_SPIM_IRQ;
fall_BUSY_enable_PENIRQ();
break;
case TS_DRV_WAIT:
if(state_counter) {
cause_event(CONV_LOOP); /* after one loop */
}
if(IS_PEN_DOWN) {
if(state_counter < 2) state_counter++;
set_timer_irq(&ts_wake_time,TICKS(CONV_TIME_LIMIT));
set_SPIM_transfert();
ts_drv_state++;
CLEAR_SPIM_IRQ; /* Consume residual irq from spim */
ENABLE_SPIM_IRQ;
fall_BUSY_enable_PENIRQ();
}
else {
if(state_counter) {
#ifdef CONFIG_XCOPILOT_BUGS
/* on xcopilot, we read the last mouse position now, because we
* missed the moves, during which the position should have been
* read
*/
xcopilot_read_now();
#endif
cause_event(CONV_END);
}
init_ts_state();
}
break;
case TS_DRV_ERROR: /* Busy doesn't want to fall down -> reboot? */
panic(__FILE__": cannot fall pull BUSY signal\n");
default:
init_ts_state();
}
restore_flags(flags); /* Enable interrupts */
#ifdef CONFIG_XCOPILOT_BUGS
if (ts_drv_state==TS_DRV_ASKX) {
handle_spi_irq();
}
PENIRQ_DATA |= PEN_MASK;
#endif
}
/*
* spi irq.
*/
static void handle_spi_irq(void) {
unsigned flags;
/* if unwanted interrupts come, trash them */
if(!device_open)
return;
save_flags(flags); /* disable interrupts */
cli();
CLEAR_SPIM_IRQ; /* clear source of interrupt */
(ts_drv_state) {
case TS_DRV_ASKX:
#ifdef CONFIG_XCOPILOT_BUGS
/* for xcopilot we bypass all SPI interrupt-related stuff
* and read the pos immediately
*/
xcopilot_read_now();
ts_drv_state = TS_DRV_WAIT;
DISABLE_SPIM_IRQ;
release_SPIM_transfert();
set_timer_irq(&ts_wake_time,sample_ticks);
break;
#endif
if(IS_BUSY_ENDED) { /* If first BUSY down, then */
ask_x_conv(); /* go on with conversion */
ts_drv_state++;
}
else fall_BUSY_enable_PENIRQ(); /* else, re-loop */
break;
case TS_DRV_ASKY:
ask_y_conv();
ts_drv_state++;
mc68328digi.c解析6
fops的定义,这个不用解释了吧
/*----------------------------------------------------------------------------*/
/* file_operations functions -------------------------------------------------*/
/*
* Called whenever a attempts to read the it has already open.
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
static ssize_t ts_read (struct file *file,
*buff, /* the buffer to fill with data */
size_t len, /* the length of the buffer. */
loff_t *offset) { /* Our offset in the file */
#else
static int ts_read(struct inode *inode, struct file *file,
*buff, /* the buffer to fill with data */
int len) { /* the length of the buffer. */
/* NO WRITE beyond that !!! */
#endif
*p_buffer;
c;
int i;
int err;
while(!new_pen_state) { /* noting to read */
if(file->f_flags & O_NONBLOCK)
return -EAGAIN;
interruptible_sleep_on(&queue->proc_list);
#if (LINUX_VERSION_CODE >= 0x020100)
if(signal_pending(current))
#else
if(current->signal & ~current->blocked) /* a signal arrived */
#endif
return -ERESTARTSYS; /* tell the fs layer to handle it */
/* otherwise loop */
}
/* Force length to the one available */
len = DATA_LENGTH;
/* verify that the user space address to write is valid */
err = verify_area(VERIFY_WRITE,buff,len);
if(err) return err;
/*
* The events are anyway put in the queue.
* But the read could choose to get the last event or the next in queue.
* This decision is controlled through the ioctl function.
* When the read doesn't flush the queue, this last is always full.
*/
if(current_params.event_queue_on) {
i = len;
while((i>0) && !queue_empty()) {
c = get__from_queue();
put_user(c,buff++);
i--;
}
}
else {
p_buffer = ( *)&ts_pen;
for(i=len-1;i>=0;i--) put_user(p_buffer[i],buff+i);
/* for(i=0;i }
if(queue_empty() || !current_params.event_queue_on)
new_pen_state = 0; /* queue events fully consumned */
else
new_pen_state = 1; /* queue not empty */
if(len-i) {
file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* nfi */
return len-i;
}
return 0;
}
/*
* file operation seems to become poll since version 2.2 of the kernel
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
static unsigned int ts_poll(struct file *file, poll_table *table) {
poll_wait(file,&queue->proc_list,table);
if (new_pen_state)
return POLLIN | POLLRDNORM;
return 0;
}
#else
static int ts_(struct inode *inode, struct file *file,
int mode, _table *table) {
if(mode != SEL_IN)
return 0; /* the is readable */
if(new_pen_state)
return 1; /* there is some new stuff to read */
_wait(&queue->proc_list,table); /* wait for data */
return 0;
}
#endif
/*
* Called whenever a attempts to do an ioctl to the it has
* already open.
*/
static int ts_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, /* the command to the ioctl */
unsigned arg) { /* the parameter to it */
struct ts_drv_params new_params;
int err;
int i;
*p_in;
*p_out;
(cmd) {
case TS_PARAMS_GET: /* Get internal params. First check if user space area
* to write is valid.
*/
err = verify_area(VERIFY_WRITE,( *)arg,sizeof(current_params));
if(err) return err;
p_in = ( *)¤t_params;
p_out = ( *)arg;
for(i=0;i put_user(p_in[i],p_out+i);
return 0;
case TS_PARAMS_SET: /* Set internal params. First check if user space area
* to read is valid.
*/
err = verify_area(VERIFY_READ,( *)arg,sizeof(new_params));
if(err) {
return err;
}
/* ok */
p_in = ( *)&new_params;
p_out = ( *)arg;
for(i=0;i#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
get_user(p_in[i],p_out+i);
#else
p_in[i] = get_user(p_out+i);
#endif
}
/* All the params are set with minimal . */
memcpy((void*)&
mc68328digi.c解析7
驱动注册,其他文中有详细介绍。
/*
* structure for driver registration.
*/
static struct mc68328_digi = {
MC68328DIGI_MINOR,"mc68328 digitizer",&ts_fops
};
/*
* Initialize the driver.
*/
static int __init mc68328digi_init(void) {
int err;
printk("%s: MC68328DIGI touch screen driver\n", __file__);
/* Register the driver */
err = _register(&mc68328_digi);
if(err<0)
printk("%s: Error registering the \n", __file__);
else
printk("%s: register with : %s and : %d %d\n",
__file__, mc68328_digi., MC68328DIGI_MAJOR, mc68328_digi.minor);
/* Init prameters settings at boot time */
init_ts_settings();
return err; /* A non zero means that init_module failed */
}
/*
* Cleanup - undid whatever mc68328digi_init did.
*/
void mc68328digi_cleanup(void) {
/* Unregister driver */
_deregister(&mc68328_digi);
}
module_init(mc68328digi_init);
module_exit(mc68328digi_cleanup);
触摸屏驱动结束语
触摸屏驱动结束语
by good02xaut
触摸屏不是什么复杂的输入设备,它和键盘()的复杂度根本就不可同日而语。因此大家开发触摸屏驱动,是件十分轻松的事情。
能否写出一个高效的触摸屏驱动,取决于对前面5点的认识。不论你使用哪种触摸屏,也不管是否使用,总之,只要能够提供给gui合适的Pen信息就是好的。
顺便提一下GUI。目前minigui,qt都默认支持触摸屏作为输入设备,这和fb是一个道理。只有你的驱动是规范化的,才可以不加修改的应用到gui中。
本介绍只是抛砖引玉,满足一般的需要。真正编写,最好多参考其他的源码。
阅读(1234) | 评论(0) | 转发(0) |