linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(1)

本文主要是介绍linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(1),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


Programming input drivers(摘于Documentation\input\input-programming.txt)

这篇文档说明的输入设备驱动的编写。

Here comes a very simple example of an input device driver. The device has
just one button and the button is accessible at i/o port BUTTON_PORT. When

pressed or released a BUTTON_IRQ happens. 

The driver could look like:

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

static struct input_dev *button_dev;  输入设备结构体

static irqreturn_t button_interrupt(int irq, void *dummy)中断处理函数
{
input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);想输入子系统报告产生按键事件
input_sync(button_dev);   通知接受者,一个事件完毕
return IRQ_HANDLED;
}

module_init(button_init);

static int __init button_init(void)
{
int error;


if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {   申请中断处理函数
                printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
                return -EBUSY;
        }


button_dev = input_allocate_device();   分配一个输入设备结构,函数源码如下所示:
if (!button_dev) {
printk(KERN_ERR "button.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;

}

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;


dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); 分配一个input_dev结构体
if (dev) {
dev->dev.type = &input_dev_type; 初始化设备的类型
dev->dev.class = &input_class;  设置为输入设备类
device_initialize(&dev->dev);  初始化device结构
mutex_init(&dev->mutex);  初始化互斥锁
spin_lock_init(&dev->event_lock); 初始化事件自旋锁
INIT_LIST_HEAD(&dev->h_list);  初始化链表
INIT_LIST_HEAD(&dev->node);


__module_get(THIS_MODULE);  增加模块引用计数
}


return dev;
}




button_dev->evbit[0] = BIT_MASK(EV_KEY);   设置按键信息
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);


error = input_register_device(button_dev);  注册一个输入设备,源码如下:
if (error) {
printk(KERN_ERR "button.c: Failed to register device\n");
goto err_free_dev;

}

/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it's capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 */
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;


__set_bit(EV_SYN, dev->evbit);设置input_dev所支持的事件类型。事件类型由input_dev的evbit成员来表示,在这里将其EV_SYN置位,表示设备支持所有的事件。常用的事件类型如下:

1. #define EV_SYN 0x00 /*表示设备支持所有的事件*/
2. #define EV_KEY 0x01 /*键盘或者按键,表示一个键码*/
3. #define EV_REL 0x02 /*鼠标设备,表示一个相对的光标位置结果*/
4. #define EV_ABS 0x03 /*手写板产生的值,其是一个绝对整数值*/
5. #define EV_MSC 0x04 /*其他类型*/
6. #define EV_LED 0x11 /*LED灯设备*/
7. #define EV_SND 0x12 /*蜂鸣器,输入声音*/
8. #define EV_REP 0x14 /*允许重复按键类型*/
9. #define EV_PWR 0x16 /*电源管理事件*/


/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/


init_timer(&dev->timer);  初始化一个timer定时器
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}


if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

检查getkeycode()函数和setkeycode()函数是否被定义,如果没定义,则使用默认的处理函数,这两个函数为 input_default_getkeycode()和input_default_setkeycode()。 input_default_getkeycode()函数用来得到指定位置的键值。input_default_setkeycode()函数用来设置键值。


snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);


error = device_add(&dev->dev);   注册到设备模型中
if (error)
return error;


path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);


error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}


list_add_tail(&dev->node, &input_dev_list);调用此函数将input_dev加入全局量input_dev_list链表,  如下定义:static LIST_HEAD(input_dev_list);


list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); 此函数用来匹配input_dev和handler,只有匹配成功,才能进行下一步的关联操作。源码如下:

static int input_attach_handler(structinput_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id; 输入设备的指针

struct input_device_id {

kernel_ulong_t flags;

__u16 bustype;  总线类型
__u16 vendor;   制造商ID
__u16 product;  产品ID
__u16 version;  版本号


kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];


kernel_ulong_t driver_info;
};
 

        int error;


if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;

首先判断handle的blacklist是否被赋值,如果被赋值,则匹配blacklist中的数据跟dev->id的数据是否匹配。blacklist是一个input_device_id*的类型,其指向input_device_ids的一个表,这个表中存放了驱动程序应该忽略的设备。即使在id_table中找到支持的项,也应该忽略这种设备。


id = input_match_device(handler->id_table, dev); 此函数数匹配handle->id_table和dev->id中的数据。handler->id_table也是一个input_device_id类型的指针,其表示驱动支持的设备列表。其中handler结构体在另一篇博客中有说明。这个函数还有后面的下篇再分析。
if (!id)
return -ENODEV;


error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);


return error;
}


input_wakeup_procfs_readers();


mutex_unlock(&input_mutex);


return 0;
}


return 0;


 err_free_dev:
input_free_device(button_dev);
 err_free_irq:
free_irq(BUTTON_IRQ, button_interrupt);
return error;
}

static void __exit button_exit(void)
{
        input_unregister_device(button_dev);
free_irq(BUTTON_IRQ, button_interrupt);
}

module_exit(button_exit);


linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(2)的链接地址



这篇关于linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(1)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/879050

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

java向微信服务号发送消息的完整步骤实例

《java向微信服务号发送消息的完整步骤实例》:本文主要介绍java向微信服务号发送消息的相关资料,包括申请测试号获取appID/appsecret、关注公众号获取openID、配置消息模板及代码... 目录步骤1. 申请测试系统2. 公众号账号信息3. 关注测试号二维码4. 消息模板接口5. Java测试

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信