- 论坛徽章:
- 0
|
和原来的触摸屏驱动区别不是很大,增加了report函数来将事件发送到应用层。
驱动结构:
很简单的字符设备+平台设备驱动,总的结构来说,主要四个部分构成:
probe
remove
resume
suspend
工作机制则是注册设备,然后发生ts按下事件后产生ts中断以及adc中断,获得按下坐标。
没有读写函数,重点就是在两个中断处理函数上。
1,平台设备架构部分分析:
probe函数:
流程:ts基址的重映射->获得并启动时钟->ADCCON、ADCDLY、ADCTSC的初始化->初始化input设备完善ts结构体->建立ts_filter_chain->申请中断->注册input设备(2.6.27后为event0不再是ts0)。
static int __init s3c2410ts_probe(struct platform_device *pdev)
{
int rc;
struct s3c2410_ts_mach_info *info;
struct input_dev *input_dev;
int ret = 0;
dev_info(&pdev->dev, "Starting\n");
info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data;//获得平台设备数据
if (!info)
{
dev_err(&pdev->dev, "Hm... too bad: no platform data for ts\n");
return -EINVAL;
}
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL "Entering s3c2410ts_init\n");
#endif
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
dev_err(&pdev->dev, "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL "got and enabled clock\n");
#endif
base_addr = ioremap(S3C2410_PA_ADC,0x20);//将PA_ADC寄存器重映射到内存上
if (base_addr == NULL) {
dev_err(&pdev->dev, "Failed to remap register block\n");
ret = -ENOMEM;
goto bail0;
}
/* If we acutally are a S3C2410: Configure GPIOs */
if (!strcmp(pdev->name, "s3c2410-ts"))
s3c2410_ts_connect();//初始化相关gpio口
if ((info->presc & 0xff) > 0)
writel(S3C2410_ADCCON_PRSCEN |
S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
base_addr + S3C2410_ADCCON);
else
writel(0, base_addr+S3C2410_ADCCON);
/* Initialise registers */
if ((info->delay & 0xffff) > 0)
writel(info->delay & 0xffff, base_addr + S3C2410_ADCDLY);
writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
/* Initialise input stuff */
memset(&ts, 0, sizeof(struct s3c2410ts));
input_dev = input_allocate_device();
if (!input_dev) {
dev_err(&pdev->dev, "Unable to allocate the input device\n");
ret = -ENOMEM;
goto bail1;
}
//初始化input设备
ts.dev = input_dev;
ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |
BIT_MASK(EV_ABS);
ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
ts.dev->name = s3c2410ts_name;
ts.dev->id.bustype = BUS_RS232;
ts.dev->id.vendor = 0xDEAD;
ts.dev->id.product = 0xBEEF;
ts.dev->id.version = S3C2410TSVERSION;
ts.state = TS_STATE_STANDBY;//设置ts状态为就绪
ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL);//为event队列申请内存空间
if (IS_ERR(ts.event_fifo)) {
ret = -EIO;
goto bail2;
}
/* create the filter chain set up for the 2 coordinates we produce */
ts.chain = ts_filter_chain_create(pdev, info->filter_config, 2);//针对android的,建立filter_chain
if (IS_ERR(ts.chain))
goto bail2;
ts_filter_chain_clear(ts.chain);
/* Get irqs */
if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n");
iounmap(base_addr);
ret = -EIO;
goto bail3;
}
if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
free_irq(IRQ_ADC, ts.dev);
iounmap(base_addr);
ret = -EIO;
goto bail4;
}
dev_info(&pdev->dev, "Successfully loaded\n");
/* All went ok, so register to the input system */
rc = input_register_device(ts.dev);
if (rc) {
ret = -EIO;
goto bail5;
}
return 0;
bail5:
free_irq(IRQ_TC, ts.dev);
free_irq(IRQ_ADC, ts.dev);
clk_disable(adc_clock);
iounmap(base_addr);
disable_irq(IRQ_TC);
bail4:
disable_irq(IRQ_ADC);
bail3:
ts_filter_chain_destroy(ts.chain);
kfifo_free(ts.event_fifo);
bail2:
input_unregister_device(ts.dev);
bail1:
iounmap(base_addr);
bail0:
return ret;
}
remove:
就是probe的逆运算,
static int s3c2410ts_remove(struct platform_device *pdev)
{
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
free_irq(IRQ_TC,ts.dev);
free_irq(IRQ_ADC,ts.dev);
if (adc_clock) {
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
input_unregister_device(ts.dev);
iounmap(base_addr);
ts_filter_chain_destroy(ts.chain);
kfifo_free(ts.event_fifo);
return 0;
}
resume与suspend函数可有可无,完成触摸屏的激活和挂起,
2,中断处理分析:
三种模式转换过程:等待down中断模式->x,y连续坐标转换模式->等待up中断模式->等待down中断模式->..
两个中断的发生:触摸屏按下,发生ts中断,开始ad转换,ad转换结束,发生adc中断。
触摸屏抬起,发生ts中断。
ts中断处理函数:
//判断ts中断是up还是down
static irqreturn_t stylus_updown(int irq, void *dev_id)
{
unsigned long data0;
unsigned long data1;
int event_type;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
ts.is_down = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
(!(data1 & S3C2410_ADCDAT0_UPDOWN));
event_type = ts.is_down ? 'D' : 'U';
if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)&event_type,
sizeof(int)) != sizeof(int))) /* should not happen */
printk(KERN_ERR __FILE__": stylus_updown lost event!\n");
if (ts.is_down)
s3c2410_ts_start_adc_conversion();//down
else
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);//up
mod_timer(&event_send_timer, jiffies + 1);//延时
return IRQ_HANDLED;
}
adc中断处理函数:
static irqreturn_t stylus_action(int irq, void *dev_id)
{//新加入进来的机制,通过filter来处理adc转换得到的数据,并用过fifo来存储
int buf[3];
/* Grab the ADC results. */
buf[1] = readl(base_addr + S3C2410_ADCDAT0) &
S3C2410_ADCDAT0_XPDATA_MASK;
buf[2] = readl(base_addr + S3C2410_ADCDAT1) &
S3C2410_ADCDAT1_YPDATA_MASK;
switch (ts_filter_chain_feed(ts.chain, &buf[1])) {
case 0:
/* The filter wants more points. */
s3c2410_ts_start_adc_conversion();
return IRQ_HANDLED;
case 1:
/* We have a point from the filters or no filtering enabled. */
buf[0] = 'P';
break;
default:
printk(KERN_ERR __FILE__
":%d Invalid ts_filter_chain_feed return value.\n",
__LINE__);
case -1:
/* Error. Ignore the event. */
ts_filter_chain_clear(ts.chain);
writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
return IRQ_HANDLED;
};
if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf,
sizeof(int) * 3) != sizeof(int) * 3))
printk(KERN_ERR __FILE__":stylus_action bug.\n");
writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
mod_timer(&event_send_timer, jiffies + 1);
return IRQ_HANDLED;
}
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/97285/showart_1967792.html |
|