module_init(egalax_i2c_ts_init)–>表示驱动加载时首先执行的函数是egalax_i2c_ts_init,下面看egalax_i2c_ts_init函数源码:
static int egalax_i2c_ts_init(void)
{
int result;
result = misc_register(&egalax_misc_dev);
if(result)
{
EGALAX_DBG(DBG_MODULE, " misc device register failed\n");
goto fail;
}
p_char_dev = setup_chardev(); // allocate the character device
if(!p_char_dev)
{
result = -ENOMEM;
goto fail;
}
dbgProcFile = proc_create(PROC_FS_NAME, S_IRUGO|S_IWUGO, NULL, &egalax_proc_fops);
if (dbgProcFile == NULL)
{
remove_proc_entry(PROC_FS_NAME, NULL);
EGALAX_DBG(DBG_MODULE, " Could not initialize /proc/%s\n", PROC_FS_NAME);
}
EGALAX_DBG(DBG_MODULE, " Driver init done!\n");
return i2c_add_driver(&egalax_i2c_driver);
//通过i2c_add_driver这个函数向内核添加驱动,从而调
用register_driver向内核注册驱动,如果驱动和设备匹配上以后,
会调用peobe函数(前面有文章专门分析这个匹配过程,这篇文章主要
分probe函数做的工作);
fail:
egalax_i2c_ts_exit();
return result;
}
这里的egalax_i2c_driver为:
static struct i2c_driver egalax_i2c_driver = {
.driver = {
.name = "egalax_i2c",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = egalax_i2c_dt_ids,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
.pm = &egalax_i2c_pm_ops,
#endif
},
.class = I2C_CLASS_HWMON,
.id_table = egalax_i2c_idtable,
.probe = egalax_i2c_probe,
.remove = __devexit_p(egalax_i2c_remove),
.suspend = egalax_i2c_pm_suspend,
.resume = egalax_i2c_pm_resume,
.detect = ctp_detect,
.address_list = normal_i2c,
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = egalax_i2c_pm_suspend,
.resume = egalax_i2c_pm_resume,
#endif
#endif
};
看probe函数的源码:
static int __devinit egalax_i2c_probe(struct i2c_client *client, const struct i2c_device_id *idp)
{
int ret;
#ifdef CONFIG_OF//这里定义的CONFIG_OF是指是否使用了设备树,
这里对使用和没使用两种,都做了兼容;
struct device_node *devnode;
#endif //#ifdef CONFIG_OF
EGALAX_DBG(DBG_MODULE, " Start probe\n");
p_egalax_i2c_dev = (struct _egalax_i2c *)kzalloc(sizeof(struct _egalax_i2c), GFP_KERNEL);
//开辟结构体在和内核的内存空间
if (!p_egalax_i2c_dev)
{
EGALAX_DBG(DBG_MODULE, " Request memory failed\n");
ret = -ENOMEM;
goto fail1;
}
#ifdef CONFIG_OF
devnode = client->dev.of_node;
if(devnode) //if use the device tree config
{
//通过of_get_named_gpio函数来获取设备树的节点信息(获取设备树中名称为int-gpios的gpio number)
并将此gpio注册为中断,返回中断号
p_egalax_i2c_dev->interrupt_gpio = of_get_named_gpio(devnode, "int-gpios", 0);
client->irq = gpio_to_irq(p_egalax_i2c_dev->interrupt_gpio);
}
#else
//p_egalax_i2c_dev->interrupt_gpio = irq_to_gpio(client->irq);
p_egalax_i2c_dev->interrupt_gpio = 356;//未使用设备树时,可直接使用该gpio对应的gpio number
client->irq = gpio_to_irq(p_egalax_i2c_dev->interrupt_gpio);
#endif //#ifdef CONFIG_OF
if( !gpio_is_valid(p_egalax_i2c_dev->interrupt_gpio) )
//判断此gpio是否可用
{
ret = -ENODEV;
goto fail1;
}
ret = gpio_request(p_egalax_i2c_dev->interrupt_gpio, "Touch IRQ");
//如果可用,向内核说明该gpio此被占用
if(ret<0 && ret!=-EBUSY)
{
EGALAX_DBG(DBG_MODULE, " gpio_request[%d] failed: %d\n", p_egalax_i2c_dev->interrupt_gpio, ret);
goto fail1;
}
gpio_direction_input(p_egalax_i2c_dev->interrupt_gpio);
//该gpio作为中断脚,将该gpio设为输入
input_dev = allocate_Input_Dev();//这个函数比较重要,单独看源码讲解
if(input_dev==NULL)
{
EGALAX_DBG(DBG_MODULE, " allocate_Input_Dev failed\n");
ret = -EINVAL;
goto fail2;
}
EGALAX_DBG(DBG_MODULE, " Register input device done\n");
p_egalax_i2c_dev->client = client;
mutex_init(&p_egalax_i2c_dev->mutex_wq);
p_egalax_i2c_dev->ktouch_wq = create_singlethread_workqueue("egalax_touch_wq");
//建立一个工作队列
INIT_WORK(&p_egalax_i2c_dev->work_irq, egalax_i2c_wq_irq);
//将egalax_i2c_wq_irq函数用一个work_struct来描述(work_irq是一个work_struct结构体)
i2c_set_clientdata(client, p_egalax_i2c_dev);
if( gpio_get_value(p_egalax_i2c_dev->interrupt_gpio) )//驱动该gpio的引脚电平的高低
p_egalax_i2c_dev->skip_packet = 0;
else
p_egalax_i2c_dev->skip_packet = 1;
p_egalax_i2c_dev->work_state = MODE_WORKING;
ret = request_irq(client->irq, egalax_i2c_interrupt, IRQF_TRIGGER_LOW, client->name, p_egalax_i2c_dev);//向内核注册中断,中断处理函数为egalax_i2c_interrupt
下面会分析中断处理函数源码
if( ret )
{
EGALAX_DBG(DBG_MODULE, " Request irq(%d) failed\n", client->irq);
goto fail3;
}
EGALAX_DBG(DBG_MODULE, " Request irq(%d) gpio(%d) with result:%d\n", client->irq, p_egalax_i2c_dev->interrupt_gpio, ret);
#ifdef CONFIG_HAS_EARLYSUSPEND
egalax_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
egalax_early_suspend.suspend = egalax_i2c_early_suspend;
egalax_early_suspend.resume = egalax_i2c_early_resume;
register_early_suspend(&egalax_early_suspend);
EGALAX_DBG(DBG_MODULE, " Register early_suspend done\n");
#endif
EGALAX_DBG(DBG_MODULE, " I2C probe done\n");
return 0;
fail3:
i2c_set_clientdata(client, NULL);
destroy_workqueue(p_egalax_i2c_dev->ktouch_wq);
free_irq(client->irq, p_egalax_i2c_dev);
input_unregister_device(input_dev);
input_dev = NULL;
fail2:
gpio_free(p_egalax_i2c_dev->interrupt_gpio);
fail1:
kfree(p_egalax_i2c_dev);
p_egalax_i2c_dev = NULL;
EGALAX_DBG(DBG_MODULE, " I2C probe failed\n");
return ret;
}
下面看函数:allocate_Input_Dev,此函数中设计的是input输入子系统中向内核注册input_dev的知识:
static struct input_dev * allocate_Input_Dev(void)
{
int ret;
//定义input_dev结构体,开辟内存空间
struct input_dev *pInputDev=NULL;
pInputDev = input_allocate_device();
if(pInputDev == NULL)
{
EGALAX_DBG(DBG_MODULE, " Failed to allocate input device\n");
return NULL;//-ENOMEM;
}
//设置input_dev的属性,包括名称,id_table,input事件等,
详细内容可以搜索下input_dev属性的设置
pInputDev->name = "eGalax_Touch_Screen";
pInputDev->phys = "I2C";
pInputDev->id.bustype = BUS_I2C;//id_table
pInputDev->id.vendor = 0x0EEF;
pInputDev->id.product = 0x0020;
pInputDev->id.version = 0x0001;
set_bit(EV_ABS, pInputDev->evbit);//绝对坐标
#ifndef CONFIG_HAS_EARLYSUSPEND //We use this config to distinguish Linux and Android
set_bit(EV_KEY, pInputDev->evbit);//按键事件
__set_bit(BTN_TOUCH, pInputDev->keybit);
input_set_abs_params(pInputDev, ABS_X, 0, MAX_RESOLUTION, 0, 0);
参数分别是(input_dev结构体,表示X轴,最小分辨率,最大分辨率,误差数据,平滑数据)
//注意这里的最小分辨率和最大分辨率表示该input_dev所能支持的屏的分辨率
input_set_abs_params(pInputDev, ABS_Y, 0, MAX_RESOLUTION, 0, 0);
#endif
//下面很好的说明了 不同内核版本下对多点触摸的支持的改动
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
__set_bit(INPUT_PROP_DIRECT, pInputDev->propbit);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
input_mt_init_slots(pInputDev, MAX_SUPPORT_POINT, 0);
#else
input_mt_init_slots(pInputDev, MAX_SUPPORT_POINT);
#endif
input_set_abs_params(pInputDev, ABS_MT_POSITION_X, 0, MAX_RESOLUTION, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_POSITION_Y, 0, MAX_RESOLUTION, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
#else
input_set_abs_params(pInputDev, ABS_MT_POSITION_X, 0, MAX_RESOLUTION, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_POSITION_Y, 0, MAX_RESOLUTION, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_WIDTH_MAJOR, 0, MAX_RESOLUTION, 0, 0); //Size
input_set_abs_params(pInputDev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //Pressure
input_set_abs_params(pInputDev, ABS_MT_TRACKING_ID, 0, MAX_SUPPORT_POINT, 0, 0);
#endif // #if LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
input_set_events_per_packet(pInputDev, MAX_EVENTS);
#endif
ret = input_register_device(pInputDev);//向input_core注册input_dev,这里还有
dev和handler的绑定问题,在下一篇中在分析
if(ret)
{
EGALAX_DBG(DBG_MODULE, " Unable to register input device.\n");
input_free_device(pInputDev);
pInputDev = NULL;
}
return pInputDev;
}
下面看probe函数中的中断注册部分:
ret = request_irq(client->irq, egalax_i2c_interrupt, IRQF_TRIGGER_LOW, client->name, p_egalax_i2c_dev);
这里的egalax_i2c_interrupt是中断处理函数,下面看egalax_i2c_interrupt的源码:
static irqreturn_t egalax_i2c_interrupt(int irq, void *dev_id)
{
struct _egalax_i2c *egalax_i2c = (struct _egalax_i2c *)dev_id;
EGALAX_DBG(DBG_INT, " INT with irq:%d\n", irq);
disable_irq_nosync(irq);
//这个函数只有下面的关键一句:将work_irq描述的函数加入到,probe函数中创建的工作队列ktouch_wq中
//所以这里的中断处理函数实际为work_irq这个结构体描述的函数,再回头看probe函数中被work_irq函数描述的是哪一个
**probe中关于工作队列的源码开始:**
//创建一个动作队列
p_egalax_i2c_dev->ktouch_wq = create_singlethread_workqueue("egalax_touch_wq");
//将函数egalax_i2c_wq_irq用work_struct work_irq描述
INIT_WORK(&p_egalax_i2c_dev->work_irq, egalax_i2c_wq_irq);
**probe中关于工作队列的源码结束:**
//将work_struct work_irq描述的函数加入到队列中(这部分设备工作队列的知识)
queue_work(egalax_i2c->ktouch_wq, &egalax_i2c->work_irq);
//既然实际的中断处理函数是egalax_i2c_wq_irq,看看egalax_i2c_wq_irq的源码
return IRQ_HANDLED;
}
egalax_i2c_wq_irq的源码:
static void egalax_i2c_wq_irq(struct work_struct *work)
{
struct _egalax_i2c *egalax_i2c = container_of(work, struct _egalax_i2c, work_irq);
struct i2c_client *client = egalax_i2c->client;
EGALAX_DBG(DBG_INT, " egalax_i2c_wq run\n");
/*continue recv data*/
while( !gpio_get_value(egalax_i2c->interrupt_gpio) )
{
egalax_i2c_measure(egalax_i2c);//当中断脚由高变低时,执行此函数,看面看这个函数的源码
schedule();
}
if( egalax_i2c->skip_packet > 0 )
egalax_i2c->skip_packet = 0;
enable_irq(client->irq);
EGALAX_DBG(DBG_INT, " egalax_i2c_wq leave\n");
}
egalax_i2c_measure源码:
static int egalax_i2c_measure(struct _egalax_i2c *egalax_i2c)
{
int ret=0, frameLen=0, loop=3, i;
EGALAX_DBG(DBG_INT, " egalax_i2c_measure\n");
//调用下面这个函数会调用i2c_tranfer读触摸ic里面的数据,存在input_report_buf数组里
if( egalax_I2C_read(input_report_buf, MAX_I2C_LEN+2) < 0)
{
EGALAX_DBG(DBG_I2C, " I2C read input report fail!\n");
return -1;
}
if( DbgLevel&DBG_I2C )//调试信息,打印input_report_buf数组信息
{
char dbgmsg[(MAX_I2C_LEN+2)*4];
for(i=0; i<MAX_I2C_LEN+2; i++)
sprintf(dbgmsg+i*4, "[%02X]", input_report_buf[i]);
EGALAX_DBG(DBG_I2C, " Buf=%s\n", dbgmsg);
}
//从这里看input_report_buf[0]和input_report_buf[1]存放的是长度,
frameLen = input_report_buf[0] + (input_report_buf[1]<<8);
EGALAX_DBG(DBG_I2C, " I2C read data with Len=%d\n", frameLen);
if(frameLen==0)
{
EGALAX_DBG(DBG_MODULE, " Device reset\n");
return -1;
}
switch(input_report_buf[2])
{
//把input_report_buf[2]作为case的判断依据,input_report_buf[2]应该数据类型
case REPORTID_MTOUCH://触摸数据
if( !egalax_i2c->skip_packet && egalax_i2c->work_state==MODE_WORKING )
ProcessReport(input_report_buf+2, egalax_i2c);//看ProcessReport源码是怎么
对读来的触摸数据进行处理的
ret = 0;
break;
case REPORTID_VENDOR://i2c芯片的版本信息
atomic_set(&wait_command_ack, 1);
EGALAX_DBG(DBG_I2C, " I2C get vendor command packet\n");
if( p_char_dev->OpenCnts>0 ) // If someone reading now! put the data into the buffer!
{
loop=3;
do {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
ret = wait_event_timeout(p_char_dev->fifo_inq, (FIFO_SIZE-kfifo_len(p_char_dev->pDataKFiFo))>=MAX_I2C_LEN, HZ);
#else
ret = wait_event_timeout(p_char_dev->fifo_inq, kfifo_avail(&p_char_dev->DataKFiFo)>=MAX_I2C_LEN, HZ);
#endif
}while(ret<=0 && --loop);
if(ret>0) // fifo size is ready
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
ret = kfifo_put(p_char_dev->pDataKFiFo, input_report_buf+2, MAX_I2C_LEN);
#else
ret = kfifo_in_locked(&p_char_dev->DataKFiFo, input_report_buf+2, MAX_I2C_LEN, &p_char_dev->FiFoLock);
#endif
wake_up_interruptible( &p_char_dev->fifo_inq );
}
else
EGALAX_DBG(DBG_CDEV, " [Warning] Can't write data because fifo size is overflow.\n");
}
break;
default:
EGALAX_DBG(DBG_I2C, " I2C read error data with hedaer=%d\n", input_report_buf[2]);
ret = -1;
break;
}
return ret;
}
ProcessReport源码:
对触摸数据的上报就是这个函数处理的
static void ProcessReport(unsigned char *buf, struct _egalax_i2c *p_egalax_i2c)
{
unsigned char i, index=0, cnt_down=0, cnt_up=0, shift=0;
unsigned char status=0;
unsigned short contactID=0, x=0, y=0;
if(TotalPtsCnt<=0)
{
//这里的buf=input_report_buf+2(调用ProcessReport函数出入的形参为input_report_buf+2)
//从下面的判断条件来看,input_report_buf[4]存放的触摸点数
if(buf[1]==0 || buf[1]>MAX_SUPPORT_POINT)
{
EGALAX_DBG(DBG_POINT, " NumsofContacts mismatch, skip packet\n");
return;
}
TotalPtsCnt = buf[1];//触摸点数
RecvPtsCnt = 0;
}
else if(buf[1]>0)
{
TotalPtsCnt = RecvPtsCnt = 0;
EGALAX_DBG(DBG_POINT, " NumsofContacts mismatch, skip packet\n");
return;
}
while(index<MAX_POINT_PER_PACKET)
{
shift = index * POINT_STRUCT_SIZE + 2;
status = buf[shift] & 0x01;//对buf中的数据进行处理
得到status 、contactID 以及x、y
contactID = buf[shift+1];
x = ((buf[shift+3]<<8) + buf[shift+2]);
y = ((buf[shift+5]<<8) + buf[shift+4]);
if( contactID>=MAX_SUPPORT_POINT )
{
TotalPtsCnt = RecvPtsCnt = 0;
EGALAX_DBG(DBG_POINT, " Get error ContactID.\n");
return;
}
EGALAX_DBG(DBG_POINT, " Get Point[%d] Update: Status=%d X=%d Y=%d\n", contactID, status, x, y);
//这里是对触摸屏上报数据的x、y轴进行反向
#ifdef _SWITCH_XY
short tmp = x;
x = y;
y = tmp;
#endif
#ifdef _CONVERT_X
x = MAX_RESOLUTION-x;
#endif
#ifdef _CONVERT_Y
y = MAX_RESOLUTION-y;
#endif
//将获得的每一点数据(contactID,status以及x、y存在结构体数组pContactBuf中)
pContactBuf[RecvPtsCnt].ID = contactID;
pContactBuf[RecvPtsCnt].Status = status;
pContactBuf[RecvPtsCnt].X = x;
pContactBuf[RecvPtsCnt].Y = y;
RecvPtsCnt++;
index++;
// Recv all points, send input report
if(RecvPtsCnt==TotalPtsCnt)//当读取了所有触摸芯片中的坐标点的时候,开始上报
{
for(i=0; i<RecvPtsCnt; i++)//一个点一个点循环上报
{
//下面也给出了不同的内核下上报点的不同,这里对各种内核版本都做了兼容
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
input_mt_slot(input_dev, pContactBuf[i].ID);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, pContactBuf[i].Status);
if(pContactBuf[i].Status)
{
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, pContactBuf[i].Status);
input_report_abs(input_dev, ABS_MT_POSITION_X, pContactBuf[i].X);
input_report_abs(input_dev, ABS_MT_POSITION_Y, pContactBuf[i].Y);
}
#else
input_report_abs(input_dev, ABS_MT_TRACKING_ID, pContactBuf[i].ID);
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, pContactBuf[i].Status);
input_report_abs(input_dev, ABS_MT_POSITION_X, pContactBuf[i].X);
input_report_abs(input_dev, ABS_MT_POSITION_Y, pContactBuf[i].Y);
input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 0);
input_mt_sync(input_dev); //进行同步,表示一个点的数据以及上报完了
#endif
if(pContactBuf[i].Status)
cnt_down++;
else
cnt_up++;
}
#ifndef CONFIG_HAS_EARLYSUSPEND //We use this config to distinguish Linux and Android
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
input_mt_report_pointer_emulation(input_dev, true);
#endif
#endif
input_sync(input_dev);
EGALAX_DBG(DBG_POINT, " Input sync point data done! (Down:%d Up:%d)\n", cnt_down, cnt_up);
TotalPtsCnt = RecvPtsCnt = 0;
return;
}
}
}
到这里这一个触摸屏驱动大概的执行流程就讲解完了,下一篇将会讲解input_dev和handler的匹配过程。
也就是input_register_device函数执行是匹配过程