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

相关文章

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

Linux之软件包管理器yum详解

《Linux之软件包管理器yum详解》文章介绍了现代类Unix操作系统中软件包管理和包存储库的工作原理,以及如何使用包管理器如yum来安装、更新和卸载软件,文章还介绍了如何配置yum源,更新系统软件包... 目录软件包yumyum语法yum常用命令yum源配置文件介绍更新yum源查看已经安装软件的方法总结软

linux报错INFO:task xxxxxx:634 blocked for more than 120 seconds.三种解决方式

《linux报错INFO:taskxxxxxx:634blockedformorethan120seconds.三种解决方式》文章描述了一个Linux最小系统运行时出现的“hung_ta... 目录1.问题描述2.解决办法2.1 缩小文件系统缓存大小2.2 修改系统IO调度策略2.3 取消120秒时间限制3

Linux alias的三种使用场景方式

《Linuxalias的三种使用场景方式》文章介绍了Linux中`alias`命令的三种使用场景:临时别名、用户级别别名和系统级别别名,临时别名仅在当前终端有效,用户级别别名在当前用户下所有终端有效... 目录linux alias三种使用场景一次性适用于当前用户全局生效,所有用户都可调用删除总结Linux

Linux:alias如何设置永久生效

《Linux:alias如何设置永久生效》在Linux中设置别名永久生效的步骤包括:在/root/.bashrc文件中配置别名,保存并退出,然后使用source命令(或点命令)使配置立即生效,这样,别... 目录linux:alias设置永久生效步骤保存退出后功能总结Linux:alias设置永久生效步骤

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创