Linux下的platform总线驱动代码分析

来源:互联网 发布:cd刻录软件 编辑:程序博客网 时间:2024/06/10 04:38

前言:

本篇blog就针对platform总线驱动的调用机制,就直接抠出代码进行分析了。


Evdev.c (drivers\input)


module_init(evdev_init);
module_exit(evdev_exit);


static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

input.c层(核心层)


 input_register_handler(struct input_handler *handler)

// 初始化handler链表

INIT_LIST_HEAD(&handler->h_list);


//现将handler保存到input_table数组中,minor一般都是32的倍数

// 注意,这个在input.c核心代码中有用到

input_table[handler->minor >> 5] = handler;

//并将handler->node加入到input_handler_list链表中

list_add_tail(&handler->node, &input_handler_list);

// 通过handler找到对应的输入设备

list_for_each_entry(dev, &input_dev_list, node)

input_attach_handler(dev, handler);
 

//更新//proc中的数据                                             

input_wakeup_procfs_readers();                                


input_attach_handler(dev, handler); 

//比对设备id列表

list_for_each_entry(dev, &input_dev_list, node)

id = input_match_device(handler, dev);

// 如果比对成功,那么执行handler的connect函数

handler->connect(handler, dev, id);        


//比对方法

static const struct input_device_id evdev_ids[] = {            

{ .driver_info = 1 },/* Matches all devices */      

{ },/* Terminating zero entry */            

};


input_match_device(handler, dev);

  //id表示的是hander中的id_table,那这里面一般有什么呢

// 在evdev_ids中兼容所有的设备

for (id = handler->id_table; id->flags || id->driver_info; id++)

