static int __init s3c_ts_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev;
struct input_dev *input_dev;
struct s3c_ts_mach_info * s3c_ts_cfg;
int ret, size;
dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev,"no memory resource specified\n");
return -ENOENT;
}
size = (res->end - res->start) + 1;
ts_mem = request_mem_region(res->start, size, pdev->name);
if (ts_mem == NULL) {
dev_err(dev, "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}
ts_base = ioremap(res->start, size);
if (ts_base == NULL) {
dev_err(dev, "failed to ioremap() region\n");
ret = -EINVAL;
goto err_map;
}
ts_clock = clk_get(&pdev->dev, "adc");
if (IS_ERR(ts_clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
ret = PTR_ERR(ts_clock);
goto err_clk;
}
clk_enable(ts_clock);
s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev);
if ((s3c_ts_cfg->presc&0xff) > 0)
writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF),\
ts_base+S3C_ADCCON);
else
writel(0, ts_base+S3C_ADCCON);
/* Initialise registers */
if ((s3c_ts_cfg->delay&0xffff) > 0)
writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);
if (s3c_ts_cfg->resol_bit==12) {
switch(s3c_ts_cfg->s3c_adc_con) {
case ADC_TYPE_2:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);
break;
case ADC_TYPE_1:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON);
break;
default:
dev_err(dev, "Touchscreen over this type of AP isn't supported !\n");
break;
}
}
writel(WAIT4INT(0), ts_base+S3C_ADCTSC);
ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);
input_dev = input_allocate_device();
if (!input_dev) {
ret = -ENOMEM;
goto err_alloc;
}
ts->dev = input_dev;
ts->dev->evbit[0] = 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);
if (s3c_ts_cfg->resol_bit==12) {
input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0);
}
else {
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);
sprintf(ts->phys, "input(ts)");
ts->dev->name = s3c_ts_name;
ts->dev->phys = ts->phys;
ts->dev->id.bustype = BUS_RS232;
ts->dev->id.vendor = 0xDEAD;
ts->dev->id.product = 0xBEEF;
ts->dev->id.version = S3C_TSVERSION;
ts->shift = s3c_ts_cfg->oversampling_shift;
ts->resol_bit = s3c_ts_cfg->resol_bit;
ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;
/* For IRQ_PENDUP */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (ts_irq == NULL) {
dev_err(dev, "no irq resource specified\n");
ret = -ENOENT;
goto err_irq;
}
ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);
if (ret != 0) {
dev_err(dev,"s3c_ts.c: Could not allocate ts IRQ_PENDN !\n");
ret = -EIO;
goto err_irq;
}
/* For IRQ_ADC */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (ts_irq == NULL) {
dev_err(dev, "no irq resource specified\n");
ret = -ENOENT;
goto err_irq;
}
ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts);
if (ret != 0) {
dev_err(dev, "s3c_ts.c: Could not allocate ts IRQ_ADC !\n");
ret = -EIO;
goto err_irq;
}
printk(KERN_INFO "%s got loaded successfully : %d bits\n", s3c_ts_name, s3c_ts_cfg->resol_bit);
/* All went ok, so register to the input system */
ret = input_register_device(ts->dev);
if(ret) {
dev_err(dev, "s3c_ts.c: Could not register input device(touchscreen)!\n");
ret = -EIO;
goto fail;
}
return 0;
fail:
free_irq(ts_irq->start, ts->dev);
free_irq(ts_irq->end, ts->dev);
err_irq:
input_free_device(input_dev);
kfree(ts);
err_alloc:
clk_disable(ts_clock);
clk_put(ts_clock);
err_clk:
iounmap(ts_base);
err_map:
release_resource(ts_mem);
kfree(ts_mem);
err_req:
return ret;
}
首先向说一下函数中的前几个变量:
1.第一个局部变量是struct resource类型的指针res变量,这个变量使用来描述设备的资源结构体(include\linux\ioport.h)
struct resource
{
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
其中的flags位表示的是资源的类型,start和end分别表示资源的起始地址和结束地址
2.device这里就不细讲了,大家知道它是对于设备的一个封装就行了
3.struct input_dev类型指针变量input_dev
input_dev 结构体成员注释:
struct input_dev — represents an input device//说明:下面出现的bitmap是指“位映像”
struct input_dev
{
const char * name; //设备名
const char * phys; //系统层次架构中设备的物理路径
const char * uniq; //设备的唯一标示码(如果设备有的话)
struct input_id id; //设备的id (struct input_id)
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //设备所支持的事件类型的bitmap (EV_KEY, EV_REL, etc.)
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //此设备所拥有的keys或者buttons的bitmap
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //此设备所拥有的相对轴的bitmap
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //此设别所拥有的绝对轴的bitmap
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; //设备所支持的杂项事件的bitmap
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; //设备上的led的bitmap
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; //设备所支持的声音效果的bitmap
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; //设备所支持的强制反馈效果的bitmap(force feedback)
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; //设备上的开关的bitmap
unsigned int keycodemax; //键码表的大小(keycode table)
unsigned int keycodesize; //键码表元素的大小
void * keycode; //此设备的扫描码到键码的映射
/*修改现有键映射的可选方法, 曾用来实现稀疏键映射. 若没有提供则使用缺省的 */
int (* setkeycode) (struct input_dev *dev, int scancode, int keycode);
/*获取当前键映射的可选方法. 若未提供则使用缺省的*/
int (* getkeycode) (struct input_dev *dev, int scancode, int *keycode); struct ff_device * ff;
//设备关联的强制反馈结构体(如果设备支持强制反馈效果的话)
unsigned int repeat_key; //存储上个按下的键的键码; 用于实现软件自动重复
struct timer_list timer; //软件自动重复的定时器
int sync; //自上一次EV_SYNC后再没有新的事件时设置为1
int abs[ABS_MAX + 1]; //要上报的绝对坐标下的当前值
int rep[REP_MAX + 1]; //自动重复参数的当前值 (delay, rate)
unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //反映设备的keys/buttons的当前状态
unsigned long led[BITS_TO_LONGS(LED_CNT)]; //反映设备LED的当前状态
unsigned long snd[BITS_TO_LONGS(SND_CNT)]; //反映声音效果的当前状态
unsigned long sw[BITS_TO_LONGS(SW_CNT)]; //反映设备开关的当前状态
int absmax[ABS_MAX + 1]; //来自绝对轴事件的最大值
int absmin[ABS_MAX + 1]; //来自绝对轴事件的最小值
int absfuzz[ABS_MAX + 1]; //描述轴的噪声
int absflat[ABS_MAX + 1]; //中央平坦位置的大小 (used by joydev)
/*这个方法在第一个用户调用input_event_device时调用。驱动必须让设备准备好开始产生事件 (开始轮询线程,请求一个IRQ,提交URB,等等) */
int (* open) (struct input_dev *dev);
void (* close) (struct input_dev *dev); //这个方法在最后一个用户调用input_close_device时被调用.
/*清空设备. 常用于清空失连的(disconnect)加载进设备的强制反馈效果 */
int (* flush) (struct input_dev *dev, struct file *file);
/*事件处理方法, 如 EV_LED 或者 EV_SND. 设备将执行一些要求的动作 (开启 LED, 放声音, 等等.) 此调用由event_lock保护并且不能够休眠 */ int (* event) (struct input_dev *dev, unsigned int type, unsigned int code, int value);
/*当前占有设备的输入句柄 (via EVIOCGRAB ioctl). 当设备对应一个句柄,这个句柄就是来自此设备所有输入事件的唯一接受者*/
struct input_handle * grab;/*当输入核接受处理设备的心事件时此spinlock锁定 (in input_event). */
spinlock_t event_lock; struct mutex mutex; //序列化对open、close、flush方法的调用
/* 存储打开此设备的用户数量(input handlers). 通过input_open_device和input_close_device来确保dev->open只能在被第一个用户打开设备时调用并且确保dev->close只能在最后一个用户关闭设备时被调用*/
unsigned int users;
int going_away; //标示设备属于未注册的一类并且会导致input_open_device*()返回-ENODEV错误.
struct device dev; //设备的驱动模型视图
struct list_head h_list; //和设备关联的输入句柄链表。当读取链表时需要锁定dev->mutex。
struct list_head node; //用来把设备放入input_dev_list
};
//AutoRepeat:用来配置对按住某键不放的处理,格式为:
//AutoRepeat 毫秒数 次数
//如:AutoRepeat 500 5,表示当按住某键500毫秒(0.5秒)后,开始自动送出该按键信号,每秒5次。
4.struct s3c_ts_mach_info指针变量s3c_ts_cfg(arch\arm\plat-s3c\include\plat\ts.h)
struct s3c_ts_mach_info
{
int delay;//延迟时间
int presc;//预分频值
int oversampling_shift;//转化次数
int resol_bit;//分辨率
enum s3c_adc_type s3c_adc_con;//adc类型,跟板子的型号有关,见下面
};
其中有
enum s3c_adc_type
{
ADC_TYPE_0,ADC_TYPE_1, /* S3C2416, S3C2450 */
ADC_TYPE_2,/* S3C64XX, S5PC1XX */
};
以上是几个局部变量的声明。
接下来用struct platform_device结构体参数的dev来初始化刚刚声明的dev变量
struct platform_device
{
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
然后初始化res变量对应的平台设备资源:在Dev-ts.c (linux2.6.28\arch\arm\plat-s3c)文件中
/* Touch srcreen */
static struct resource s3c_ts_resource[] =
{
[0] = {
.start = S3C_PA_ADC, I/O端口
.end = S3C_PA_ADC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_PENDN, 中断
.end = IRQ_PENDN,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_ADC, 中断
.end = IRQ_ADC,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_ts =
{
.name = "s3c-ts",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_ts_resource),
.resource = s3c_ts_resource,
};
再为驱动申请内存。ioremap是把物理地址重映射为虚拟地址,便于操作系统操作。
接着申请时钟,时钟使能。获取s3c6410 touchscreen machine infomation,s3c_ts_mach_info结构体(delay、presc、oversampling_shift、resol_bit)设置预分频接口寄存器、设置delay接口寄存器、设置resol_bit接口寄存器
writel(WAIT4INT(0), ts_base+S3C_ADCTSC);设置为等待中断模式
ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);//申请内存
input_dev = input_allocate_device();//申请input device
struct s3c_ts_info {
struct input_dev *dev;
long xp;
long yp;
int count;
int shift;
char phys[32];
int resol_bit;
enum s3c_adc_type s3c_adc_con;
};
这就是ts的结构体,结构体的第一个元素就是input_dev结构体接着初始化input device结构体、初始化ts结构体注册中断函数