記事ディレクトリ
linuxカーネルコード分析
コードフォローアップ方法
- 質問で読む
- メモを取り、絵を描く
- 連絡先アプリケーションを駆動すると、アプリケーションが呼び出され、ドライブが実現されます
階層分析
- 入力ハンドラ層:/driver/input/evdev.c
module_init(evdev_init);
module_exit(evdev_exit);
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 int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
//将当前的handler加入到一个input_handler_list
list_add_tail(&handler->node, &input_handler_list);
//遍历链表input_dev_list
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
//将当前的handler和input dev 进行匹配,event handler能够匹配所有的input dev
id = input_match_device(handler, dev);
//匹配成功,之后要调用handler中connect方法
//实际就是event handler
error = handler->connect(handler, dev, id);
//将当前的handler加入到/proc/bus/input/handlers文件中
input_wakeup_procfs_readers();
概要:
(1)登録済みevdev_handler
(2)入力開発リストをトラバースします。並列マッチング。定数マッチングが成功すると、ハンドラーのconnectメソッドが自動的に呼び出されます------ evdev_connect
-入力コアレイヤー:/driver/input/input.c
subsys_initcall(input_init); //类似于module_init();不过等级较高
module_exit(input_exit);
//注册类,类似于class_create();
err = class_register(&input_class);
//在/proc目录下创建bus/input/devices handlers
err = input_proc_init();
//申请设备号
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
概要:
(1)メインデバイス番号を
登録しました(2)入力クラスを登録しました
- Simple_input.c
input_register_device(inputdev);
//将input dev加入到链表 input_dev_list
list_add_tail(&dev->node, &input_dev_list);
//遍历input_handler_list链表进行匹配
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
//匹配成功,之后要调用handler中connect方法
//实际就是event handler
error = handler->connect(handler, dev, id);
レイヤードグラフ
分析:evdev.cのevdev_connect()-入力ハンドラーレイヤーに属します
evdev_connect();
|
//找到一个没有被使用的次设备号,从64开始,65,66
minior = input_get_new_minior(EVDEV_MINOR_BASE,EVDEV_MINORS,true);
//实例化一个evdev对象
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
//初始化evdev对象
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
//等待队列是完成阻塞
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
dev_no = minor;
dev_no -= EVDEV_MINOR_BASE; //减去了64
//创建设备文件 /dev/event0 1 2
dev_set_name(&evdev->dev,"event%d",dev_no);
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);//12 64
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
//以上代码和device_create是一样的
//利用handle记录input device和input handler
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
//你中有我,我中有你
evdev->handle.private = evdev;
//将儿子关联到父亲(input handler)和母亲(input dev)
error = 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);
//初始化了cdev,完成了fops
cdev_init(&evdev->cdev,&evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev,evdev->dev.devt,1);
device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt,...)
|
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
|
dev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
device_initialize(dev);
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->release = device_create_release;
dev_set_drvdata(dev,drvdata);
kobject_set_name_vargs(&dev->kobj,fmt,args); //设置名字
device_add(dev); //注册到系统
**総括する:
- edevの割り当て、初期化、入力デバイスとハンドラー間の関係の記録
- デバイスノード/ dev / event0を作成します
- cdevを登録し、fopsを実装します**
- 関係:
複数の入力デバイスが1つのイベントハンドラーに対応し、
1つの入力デバイスが1つのevdevに対応し、1つのデバイスノード/ dev / event1,1,2に対応します。 - すべてのデバイスノードがファイルIOのオープン、読み取り、書き込みを呼び出すと、実際にはcdevのfopsで各インターフェイスが呼び出され、最終的に呼び出されます。
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,
};
入力カーネル層のinput_devとinput_handlerの関係:
アプリケーションの入力サブシステムを呼び出すコード、データがユーザーレイヤーに渡される方法
アプリケーション層とドライバー層の呼び出しグラフ
以下に説明するように:
概要:
1つのデバイスノードは1つのcdevに対応し、1つのcdevは1つのevdevに対応し、input_devとinput_handlerの関係はevdevに記録され、バッファーキューもあります。evdevの役割は、使用されたすべてのものを記録することです。
evdev_openメソッド
evdev_openメソッドの機能
//通过儿子,找到老母 input device
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
//分配一个client对象,描述一个缓冲队列,存放发就是 input_event
/*sizeof(struct evdev_client)
+bufsize * sizeof(struct input_event)
包含了很多input_event大小
*/
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
//client 中有一个缓冲区
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
//在client中记录evdev
client->evdev = evdev;
//将client加入到evdev中的一个小链表
evdev_attach_client(evdev, client);
|
list_add_tail_rcu(&client->node,&evdev->client_list);
error = evdev_open_device(evdev);
if (error)
goto err_free_client;
//将client记录到file,方便其他的接口使用
file->private_data = client;
nonseekable_open(inode, file);
- 入力デバイスにバッファevdev_clientを割り当て、ユーザーは入力デバイス層によって報告されたデータを保存します
- evdev_clientはevdevに記録します
- evdev_clientはファイルに記録されます
読み取りアプリケーションでデータを取得する方法
読んだ
evdev_readメソッド
//获取到open中分配的缓冲区对象
struct evdev_client *client = file->private_data;
//获取到 evdev
struct evdev *evdev = client->evdev;
//表示一个数据包,要给用户
struct input_event event;
for(;;)
{
//实现非阻塞
if (client->head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
while (retval + input_event_size() <= count &&
//1.从缓冲区获取数据,存放在input_event数据包
evdev_fetch_next_event(client, &event)) {
|
*event = client->buffer[client->tail++];
// 2.将数据上报给用户
if (input_event_to_user(buffer + retval, &event))
|
copy_to_user(buffer,event,sizeof)
return -EFAULT;
// 3.统计上报多少数据
retval += input_event_size();
}
//如果当前是阻塞模式
if(!(file->f_flags & O_NONBLOCK))
{
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail) ||
!evdev->exist || client->revoked);
}
}
総括する:
- データがない場合は、スリープして待機します
- データがある場合は、バッファclient-> buffer [client-> tail ++]からデータを取得し、copy_to_userを介してユーザーに報告します。
質問:
データはバッファにどのように保存され
ますか?待機キューをウェイクアップするのは誰ですか?
input_report_key(inputdev、pdesc-> key_code、0);
input_sync(inputdev); //レポートデータの終了
input_report_key(inputdev,pdesc->key_code,0);
input_sync(inputdev); //上报数据结束
|//input_event(dev,EV_KEY,code,!!value);
input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);//上报数据
|
input_handle_event(dev, type, code, value);
|
//如果将数据较交给input handler去处理
if(disposition & INPUT_PASS_TO_HANDLERS)
{
struct input_value *v;
//将input device获取到数据暂存一下input value
v = &dev->vals[dev->num_vals++];
v->type = type;
v->code = code;
v->value = value;
input_pass_values(dev,dev->vals,dev->num_vals)
|
//从input device中获取到input handle
else
{
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (!handle->open)
count=input_to_handler(handle,vals,count);
|
//如果handler儿子找到handler父亲
struct input_handler *handler = handler->handler;
//如果有events函数指针,那就调用
if(hadnler->events)
handler->events(handle,vals,count);
else if(handler->event) //否则调用event();
else if(handler->event)
for(v = vals; v != end; v++)
handler->event(handle,v->type,v->code,v->value);
}
総括する:
- データが報告された場合、ハンドラーのevent()またはevent()は
実際にはevdev.cと呼ばれます。
.event = evdev_event,
.events = evdev_events,
|
//拿到evdev ,肯定要拿到缓冲区
struct evdev *evdev = handle->private;
struct evdev_client *client;
//获取到缓冲区evdev_client
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client,vals,count,time_mono,time_real);
|
//通过client获取到evdev
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
//获取到当前的时间戳
event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real);
for(v = vals; v != vals + count; v++)
{
//将input device 上报的数据获取到,并且封装成input event对象
event.type = v->type;
event.code = v->code;
event.value = v->valuel;
//将input event数据存放到缓冲区中
__pass_event(client,&event);
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
//如果调用 input_sync()
if(v->type == EV_SYN && v->code == SYN_REPORT)
wakeup = true;
}
//唤醒等待队列
if(wakeup)
wake_up_interruptible(&evdev->wait);
したがって、上記の質問に対する答えは、上記から分析できます。
データはどのようにバッファに保存されますか?
回答:データはinput_report_key(inputdev、pdesc-> key_code、0);
誰が待機キューをウェイクアップしますか?
回答:待機キューはinput_sync(inputdev);によって起こされます。
総括する:
- input_report_key(inputdev、pdesc-> key_code、0)
*入力デバイスによって生成されたデータは入力ハンドラーに渡され、入力ハンドラーはevent();を呼び出します。データをバッファーに格納しますclient-> buffer [client-> head ++] = event; - input_sync(inputdev);
レポートデータの終了、待機キューをウェイクアップし、入力デバイスがレポートデータのレポートを完了したことを示します
全体的な論理図は次のとおりです