ts.dev->id.bustype = BUS_RS232;
ts.dev->id.vendor = 0xDEAD;
ts.dev->id.product = 0xBEEF;
ts.dev->id.version = S3C2410TSVERSION;
4.3.3 注册
input_register_device(ts.dev);
自此,该驱动设备注册到输入子系统中,会和事件处理层程序evdev匹配。
五,输入子系统
输入子系统分为三个层次,设备驱动层、核心层、以及事件处理层。
图片来源于网络
三个层级间的处理和联系
六,驱动层ts程序和事件处理层程序evdev匹配
6.1 evdev的注册
编译进内核,系统启动时自动加载,进而调用其evdev_init函数。
6.1.1 编译进内核
linux-2.6.22.6/drivers/input/Makefile
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
linux-2.6.22.6/.config
CONFIG_INPUT_EVDEV=y
6.1.2 调用evdev_init,注册到输入子系统
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);
}
6.2 驱动层ts程序注册到输入子系统
由第四点的s3c2410ts_probe函数分析可知,驱动层通过input_register_device接口注册到输入子系统。
input_register_device(ts.dev);
6.3 匹配
6.3.1 evdev注册时尝试匹配驱动层ts程序
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5])
return -EBUSY;
input_table[handler->minor >> 5] = handler;
}
// 将处理层程序加入到input_handler_list链表
list_add_tail(&handler->node, &input_handler_list);
// 从设备驱动层链表中取出每一个设备驱动层程序,和事件处理层程序匹配
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
// 和事件处理层程序中设置的黑名单进行匹配,如果设备驱动层程序在白名单中,则忽略!
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;
// 进行匹配
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
// 调用事件处理层程序中的connect函数 使事件处理层程序和设备驱动层程序建立联系。
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
'input: failed to attach handler %s to device %s, '
'error: %dn',
handler->name, kobject_name(&dev->cdev.kobj), error);
return error;
}
static const struct input_device_id *input_match_device(const struct input_device_id *id,
struct input_dev *dev)
{
int i;
// id:
// static const struct input_device_id evdev_ids[] = {
// { .driver_info = 1 }, /* Matches all devices */
// { }, /* Terminating zero entry */
// };
// dev:
// ts.dev->id.bustype = BUS_RS232;
// ts.dev->id.vendor = 0xDEAD;
// ts.dev->id.product = 0xBEEF;
// ts.dev->id.version = S3C2410TSVERSION;
// evdev事件处理层程序成功匹配ts驱动层程序
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
return id;
}
return NULL;
}
6.3.2 驱动层ts程序注册时尝试匹配evdev
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);
/*
* 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);
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;
}
/* 没有定义设备的getkeycode函数,则使用默认的获取键值函数 */
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
/*没有定义设备的setkeycode函数,则使用默认的设定键值函数*/
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
// 将设备驱动层程序加入到input_dev_list链表
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
'input%ld', (unsigned long) atomic_inc_return(&input_no) - 1);
if (!dev->cdev.dev)
dev->cdev.dev = dev->dev.parent;
// 会在/sys/class/input设备类下创建设备 /sys/class/input/input%ld
error = class_device_add(&dev->cdev);
if (error)
return error;
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO 'input: %s as %sn',
dev->name ? dev->name : 'Unspecified device', path ? path : 'N/A');
kfree(path);
// 从事件处理层程序链表中取出每一个事件处理层程序,和设备驱动层程序匹配
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
}
调用 input_attach_handler 之后的流程和evdev注册时尝试匹配驱动层ts程序一样
6.3.3 匹配成功建立联系
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
dev_t devt;
int minor;
int error;
// 从evdev_table数组中取出空位。最多支持32个设备。
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR 'evdev: no more free evdev devicesn');
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait);
evdev->exist = 1;
evdev->minor = minor;
// handle中的handler成员指向了配对成功的handler,
// dev成员指向了配对成功的device,
// private成员则指向了evdev设备本身。
// 将设备驱动层程序存储在evdev handle中
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
// 将事件处理层程序存储在evdev handle中
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, 'event%d', minor);
evdev_table[minor] = evdev;
// 在/sys/class/input设备类下创建设备/sys/class/input/event0,并创建设备节点/dev/event%d 自此应用程序就可以通过设备节点读取与设置硬件设备
// input类的主设备号 创建设备的次设备号
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);
if (IS_ERR(cdev)) {
error = PTR_ERR(cdev);
goto err_free_evdev;
}
/* temporary symlink to keep userspace happy */
error = sysfs_create_link(&input_class.subsys.kobj,
&cdev->kobj, evdev->name);
if (error)
goto err_cdev_destroy;
上一篇:《Linux驱动:s3c2440 lcd 驱动分析》
下一篇:《Linux驱动:s3c2410/s3c2440 ts驱动分析 之二》
设计资源 培训 开发板 精华推荐
- 使用 Analog Devices 的 ADM3062EBCPZ 的参考设计
- OM13092: 面向LPC5460x MCU的LPCXpresso开发板
- 使用 Microchip Technology 的 MIC2774N-XXBM5 的参考设计
- 使用 Analog Devices 的 LTC1067 的参考设计
- 实现 KEELOQ 和 PIC 微控制器的无线家庭安全
- SI1000DK,开发套件由主板和基于Si1005 8051 MCU的子卡组成
- 用于工业控制的 12W、12V 交流转直流单路输出电源
- SI2404FS08-EVB,带有 UART 接口的 SI2404 ISOmodem 芯片评估板
- 使用 NXP Semiconductors 的 PC9S08PT60VLH 的参考设计
- 使用 Analog Devices 的 LTC3370 的参考设计
- Imagination新一代多核IMG Series4 NNA赋予自动驾驶更高性能
- 苹果 M1 处理器跑分结果揭晓,性能超所有 iOS 设备
- 谷歌也要自研手机芯片?Pixel芯片将在未来几个月问市
- Redmi K40系列:小孔径OLED挖孔直屏 支持120Hz刷新率
- 华尔街投行:高通已经获得向华为出口4G芯片许可证
- 最高负载可达1吨,MiR1000机器人亮相2019工博会
- 第36章 SDIO—SD卡读写测试—零死角玩转STM32-F429系列
- 洛克希德马丁Q-53雷达选择Qorvo的碳化硅基氮化镓技术
- 2019工博会,仙知机器人如何做到“技”压群雄,众人围观?
- 安富利收购嵌入式软件供应商Witekio,拓展物联网战略