1.file_operations结构体:
2.misc_register/misc_deregister:该函数是用来注册一个杂项设备的。返回值为一个整数,如果注册成功返回0,注册失败则返回失败的错误代码。
3.miscdevice:misc_register要注册的设备结构,这个结构体是misc设备基本的结构体,在注册misc设备的时候必须要声明并初始化一个这样的结构体,但其中一般只需填充name minor fops字段就可以了
4.register_chrdev与misc_register:
5.i2c_set_clientdata://初始化I2c资源
6.disable_irq_nosync
7.i2c_check_functionality:用来判定设配器的能力,函数用于返回algorithm所支持的通信协议,比如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等
8.i2c_check_functionality(client->adapter, I2C_FUNC_I2C) 这一行代码可以使用I2C协议和设备进行通信。它进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。
9.INIT_WORK:可以理解为INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func
10.mutex_init
11.wake_lock_init:该函数设置锁的名字,类型,最后将新建的锁挂接到一个专门链接这些非锁状态的链表inactive_locks上(新建的wakelock初期都是出于非锁状态的,除非显示调用函数wake_lock来上锁)。接着如果使用函数wake_lock()来给特定的wakelock上锁的话,会将该锁从链表inactive_locks上移动到对应类型的专用链表上active_wake_locks[type]上。
12.create_singlethread_workqueue
13.input_allocate_device:申请并初始化一个输入设备。通过输入设备,驱动程序才能和用户交互。
14.input_register_device
15.regulator_get
16.INIT_WORK
17.input_allocate_device
18.sysfs_create_group
/*xiaofei modify end*/
static int cy8ctst242_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct cy8ctst242_ts_data *cy8ctst242_ts;
struct input_dev *input_dev;
struct device_node *np = client->dev.of_node; //device_node: 设备节点
const char *fw_name;
int len,temp_val,err = 0;
#ifdef CY8C242_PROXIMITY
struct input_dev *input_proximity;
#endif
//struct common_ts_platform_data *pdata = client->dev.platform_data;
struct common_ts_platform_data *pdata = &common_ts_info;
TPDBG("Cy8c242_ts cy8ctst242_ts_probe\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { //用来判定设配器的能力,函数用于返回algorithm所支持的通信协议,比如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等
err = -ENODEV;
goto exit_check_functionality_failed;
}
cy8ctst242_ts = kzalloc(sizeof(*cy8ctst242_ts), GFP_KERNEL);//用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0. GFP_KERNEL:内核内存的正常分配. 可能睡眠.
if (!cy8ctst242_ts) {
err = -ENOMEM;
goto exit_alloc_data_failed;
}
g_cy8ctst242_ts = cy8ctst242_ts;
cy8ctst242_ts->platform_data = pdata;
this_client = client;
cy8ctst242_ts->client = client;
i2c_set_clientdata(client, cy8ctst242_ts); //i2c_set_clientdata:初始化I2c资源
/*****************************************fw升级用的一些属性start*********************************************************/
fw_name = "Junda_8076d_vd807.iic";
err = of_property_read_string(np, "cy8c242,fw-name", &fw_name); //读取字符串属性
if (err && (err != -EINVAL)) {
dev_err(&client->dev, "Unable to read fw name\n");
goto prase_dt_err; }
if (fw_name) {
len = strlen(fw_name);
if (len > CY_FW_NAME_MAX_LEN - 1) {
dev_err(&client->dev, "Invalid firmware name\n");
goto prase_dt_err;
}
strlcpy(cy8ctst242_ts->fw_name, fw_name, len + 1);
}
err = of_property_read_u32(np, "cy8c242,fw-upgrade-id", &temp_val);
if (err && (err!= -EINVAL)) {
dev_err(&client->dev, "Unable to read fw upgrade id1\n");
goto prase_dt_err;
} else if (err!= -EINVAL)
cy8ctst242_ts->update_fw_version= temp_val;
/*****************************************************end**********************************************************/
if(cy8ctst242_ts_hw_init()<0) //初始化cy8ctst242硬件配置
goto exit_create_singlethread;
mutex_init(&cy8ctst242_ts->lock); //创建互斥锁
wake_lock_init(&cy8ctst242_ts->wake_lock, WAKE_LOCK_SUSPEND, CY8C242_NAME); //用于初始化一个新锁,type参数指定了锁的类型 WAKE_LOCK_SUSPEND – 这种锁如果被某个task持有,那么系统将无法进入休眠 名字??!
*********************************************************************************************************************
INIT_WORK(&cy8ctst242_ts->pen_event_work, cy8ctst242_ts_pen_irq_work); // #define INIT_WORK(_work, _func, _data),可以理解为INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func。
INIT_WORK(&cy8ctst242_ts->fw_update_work,fw_update_work);
#ifdef CY8C242_PROXIMITY
INIT_DELAYED_WORK(&cy8ctst242_ts->delayed_work, cy8ctst242_ts_proximity_work); // 定时器中断工作队列函数的调用 一般与schedule_delayed_work同步使用 schedule_delayed_work(&g_cy8ctst242_ts->delayed_work, msecs_to_jiffies(200)); 延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,输入参数:@work_struct:具体任务对象指针 @delay:延迟时间
#endif
cy8ctst242_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev)); //用于创建workqueue,只创建一个内核线程。输入参数: 可用PS来查看创建的线程
if (!cy8ctst242_ts->ts_workqueue) {
err = -ESRCH;
goto exit_create_singlethread;
}
#ifdef CFG_DETECT_UP_EVENT
dev_dbg(&client->dev,"%s: add up event timer \n",__func__);
init_timer(&_st_up_evnet_timer); //初始化结构体_st_up_evnet_timer init_timer() 必须在其他timer相关函数执行前调用,对结构体初始化
_st_up_evnet_timer.function = cy8ctst242_force_read_up_event; 延时结束时执行的回调函数,注意这里传递一个无符号长整型数字
_st_up_evnet_timer.data = 1;
_sui_last_point_cnt = 0;
msleep(10);
#endif
dev_dbg(&client->dev,"%s IRQ number is %d", client->name, client->irq);
input_dev = input_allocate_device(); // /*分配一个设备结构体*/ input_allocate_device()函数在内存中为输入设备结构体分配一个空间,并对其主要的成员进行了初始化.
if (!input_dev) {
err = -ENOMEM;
dev_err(&client->dev, "failed to allocate input device\n");
goto exit_input_dev_alloc_failed;
}
cy8ctst242_ts->input_dev = input_dev;
/*guliangzeng add for cp5217 cp5310 virtual_key self-adaption 20140126 start*/ 产品判断
if(get_hipad_board_id()==HIPAD_BOARD_ID_CP5217){
screen_max_y=SCREEN_MAX_Y_CP5217;
}else if(get_hipad_board_id()==HIPAD_BOARD_ID_CP5310){
screen_max_y=SCREEN_MAX_Y_CP5310;
}
/*guliangzeng add for cp5217 cp5310 virtual_key self-adaption 20140126 end*/
#ifdef CONFIG_CY8CTST242_MULTITOUCH
set_bit(EV_KEY, input_dev->evbit); //set_bit 告诉input输入子系统支持哪些事件,哪些按键 EV_KEY: 用来描述键盘,按键或者类似键盘设备的状态变化
set_bit(EV_ABS, input_dev->evbit); //EV_ABS :用来描述相对坐标轴上数值的变化,例如:描述触摸屏上坐标的值。
//set_bit(EV_SYN, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit); //BTN_TOUCH : 用于触摸接触事件
set_bit(INPUT_PROP_DIRECT, input_dev->propbit); //INPUT_PROP_DIRECT: 特性表明设备的坐标直接和屏幕坐标向对应
//set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
//set_bit(ABS_MT_POSITION_X, input_dev->absbit);
//set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
//set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
//__set_bit(KEY_MENU, input_dev->keybit);
//__set_bit(KEY_BACK, input_dev->keybit);
//__set_bit(KEY_HOME, input_dev->keybit);
//__set_bit(KEY_SEARCH, input_dev->keybit);
input_set_abs_params(input_dev,
ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
/*guliangzeng add for cp5217 cp5310 virtual_key self-adaption 20140126 start*/
//input_set_abs_params(input_dev,
// ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_POSITION_Y, 0, screen_max_y, 0, 0);
/*guliangzeng add for cp5217 cp5310 virtual_key self-adaption 20140126 end*/
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
input_set_abs_params(input_dev,ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
#else
set_bit(ABS_X, input_dev->absbit);
set_bit(ABS_Y, input_dev->absbit);
set_bit(ABS_PRESSURE, input_dev->absbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(EV_ABS, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
/*guliangzeng add for cp5217 cp5310 virtual_key self-adaption 20140126 start*/
//input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, screen_max_y, 0, 0);
/*guliangzeng add for cp5217 cp5310 virtual_key self-adaption 20140126 end*/
input_set_abs_params(input_dev,
ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);
#endif
#ifdef TOUCH_VIRTUAL_KEYS
cy8ctst242_ts_virtual_keys_init(); //触摸屏下方的三个按键初始化
#endif
cy8ctst242_dev_info(); //创建TP_INFO 既sys/tp_info/name
input_dev->name = CY8C242_TS_DEVICE; //dev_name(&client->dev)
err = input_register_device(input_dev); //函数注册输入设备结构体 用getevent命令查看输入设备是否存在
if (err) {
dev_err(&client->dev,
"cy8ctst242_ts_probe: failed to register input device: %s\n",
dev_name(&client->dev));
goto exit_input_register_device_failed;
}
/***********************************同上********************************************
#ifdef CY8C242_PROXIMITY
input_proximity = input_allocate_device();
if (!input_proximity) {
err = -ENOMEM;
dev_err(&client->dev, "failed to allocate input proximity\n");
goto exit_input_dev_alloc_failed;
}
cy8ctst242_ts->input_proximity = input_proximity;
input_set_abs_params(input_proximity, ABS_DISTANCE, 0, 1, 0, 0);
set_bit(EV_ABS, input_proximity->evbit);
input_proximity->name = "cy8c242_proximity";
err = input_register_device(input_proximity);
if (err) {
dev_err(&client->dev,
"failed to register input proximity: %s\n",
dev_name(&client->dev));
goto exit_input_register_device_failed;
}
/*make sure CTP already finish startup process */
msleep(10);
#endif
/*******************************************************************************
dev_dbg(&client->dev,"==register_early_suspend =");
#if defined (CONFIG_HAS_EARLYSUSPEND)
cy8ctst242_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
cy8ctst242_ts->early_suspend.suspend = cy8ctst242_ts_suspend;
cy8ctst242_ts->early_suspend.resume = cy8ctst242_ts_resume;
register_early_suspend(&cy8ctst242_ts.early_suspend); 在android系统中设定了一种特殊的电源管理模式即早期睡眠earlysuspend,他是系统进入待机的第一种状态,在android中设定了一些特殊的设备在此阶段关闭,主要是lcd及其背光。这些设备可以通过编程设定,主要实在设备的驱动程序中通过调用register_early_suspend()函数来注册。当系统在设定的空闲时间内没有任何活动事件发生时,系统就会自动进入earlysuspend状态。进入earlysuspend状态后系统会检测锁链表,看是否存在处于活动的锁,如果没有则进入挂起状态,如果有则等待。当系统进入挂起状态后,系统里的所有外设都已经处于掉电状态,这些设备挂起之前的状态都保存在内存中,当系统被设定的外中断唤醒时,这些设备均能通过设备驱动程序中已经注册的resume()函数来恢复原状态。
#endif
msleep(10);
err = misc_register(&cy8c242_misc_device); misc设备在misc_register注册的时候链接到这个链表,在misc_deregister中解除链接 可以看出,这个函数首先遍历misc_list链表,查找所用的次设备号是否已经被注册,防止冲突。如果是动态次设备号则分配一个,然后调用MKDEV生成设备号,从这里可以看出所有的misc设备共享一个主设备号MISC_MAJOR,然后调用device_create,生成设备文件。最后加入到misc_list链表中。 关于device_create,class_create 作用: class_create函数在misc.c中的模块初始化中被调用,现在一起说一下。这两个函数看起来很陌生,没有在ldd3中发现过,看源代码的时候发现class_create会调用底层组件__class_regsiter()是说明它是注册一个类。而device_create是创建一个设备,他是创建设备的便捷实现调用了device_register函数。他们都提供给linux设备模型使用,从linux内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代。相比devfs,udev有很多优势。
01.struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
02.class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);
struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”); 这样就创建了一个类和设备,模块被加载时,udev daemon就会自动在/dev下创建my_device设备文件节点。这样就省去了自己创建设备文件的麻烦。这样也有助于动态设备的管理。
if (err < 0) {
pr_err("Register cy8c242 misc device failed\n");
goto err_misc_register;
}
#ifdef CY8C242_PROXIMITY
err = misc_register(&cy8c242_proximity_misc_device); 同上
if (err < 0) {
pr_err("Register cy8c242 proximity misc device failed\n");
goto err_misc_register;
}
#endif
#ifdef SYSFS
//cy8ctst242_create_sysfs(client);
#endif
err = request_irq(client->irq, cy8ctst242_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, cy8ctst242_ts);
在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
if (err < 0) {
dev_err(&client->dev, "cy8ctst242_probe: request irq failed\n");
goto exit_irq_request_failed;
}
/* fangxiaofei modify for add cy8c242_ts 2013-11-26*/
cy8ctst242_ts_reset();
/* fangxiaofei modify for add cy8c242_ts 2013-11-26*/
cy8c242_ts_del_time_sync();
msleep(10);
cy8c242_ts_add_time();
err = device_create_file(&client->dev, &dev_attr_update_fw); //添加设备属性 ????属性在那看到? 把这一句注释掉呢!
if (err) {
dev_err(&client->dev, "sys file creation failed\n");
goto free_fw_update_sys;
}
dev_dbg(&client->dev,"%s: ==probe OK =\n",__func__);
return 0;
free_fw_update_sys:
exit_irq_request_failed:
free_irq(client->irq, cy8ctst242_ts);
err_misc_register:
exit_input_dev_alloc_failed:
exit_input_register_device_failed:
input_free_device(input_dev);
cancel_work_sync(&cy8ctst242_ts->pen_event_work);
destroy_workqueue(cy8ctst242_ts->ts_workqueue);
exit_create_singlethread:
prase_dt_err:
i2c_set_clientdata(client, NULL);
kfree(cy8ctst242_ts);
g_cy8ctst242_ts=NULL;
exit_alloc_data_failed:
exit_check_functionality_failed:
dev_err(&client->dev,"%s failed\n", __func__);
return err;
}
static int cy8ctst242_ts_hw_init(void)
{
int err = -1;
struct common_ts_platform_data *pdata = g_cy8ctst242_ts->platform_data;
struct i2c_client *client = g_cy8ctst242_ts->client;
TPDBG("Cy8c242_ts cy8ctst242_ts_hw_init\n");
if(pdata->power_onoff!=NULL)
pdata->power_onoff(1); //ic上电
if(pdata->reset_gpio_number!= -1)
{
err=gpio_request(pdata->reset_gpio_number, "ts_rst"); //其原型为 int gpio_request(unsigned gpio, const char *label) 先说说其参数,gpio则为你要申请的哪一个管脚,label则是为其取一个名字。debugfs有效,一个/sys/kernel/debug/gpio文件在那里将被找到
if(err<0)
goto gpio_request_rst_fail;
}
cy8ctst242_ts_reset(); //TP reset
if(err<0)
goto gpio_readwrite_fail;
if(pdata->irq_gpio_number!= -1)
{
err=gpio_request(pdata->irq_gpio_number,"ts_irq"); //irq命名
if(err<0)
goto gpio_request_irq_fail;
gpio_direction_input(pdata->irq_gpio_number); //可以调用gpio_direction_input和gpio_direction_output函数设置gpio输入输出方向
client->irq = gpio_to_irq(pdata->irq_gpio_number); //gpio_to_irq函数通过指定GPIO口映射到指定的IRQ中断号 gpio_to_irq你得到应该是irq的入口地址。
}
return 0;
gpio_request_irq_fail:
gpio_readwrite_fail:
gpio_free(pdata->reset_gpio_number); //用户的驱动程序可调用gpio_request和gpio_free使用或释放该gpio
gpio_request_rst_fail:
if(pdata->power_onoff!=NULL)
pdata->power_onoff(0);
return err;
}
/* heziqin modify bug for tp suspend end 2014.1.24 */
static void cy8ctst242_ts_reset(void)
{
struct common_ts_platform_data *pdata = g_cy8ctst242_ts->platform_data;
TPDBG("Cy8c242_ts cy8ctst242_ts_reset\n");
if(pdata!=NULL && pdata->reset_gpio_number!= -1)
{
dev_dbg(&this_client->dev,"%s", __func__);
gpio_direction_output(pdata->reset_gpio_number, 1); //设置GPIO方向
gpio_set_value(pdata->reset_gpio_number, 0); //GPIO赋值
msleep(1);
gpio_set_value(pdata->reset_gpio_number, 1);
msleep(10);
gpio_set_value(pdata->reset_gpio_number, 0);
msleep(100);
}
/*guliangzeng add for bug6114 proximity only work once 20140126 start*/
#ifdef CY8C242_PROXIMITY
cy8c242_proximity_set_enable(proximity_status); //TP 距离传感势能 贴脸熄灭
#endif
/*guliangzeng add for bug6114 proximity only work once 20140126 end*/
}
static void cy8ctst242_ts_virtual_keys_init(void)
{
int ret;
struct kobject *properties_kobj;
TPDBG("Cy8c242_ts cy8ctst242_ts_virtual_keys_init\n");
properties_kobj = kobject_create_and_add("board_properties", NULL); // kobject_create_and_add动态创建了一个kobject结构体,将其初始化,将其加入到kobject层次中,并最终返回所创建的 kobject的指针,当然如果函数执行失败,则返回NULL;在sys目录下创建board_properties文件夹
if (properties_kobj)
ret = sysfs_create_group(properties_kobj, //sysfs_create_file()在kobj对应的目录下创建attr对应的属性文件 既properties_attr_group对应的 "virtualkeys.cy8c242_ts" 这个文件
&properties_attr_group);
if (!properties_kobj || ret)
pr_err("failed to create board_properties\n");
}