/*以下有几种比对方式:

1, 比较id->flags: 前提就是设置了flags,如果没有设置,就不比较

总线INPUT_DEVICE_ID_MATCH_BUS,id->bustype != dev->id.bustype

厂商:INPUT_DEVICE_ID_MATCH_VENDOR,id->vendor != dev->id.vendor

产品id:INPUT_DEVICE_ID_MATCH_PRODUCT,id->product != dev->id.product

版本:INPUT_DEVICE_ID_MATCH_VERSION,id->version != dev->id.version

2,比较设置产生的事件类型:比如有evbit,keybit,absbit

//就是将dev->id和handler->id中evbit/keybit/absbit数组进行位与预算

MATCH_BIT(evbit,  EV_MAX);


3,如果hander有match函数,那么就执行match函数,以下这种写法很神奇

if (!handler->match || handler->match(handler, dev))

return id; // 代码能执行到这里就表示找到了


总结:1,将构建的handler加入到input_handler_list中

     2,并匹配input_dev_list中的device,device的类型为struct input_dev

     3, 匹配的依据是各自对象中的id结构体,匹配成功后会执行handler中的connect函数

     4,对于input设备对象的驱动代码中,需要设置evbit/keybit/absbit等数组中的位,用于进行比对



------------------------------------------------------------------------------------------------------------

hander对象中的connect函数:

static struct input_handler evdev_handler = {

.event= evdev_event,

.connect= evdev_connect,

.disconnect= evdev_disconnect,

.fops= &evdev_fops,

.minor= EVDEV_MINOR_BASE,

.name= "evdev",

.id_table= evdev_ids,

};

//参数1,handler对象本身 2,匹配成功后的input设备对象 3,id列表

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)


struct evdev *evdev;


// 在evdev_table数组找到一个没有使用的位置

for (minor = 0; minor < EVDEV_MINORS; minor++)

if (!evdev_table[minor]);



//分配一个evdev 对象

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

//  初始化列表,用于记录????

INIT_LIST_HEAD(&evdev->client_list);

//初始化等待队列

init_waitqueue_head(&evdev->wait);



//设置evdev的名字,用于用户空间的设备节点

dev_set_name(&evdev->dev, "event%d", minor);


evdev->exist = true;

evdev->minor = minor;

// 此时出现了struct input_handle

// struct input_handle会记录input设备是哪个

evdev->handle.dev = input_get_device(dev);

evdev->handle.name = dev_name(&evdev->dev);

//同时handle也会记录handler是哪个

evdev->handle.handler = handler;

evdev->handle.private = evdev;

//设置input设备的设备号,次设备号从64开始

evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);

evdev->dev.class = &input_class;

evdev->dev.parent = &dev->dev;

evdev->dev.release = evdev_free;

device_initialize(&evdev->dev);



// 注册handle,实际就是将input设备对象加入到handle中的handle->d_node链表中

// 将handler加入到handle中的handle->h_node链表中

input_register_handle(&evdev->handle);


list_add_tail_rcu(&handle->d_node, &dev->h_list);

list_add_tail_rcu(&handle->h_node, &handler->h_list);

// 如果handler中有start,那么就执行start

if (handler->start)

handler->start(handle);

// 将evdev存放到evdev_table数组中,下标为次设备号

evdev_install_chrdev(evdev);

//注册字符设备,创建相应的设备节点

device_add(&evdev->dev);



总结:

1, struct evdev是用于与应用程序进行交互的媒介,用于创建字符设备

2, struct evdev是在handler和input设备匹配成功后产生的

3,  struct  evdev中包含了struct input_handle,该handle中记录了handler和input设备

4, 每一个input设备对应一个struct evdev,一个handler可以对应多个struct evdev和input设备

5, evdev中的input_handle两个链表:handle->d_node记录对应的input设备对象的链表

handle->d_node记录对应的handler对象

-----------------------------------------------------------------------------------



input核心层代码分析:

Input.c (drivers\input)


subsys_initcall(input_init);

module_exit(input_exit);


struct class input_class = {

.name= "input",

.devnode= input_devnode,

};


input_init(void)


// 创建/sys/class/input

class_register(&input_class);

// 创建/proc/bus/input/: devices   handlers,你可以理解成是总线

err = input_proc_init();


//注册字符设备

register_chrdev(INPUT_MAJOR, "input", &input_fops);


static const struct file_operations input_fops = {

.owner = THIS_MODULE,

.open = input_open_file,

.llseek = noop_llseek,

};

应用open()

sys_open


static int input_open_file(struct inode *inode, struct file *file)

struct input_handler *handler;

//根据设备此设备号召到对应的input_table

// 该数组在input_register_handler函数中初始化了

handler = input_table[iminor(inode) >> 5];

if (handler)

//如果有当前次设备对应的handler,那么就获取hander对应的fops

new_fops = fops_get(handler->fops);

// 保存用户空间的f_op

old_fops = file->f_op;

// 将handler的f_op赋值给用户空间的f_op

file->f_op = new_fops;

// 执行handler的open函数

err = new_fops->open(inode, file); 


此时要切换到evdev.c中代码中驱动

static struct input_handler evdev_handler = {

.event= evdev_event,

.connect= evdev_connect,

.disconnect= evdev_disconnect,

.fops= &evdev_fops,

.minor= EVDEV_MINOR_BASE,

.name= "evdev",

.id_table= evdev_ids,

};


static const struct file_operations evdev_fops = {

.owner= THIS_MODULE,

.read= evdev_read,

.write= evdev_write,

.poll= evdev_poll,

.open= evdev_open,

.release= evdev_release,

.unlocked_ioctl= evdev_ioctl,

#ifdef CONFIG_COMPAT

.compat_ioctl= evdev_ioctl_compat,

#endif

.fasync= evdev_fasync,

.flush= evdev_flush,

.llseek= no_llseek,

};

static int evdev_open(struct inode *inode, struct file *file)

int i = iminor(inode) - EVDEV_MINOR_BASE;

// 在evdev_table数组中根据次设备号找到对应的struct evdev

// 该对象是在evdev_connect()中实现的

evdev = evdev_table[i];

bufsize = evdev_compute_buffer_size(evdev->handle.dev);

//产生一个struct evdev_client对象,该对象用于承载用户空间和内核空间的缓冲区

//里面包含一个struct input_event buffer[];

client = kzalloc(sizeof(struct evdev_client) +

bufsize * sizeof(struct input_event),

GFP_KERNEL);

client->bufsize = bufsize;

client->evdev = evdev;

// 将evdev_client放入到evdev->client_list链表中

evdev_attach_client(evdev, client);

error = evdev_open_device(evdev);

//好像没做什么事情,主要是判断input设备对象是否有open方法,有就打开

//并且对应handle的使用进行计数

input_open_device(&evdev->handle);

//将evdev_client记录到应用空间的file中的私有数据中

file->private_data = client;


总结:1,当应用空间通过open函数时, 在evdev层会产生一个evdev_client对象

用户open-->input_handler-->evdev(handle)<----input_dev

 evdev_client

==============================================================================================
应用read()
---------------------------------------------------------------------
sys_read
-------------------------------------------------------------------
input.c  file->f_op = new_fops;

evdev.c:  evdev_handler->evdev_read


static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)


struct evdev_client *client = file->private_data;

struct evdev *evdev = client->evdev;

// 等待数据

wait_event_interruptible(evdev->wait,client->packet_head != client->tail || !evdev->exist);

//如果数据到来后,将数据上传给用户

input_event_to_user(buffer + retval, &event)

copy_to_user(buffer, event, sizeof(struct input_event)



总结: 1,read中数据的等待需要有等待队列来完成

        2,在read中的等待队列是谁唤醒的呢:


      

接下来我们来看看input设备驱动层中数据提交:

 input_report_key(button_dev, KEY_LEFT, 0);


input_event(dev, EV_KEY, code, !!value);


input_handle_event(dev, type, code, value);


input_start_autorepeat(dev, code);

input_pass_event(dev, type, code, value);


struct input_handler *handler;

struct input_handle *handle;

// 在input设备对象中的dev->h_list找到handle对象

list_for_each_entry_rcu(handle, &dev->h_list, d_node)

//再找到handler

handler = handle->handler;

//执行handler的event函数,并传递type, code, value

handler->event(handle, type, code, value);

  

所以此时又切换到evdev.c中的evdev_handler:

static void evdev_event(struct input_handle *handle, 

                   unsigned int type, unsigned int code, int value)
|

//获取通过handle中找到evdev

struct evdev *evdev = handle->private;

//封装input_event

event.type = type;

event.code = code;

event.value = value;

//通过evdev->client_list找到一个evdev_client对象

list_for_each_entry_rcu(client, &evdev->client_list, node)

// 将input设备层传递过来的数据放入到client对象中

evdev_pass_event(client, &event);

//如果type为EV_SYN的话,那么就将唤醒等待队列

if (type == EV_SYN && code == SYN_REPORT)

wake_up_interruptible(&evdev->wait);

总结:

      evdev.c                         

用户open-->input_handler-->evdev(handle)<----input_dev

  file->private_data<----evdev_client


用户read-->input_handler--evdev_client                              input_dev

填充evdev_client<----------------------input_report_key     


 <----input_evnt----------evdev->waitqueue<---wake_up_interruptible<----input_sync


0 0