linux输入子系统

search到的另外一篇比较不错的讲述linux input子系统的文章,特此留底。

最近公司的键盘驱动出了点问题,便看起了Red Hat 9 里键盘驱动的实现:
编写硬件相关的初始化程序,以获得的扫描码为参数调用drivers/char/keyboard.c中的handle_scancode函数。
以上是基于linux 2.4的键盘驱动实现。
由于增加了输入子系统,2.6版本的内核在编写键盘驱动上似乎不那么明朗了。这几天看了下输入子系统的实现,虽然研究得不够深入,但理清了输入子系统的层次关系,算是一个小收获。
拿键盘来说,整个输入子系统的架构如下:

      keyboard    handler
                       |
                       |
                       |
                       |
             input    core
                       |                     
                       |
                       |
                       |
          device     driver
很明显,这里的主角是input core。下面,我为你慢慢解释整个输入子系统的核心到底都干了些什么“你不知道的事”......
它主要位于drivers/input/input.c
涉及到的主要数据结构:
struct input_handler *handler,struct input_dev *dev,struct input_handle *handle(这个和前面那家伙有点像,也只是长得有点像而已......)
input.c维护着整个子系统的事件注册,管理以及分发,是整个子系统的精华所在。
如果你配置了CONFIG_VT(没有不配置这个的吧...?)在tty模块初始化(也就是在tty_init函数里,位于drivers/char/tty_io.c)的时候会调用vty_init():

static int __init tty_init(void)
{
    cdev_init(&tty_cdev, &tty_fops);
    if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
        register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
        panic("Couldn't register /dev/tty driver/n");
    device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "tty");

…………

…………

#ifdef CONFIG_VT
    cdev_init(&vc0_cdev, &console_fops);
    if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
        register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
        panic("Couldn't register /dev/tty0 driver/n");
    device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0");

    vty_init();
#endif

    return 0;
}
module_init(tty_init);

下面是 vty_init()位于drivers/char/vt.c

int __init vty_init(void)
{
    vcs_init();

    …………

    …………

    kbd_init();
    console_map_init();
#ifdef CONFIG_PROM_CONSOLE
    prom_con_init();
#endif
#ifdef CONFIG_MDA_CONSOLE
    mda_console_init();
#endif
    return 0;
}

你看到什么了?哈哈,就是那个kdb_init,位于
drivers/char/keyboard.c的家伙:
在这里,我们初始化了子系统上层事件处理层模块:

int __init kbd_init(void)
{
    int i;
    int error;

        for (i = 0; i < MAX_NR_CONSOLES; i++) {
        kbd_table[i].ledflagstate = KBD_DEFLEDS;
        kbd_table[i].default_ledflagstate = KBD_DEFLEDS;
        kbd_table[i].ledmode = LED_SHOW_FLAGS;
        kbd_table[i].lockstate = KBD_DEFLOCK;
        kbd_table[i].slockstate = 0;
        kbd_table[i].modeflags = KBD_DEFMODE;
        kbd_table[i].kbdmode = VC_XLATE;
    }
//在这里我们调用input_register_handler来向输入子系统注册一个struct input_handler *handler的结构。
    error = input_register_handler(&kbd_handler);
    if (error)
        return error;

    tasklet_enable(&keyboard_tasklet);
    tasklet_schedule(&keyboard_tasklet);

    return 0;
}
input_handler是输入设备事件接口的实现,定义于include/linux/input.h :
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
*    input core right after connect() method and also when a process
*    that "grabbed" a device releases it
* @fops: file operations this driver implements
* @minor: beginning of range of 32 minors for devices this driver
*    can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
*    handle
* @blacklist: prointer to a table of input_device_ids this driver should
*    ignore even if they match @id_table
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*/
struct input_handler {

    void *private;

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);

    const struct file_operations *fops;
    int minor;
    const char *name;

    const struct input_device_id *id_table;
    const struct input_device_id *blacklist;

    struct list_head    h_list;
    struct list_head    node;
};

对于我们的键盘,按键事件的处理可都在这个家伙里面啊!

input_register_handler是子系统中的一个非常重要的函数,定义于drivers/input/input.c

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;
    }
//将注册的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;
}
EXPORT_SYMBOL(input_register_handler);
在说input_attach_handler这个大腕的前面,我们先点一下那个叫input_table的数组,这是一个在本文件里定义的一个struct input_handler结构的数组,我们在注册struct input_handler的时候会根据设备的次设备号对32的倍数做为下标,将其input_handler放入数组.为什么是32?为什么要对32除?
在此文件开始处有一个宏定义#define INPUT_DEVICES    256我们的子系统最多支持256个设备,而我们的input_handler结构的数组最多处理8类事件,所以分给每一类的次设备号段就是32个了。

input_attach_handler定义于drivers/input/input.c :
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;
//在这里匹配主要是调用 input_match_device来进行的,但是handler结构体里面还有个"黑名单"字段,意思是说虽然咱俩认识,但是你在我的黑名单里,照样拜拜...
    if (handler->blacklist && input_match_device(handler->blacklist, dev))
        return -ENODEV;
//handler结构体里的id_table指针表明了可以与此驱动联姻的设备种类,根据这个字段我们调用这个函数
    id = input_match_device(handler->id_table, dev);
    if (!id)
        return -ENODEV;
//这个函数如果返回不为空那么我们就为他们正式举行婚礼...而此时,我们调用的这个connect就是在keyboard.从中定义的那个handler结构体中的那个connect函数.是不是有点明白了?
    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->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;

    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;
}
MATCH_BIT宏:
#define MATCH_BIT(bit, max) /
        for (i = 0; i < NBITS(max); i++) /
            if ((id->bit[i] & dev->bit[i]) != id->bit[i]) /
                break; /
        if (i != NBITS(max)) /
            continue;

这个宏的意思是,对于struct input_device_id中的每个数组,如果其中的元素某个位置位,input_dev中的相应数组的相应元素相应位也要置位,否则就不能匹配.
struct input_dev定义于input.h:
struct input_dev {

    void *private;

    const char *name;
    const char *phys;
    const char *uniq;
    struct input_id id;
    /*
    * 这里是根据输入信号的类型建立的各种数组,
    * 数组的每1bit代表一种信号类型,
    */
    unsigned long evbit[NBITS(EV_MAX)];
    unsigned long keybit[NBITS(KEY_MAX)];
    unsigned long relbit[NBITS(REL_MAX)];
    unsigned long absbit[NBITS(ABS_MAX)];
    unsigned long mscbit[NBITS(MSC_MAX)];
    unsigned long ledbit[NBITS(LED_MAX)];
    unsigned long sndbit[NBITS(SND_MAX)];
    unsigned long ffbit[NBITS(FF_MAX)];
    unsigned long swbit[NBITS(SW_MAX)];

    unsigned int keycodemax;
    unsigned int keycodesize;
    void *keycode;
    int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
    int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

    struct ff_device *ff;

    unsigned int repeat_key;
    struct timer_list timer;

    int state;

    int sync;

    int abs[ABS_MAX + 1];
    int rep[REP_MAX + 1];

    unsigned long key[NBITS(KEY_MAX)];
    unsigned long led[NBITS(LED_MAX)];
    unsigned long snd[NBITS(SND_MAX)];
    unsigned long sw[NBITS(SW_MAX)];

    int absmax[ABS_MAX + 1];
    int absmin[ABS_MAX + 1];
    int absfuzz[ABS_MAX + 1];
    int absflat[ABS_MAX + 1];

    int (*open)(struct input_dev *dev);
    void (*close)(struct input_dev *dev);
    int (*flush)(struct input_dev *dev, struct file *file);
    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

    struct input_handle *grab;

    struct mutex mutex;    /* serializes open and close operations */
    unsigned int users;

    struct class_device cdev;
    union {            /* temporarily so while we switching to struct device */
        struct device *parent;
    } dev;

    struct list_head    h_list;
    struct list_head    node;
};
这个结构体是以链表的形式存在于输入子系统中,也就是struct input_handler千辛万苦要寻找的另一半.
当这一切都成功地完成了以后,handler中的connect函数调用:
/*
* When a keyboard (or other input device) is found, the kbd_connect
* function is called. The function then looks at the device, and if it
* likes it, it can open it and get events from it. In this (kbd_connect)
* function, we should decide which VT to bind that keyboard to initially.
*/
static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
            const struct input_device_id *id)
{
    struct input_handle *handle;
    int error;
    int i;

    for (i = KEY_RESERVED; i < BTN_MISC; i++)
        if (test_bit(i, dev->keybit))
            break;

    if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit))
        return -ENODEV;

    handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
    if (!handle)
        return -ENOMEM;

    handle->dev = dev;
    handle->handler = handler;
    handle->name = "kbd";

    error = input_register_handle(handle);
    if (error)
        goto err_free_handle;

    error = input_open_device(handle);
    if (error)
        goto err_unregister_handle;

    return 0;

err_unregister_handle:
    input_unregister_handle(handle);
err_free_handle:
    kfree(handle);
    return error;
}
这个函数流程很清晰,初始化并注册一个struct input_handle,然后调用input_open_device(handle).
还记得上面我说struct input_handle和struct input_handler很像么?
struct input_handle就是用来连接struct input_handler和struct input_dev的结构.
int input_register_handle(struct input_handle *handle)
{
    struct input_handler *handler = handle->handler;
//将此结构放入相应的handler和dev的h_list链表末尾
//一个handle结构对应一个handler,而一个handler不一定对应一个handle
    list_add_tail(&handle->d_node, &handle->dev->h_list);
    list_add_tail(&handle->h_node, &handler->h_list);
//这里还要检查下start函数,不为空在这里要被调用
    if (handler->start)
        handler->start(handle);

    return 0;
}
input_open_device定义于input.c:
int input_open_device(struct input_handle *handle)
{
    struct input_dev *dev = handle->dev;
    int err;

    err = mutex_lock_interruptible(&dev->mutex);
    if (err)
        return err;

    handle->open++;
//如果是第一次打开,dev->open不为空就调用设备的open函数
    if (!dev->users++ && dev->open)
        err = dev->open(dev);

    if (err)
        handle->open--;

    mutex_unlock(&dev->mutex);

    return err;
}
到了这一步,可以说从handler与dev的配对已经完成了,我们也搞清楚了keyboard handler与input core之间的联系,想必大家现在对输入子系统已经有了一个框架的概念了吧?
但是,你有没有想过如果input_dev_list中的元素是怎么来的?也就是说假如我们只初始化一个handler,而没有相应的input_dev,那不是一点意义都没有?
虽然我们在以键盘的眼光在看输入子系统,可是我们对按键中断,键值的处理到现在还没有给出一个概念,我们的子系统到现在也没有与实实在在的键盘驱动联系起来.
下面我们将以普通PC上的PS/2接口的键盘驱动为切入点来一窥整个子系统运转的真正面目.

关于键盘么,我们插点背景来娱乐一下,就像听着许茹芸的声音,总会感觉很舒服:
我们常用的键盘一般包括:
USB 键盘 - 最近为所有的新机器所支持(Macintosh and IBM/compatible).
IBM/兼容 键盘 - 也称 "AT keyboards" 或者 "PS/2 keyboards", 现代pc都支持. 本文的主题.
ADB 键盘 - Apple Desktop Bus of older Macintosh systems.
原来的IBM 以及兼容机使用一种称作 "XT "的键盘. 现在不多见了,我们不介绍.后来IBM引入 AT 系统, AT之后是IBM PS/2.
AT 键盘和 PS/2 键盘类似,是我们常用的键盘. PS/2 设备使用更小的连接器,支持更多一点的特性. 同时兼容AT.
而说到键盘驱动,我们就不能不提那个Intel 的8042控制器.
键盘包含一个由 keys组成的矩阵. 所有的键都为一个板上处理器监控,称作键盘编码器, (一般是i8048? 见下表).虽然这种芯片挺多,但是其职
能基本如下:

监控是哪个或那几个键被按下/释放,把相应的数据送到主板. 这个处理器处理所有的 debouncing(?what!) ,把数据缓存到他的16-byte 的缓冲区中. 在IBM兼容机上,主板也有一个板上芯片,称作键盘控制器.一般是8042(就是我们说的那个了). 他负责解码从键盘来的信息,通知系统软件各种事件.在host 和主板的通讯中 都使用IBM 协议.

现代键盘的encoders:
Holtek: HT82K28A, HT82K628A, HT82K68A, HT82K68E?EMC: EM83050, EM83050H, EM83052H, EM83053H,?Intel: 8048, 8049
Motorola: 6868, 68HC11, 6805
Zilog: Z8602, Z8614, Z8615, Z86C15, Z86E23

键盘处理器(encoder)大部分时间在"扫描", 监视着键矩阵. 一旦发现有键被按下,释放,或被按住不放,encoder就会向计算机发送一个数据包,称为扫描码. 有两种不同的扫描码, "make codes" 和 "break codes". make code 是键被按下,按住不放是产生的. break code 是键被释放时产生的. 每个键都有自己唯一的make code 和 break code. make code 和 break codes 的集合称为扫描码集. 共有三种标准的扫描码集.所有现代的键盘默认使用扫描码集 set 2.
以上的讨论都是针对硬件,其实,如果写一个底层的键盘相关的软件for PC,是不该直接和键盘通信的. 主板上一般都有键盘控制器,它在键盘和外设总线间是一个接口. 这个控制器处理 signal-level的东东和协议的细节 ,同时提供转换,解释,处理扫描码,执行命令的功能.
PC 的键盘一般使用Intel 8042/兼容 的微控制器.现代计算机上,这个功能一般集成到南桥 . 然而,这个设备逻辑上仍然叫做 "the 8042".基于主板的不同,键盘控制器可以工作于:"AT-兼容" 模式, 或者 "PS/2-兼容" 模式. 如果主板支持 PS/2 鼠标就会使用后者. 这时, 8042 既是键盘控制器又是鼠标控制器. 键盘控制器根据硬连线的方式自动决定工作于哪种模式.
在看驱动之前,我们很有必要看一下目录drivers/input/keyboard下面的Kconfig,就像复旦人甲说的那样,这玩意的作用就像路标:
config KEYBOARD_ATKBD
    tristate "AT keyboard" if EMBEDDED || !X86_PC
    default y
    select SERIO
    select SERIO_LIBPS2
    select SERIO_I8042 if X86_PC
    select SERIO_GSCPS2 if GSC
    help
    Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
    you'll need this, unless you have a different type keyboard (USB, ADB
    or other). This also works for AT and PS/2 keyboards connected over a
    PS/2 to serial converter.

    If unsure, say Y.

    To compile this driver as a module, choose M here: the
    module will be called atkbd.
这个就是我们需要的,根据相应的makefile中的内容,我们知道目标就是drivers/input/keyboard/atkbd.c.
看看他的初始化:
static int __init atkbd_init(void)
{
    return serio_register_driver(&atkbd_drv);
}

static void __exit atkbd_exit(void)
{
    serio_unregister_driver(&atkbd_drv);
}

module_init(atkbd_init);
module_exit(atkbd_exit);
哇哇,超级简单,可简单的背后,却不一定能看到简单的实质.

static struct serio_driver atkbd_drv = {
    .driver        = {
        .name    = "atkbd",
    },
    .description    = DRIVER_DESC,
    .id_table    = atkbd_serio_ids,
    .interrupt    = atkbd_interrupt,
    .connect    = atkbd_connect,
    .reconnect    = atkbd_reconnect,
    .disconnect    = atkbd_disconnect,
    .cleanup    = atkbd_cleanup,
};
像键盘,鼠标,触摸屏的底层驱动,一般都是用这个struct serio_driver来实现(内部封装了一个device_driver结构).
serio_register_driver定义于serio.h:
static inline int serio_register_driver(struct serio_driver *drv)
{
    return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
}
__serio_register_driver定义于serio.c:
int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
{
//struct serio_driver结构中的一个字段,用来指明与设备绑定是手动还是自动
    int manual_bind = drv->manual_bind;
    int error;
//驱动的总线类型是serio_bus
    drv->driver.bus = &serio_bus;
    drv->driver.owner = owner;
    drv->driver.mod_name = mod_name;

    /*
    * Temporarily disable automatic binding because probing
    * takes long time and we are better off doing it in kseriod
    */
    drv->manual_bind = 1;
//注册驱动
    error = driver_register(&drv->driver);
    if (error) {
        printk(KERN_ERR
            "serio: driver_register() failed for %s, error: %d/n",
            drv->driver.name, error);
        return error;
    }

    /*
    * Restore original bind mode and let kseriod bind the
    * driver to free ports
    */
//看到英文注释了吧?关于kseriod这个内核线程,我们会在下面看到
    if (!manual_bind) {
        drv->manual_bind = 0;
        error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
        if (error) {
            driver_unregister(&drv->driver);
            return error;
        }
    }

    return 0;
}
很 明显,我们下一步要看的就是那个driver_register.其实很多东西都是后来慢慢才有体会.我记得当时看linux那些事的时候,对这些总线, 驱动,设备基本不懂,看着文章好玩,可看完又没能体会其中真正的意义.慢慢地,内核逐渐熟悉了以后,才逐渐明白这个模型.
就如同他们说的那样,总 线是一条主线,上面挂着2条子链,一条是设备的,一条是驱动的.他们3者的结构体里面都分别有指向对方的指针.每当有一个驱动要挂到总线的驱动链上去,他 都会到总线的设备链上一一查看,看是否有能和自己"配对"的设备,像这个操作,一般都是由函数名里面包含probe这样的函数完成的.设备也是如此.
当明白了这个模型以后,我们再看代码就会发现清晰了很多.
"我们不光要看代码,还要看代码背后的哲学"

driver_register定义于driver.c
/**
*    driver_register - register driver with bus
*    @drv:    driver to register
*
*    We pass off most of the work to the bus_add_driver() call,
*    since most of the things we have to do deal with the bus
*    structures.
*/
int driver_register(struct device_driver * drv)
{
    if ((drv->bus->probe && drv->probe) ||
        (drv->bus->remove && drv->remove) ||
        (drv->bus->shutdown && drv->shutdown)) {
        printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods/n", drv->name);
    }
    klist_init(&drv->klist_devices, NULL, NULL);
    return bus_add_driver(drv);
}
英文注释说的很明白他的主要功能是由bus_add_driver这个函数完成的,定义于bus.c:

int bus_add_driver(struct device_driver *drv)
{
    struct bus_type * bus = get_bus(drv->bus);
    int error = 0;

    if (!bus)
        return -EINVAL;
//针对基类kobject以及sysfs的操作(推荐看linux那些事之--我是sysfs)
    pr_debug("bus %s: add driver %s/n", bus->name, drv->name);
    error = kobject_set_name(&drv->kobj, "%s", drv->name);
    if (error)
        goto out_put_bus;
    drv->kobj.kset = &bus->drivers;
    if ((error = kobject_register(&drv->kobj)))
        goto out_put_bus;

//在注册serio_bus的时候,这个字段被置为1
    if (drv->bus->drivers_autoprobe) {
        error = driver_attach(drv);
        if (error)
            goto out_unregister;
    }
//将驱动挂到总线上
    klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
    module_add_driver(drv->owner, drv);
//设置属性
    error = driver_add_attrs(bus, drv);
    if (error) {
        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",
            __FUNCTION__, drv->name);
    }
    error = add_bind_files(drv);
    if (error) {
        /* Ditto */
        printk(KERN_ERR "%s: add_bind_files(%s) failed/n",
            __FUNCTION__, drv->name);
    }

    return error;
out_unregister:
    kobject_unregister(&drv->kobj);
out_put_bus:
    put_bus(bus);
    return error;
}
大家都是明眼人,知道我们的重点是要说driver_attach(drv):
/**
*    driver_attach - try to bind driver to devices.
*    @drv:    driver.
*
*    Walk the list of devices that the bus has on it and try to
*    match the driver with each one. If driver_probe_device()
*    returns 0 and the @dev->driver is set, we've found a
*    compatible pair.
*/
int driver_attach(struct device_driver * drv)
{
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
简洁!明了!

/**
*    bus_for_each_dev - device iterator.
*    @bus:    bus type.
*    @start:    device to start iterating from.
*    @data:    data for the callback.
*    @fn:    function to be called for each device.
*
*    Iterate over @bus's list of devices, and call @fn for each,
*    passing it @data. If @start is not NULL, we use that device to
*    begin iterating from.
*
*    We check the return of @fn each time. If it returns anything
*    other than 0, we break out and return that value.
*
*    NOTE: The device that returns a non-zero value is not retained
*    in any way, nor is its refcount incremented. If the caller needs
*    to retain this data, it should do, and increment the reference
*    count in the supplied callback.
*/

int bus_for_each_dev(struct bus_type * bus, struct device * start,
             void * data, int (*fn)(struct device *, void *))
{
    struct klist_iter i;
    struct device * dev;
    int error = 0;

    if (!bus)
        return -EINVAL;

    klist_iter_init_node(&bus->klist_devices, &i,
                 (start ? &start->knode_bus : NULL));
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
    klist_iter_exit(&i);
    return error;
}
说 实话,我对内核里那些有关链表的宏实在是很佩服,我估计把这些宏拿出来,应该不比STL差吧?还有那些红黑树,堆排序...本科的时候天天听老师说数据结 构是多么多么重要,一直没当回事,直到失去才后悔莫及...如果给我一个再来一次的机会,我会问一句,那些写内核的是不是小学就开始学数据结构了?
这个函数就是对总线上的每个设备都执行fn(dev, data),也就是__driver_attach这个函数

static int __driver_attach(struct device * dev, void * data)
{
    struct device_driver * drv = data;

    /*
    * Lock device and try to bind to it. We drop the error
    * here and always return 0, because we need to keep trying
    * to bind to devices and some drivers will return an error
    * simply if it didn't support the device.
    *
    * driver_probe_device() will spit a warning if there
    * is an error.
    */

    if (dev->parent)    /* Needed for USB */
        down(&dev->parent->sem);
//在这里,我们获得了设备的信号量,下面我们会讲到
    down(&dev->sem);

//哟......还单身着哪?师太,你就从了老衲吧...
    if (!dev->driver)
        driver_probe_device(drv, dev);
    up(&dev->sem);
    if (dev->parent)
        up(&dev->parent->sem);

    return 0;
}


/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*
* This function returns 1 if a match is found, -ENODEV if the device is
* not registered, and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
    int ret = 0;

    if (!device_is_registered(dev))
        return -ENODEV;
//从不从得看咱俩合不合适,俺虽然不算沉鱼落雁,但也不是人人都跟的哈
    if (drv->bus->match && !drv->bus->match(dev, drv))
        goto done;

    pr_debug("%s: Matched Device %s with Driver %s/n",
        drv->bus->name, dev->bus_id, drv->name);

   
ret = really_probe(dev, drv);

done:
    return ret;
}
注释是好东西啊,连怎么用都给你说好了,看着啊,调用之前我们一定要获得设备的信号量,我们在刚才获得信号量的时候提了一下.然后,回调了serio_bus的match函数,要记住,我们之前所做的每一步都在为未来做铺垫...这句话是不是傻了点?

static int really_probe(struct device *dev, struct device_driver *drv)
{
    int ret = 0;

    atomic_inc(&probe_count);
    pr_debug("%s: Probing driver %s with device %s/n",
        drv->bus->name, drv->name, dev->bus_id);
    WARN_ON(!list_empty(&dev->devres_head));

    dev->driver = drv;
    if (driver_sysfs_add(dev)) {
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
            __FUNCTION__, dev->bus_id);
        goto probe_failed;
    }
//总线或者驱动里有探测函数的,我们就要探测下,此时也就回调了驱动或者总线里的probe函数,像我们键盘里的atkbd_connect,就会在probe里调用,到这里,我们才算完成了驱动和设备的匹配,所以说啊,感情的路上一直都是很坎坷的,配个对还那么麻烦...
    if (dev->bus->probe) {
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }

    driver_bound(dev);
    ret = 1;
    pr_debug("%s: Bound Device %s to Driver %s/n",
        drv->bus->name, dev->bus_id, drv->name);
    goto done;

probe_failed:
    devres_release_all(dev);
    driver_sysfs_remove(dev);
    dev->driver = NULL;

    if (ret != -ENODEV && ret != -ENXIO) {
        /* driver matched but the probe failed */
        printk(KERN_WARNING
               "%s: probe of %s failed with error %d/n",
               drv->name, dev->bus_id, ret);
    }
    /*
    * Ignore errors returned by ->probe so that the next driver can try
    * its luck.
    */
    ret = 0;
done:
    atomic_dec(&probe_count);
    wake_up(&probe_waitqueue);
    return ret;
}

如果出了错,就要进行一些清理工作,释放资源,sysfs也不会再容你存在,相应的计数也要平衡.

我们来看一下dev->bus->probe:
static int serio_driver_probe(struct device *dev)
{
    struct serio *serio = to_serio_port(dev);
    struct serio_driver *drv = to_serio_driver(dev->driver);

    return
serio_connect_driver(serio, drv);
}
这里调用了一个serio_connect_driver:

static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
{
    int retval;

    mutex_lock(&serio->drv_mutex);
   
retval = drv->connect(serio, drv);
    mutex_unlock(&serio->drv_mutex);

    return retval;
}
恩恩,千呼万唤始出来,我们的 static struct serio_driver atkbd_drv = {
    .driver        = {
        .name    = "atkbd",
    },
    .description    = DRIVER_DESC,
    .id_table    = atkbd_serio_ids,
    .interrupt    = atkbd_interrupt,
    .connect    = atkbd_connect,
    .reconnect    = atkbd_reconnect,
    .disconnect    = atkbd_disconnect,
    .cleanup    = atkbd_cleanup,
};
结构里的atkbd_connect终于在这里被调用了:
static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
{
    struct atkbd *atkbd;
    struct input_dev *dev;
    int err = -ENOMEM;
//1.分配并清零一个struct atkbd结构
//2.分配并初始化一个input_dev结构
    atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL);
    dev = input_allocate_device();
    if (!atkbd || !dev)
        goto fail1;

    atkbd->dev = dev;
    ps2_init(&atkbd->ps2dev, serio);
    INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work);
    mutex_init(&atkbd->event_mutex);

    switch (serio->id.type) {

        case SERIO_8042_XL:
            atkbd->translated = 1;
        case SERIO_8042:
            if (serio->write)
                atkbd->write = 1;
            break;
    }

    atkbd->softraw = atkbd_softraw;
    atkbd->softrepeat = atkbd_softrepeat;
    atkbd->scroll = atkbd_scroll;

    if (atkbd->softrepeat)
        atkbd->softraw = 1;

    serio_set_drvdata(serio, atkbd);

    err = serio_open(serio, drv);
    if (err)
        goto fail2;

    if (atkbd->write) {

        if (atkbd_probe(atkbd)) {
            err = -ENODEV;
            goto fail3;
        }

        atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
        atkbd_activate(atkbd);

    } else {
        atkbd->set = 2;
        atkbd->id = 0xab00;
    }

    atkbd_set_keycode_table(atkbd);
//在这里对atkbd->dev进行进一步填充,设置其属性(不然你拿上面那个几乎空白的input_dev跟谁匹配去啊...)
    atkbd_set_device_attrs(atkbd);

    err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
    if (err)
        goto fail3;

    atkbd_enable(atkbd);

    err =
input_register_device(atkbd->dev);
    if (err)
        goto fail4;

    return 0;

fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
fail3:    serio_close(serio);
fail2:    serio_set_drvdata(serio, NULL);
fail1:    input_free_device(dev);
    kfree(atkbd);
    return err;
}
我们很有必要对函数里发红的那家伙说点什么,恩恩,哈哈,走了千万里,终于找到了你,就是在这里,我们将一个input_dev注册到输入子系统的input_dev_list链表中去:

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;
//设置evbit[0]的第_SYN位,还记得原来我们说的么,每一位代表一个事件
    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);
//这些如果为空就设置位默认值,在我们这里是不需要的,已经被初始化了,是在atkbd_set_device_attrs(atkbd)里//进行的
    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;
//插入到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;

    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 %s/n",
        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    kfree(path);
//每次注册一个input_dev设备,我们同样要对handler链表遍历,进行匹配
    list_for_each_entry(handler, &input_handler_list, node)
       
input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    return 0;
}
到了这里,也许有人问,那个serio结构体是干什么的呢?
在回答你这个问题之前,我们先来看看drivers/input/serio/下面的Kconfig(怎么又是他?你说不是他还能是谁...?)
config SERIO
    tristate "Serial I/O support" if EMBEDDED || !X86
    default y
    ---help---
    Say Yes here if you have any input device that uses serial I/O to
    communicate with the system. This includes the
            * standard AT keyboard and PS/2 mouse *
    as well as serial mice, Sun keyboards, some joysticks and 6dof
    devices and more.

    If unsure, say Y.

    To compile this driver as a module, choose M here: the
    module will be called serio.

if SERIO

config SERIO_I8042
    tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
    default y
    depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K
    ---help---
    i8042 is the chip over which the standard AT keyboard and PS/2
    mouse are connected to the computer. If you use these devices,
    you'll need to say Y here.

    If unsure, say Y.

    To compile this driver as a module, choose M here: the
    module will be called i8042.
看见了吧,符合这些规定的都使用serio来作为底层驱动的实现.
我们来小看一下serio.c,什么?你怕?怕什么,有我呢,我都不怕你怕啥,我们的空15军都敢从5000米高空盲降,你还有什么好怕的呢?

static int __init serio_init(void)
{
    int error;

    error = bus_register(&serio_bus);
    if (error) {
        printk(KERN_ERR "serio: failed to register serio bus, error: %d/n", error);
        return error;
    }

    serio_task = kthread_run(serio_thread, NULL, "kseriod");
    if (IS_ERR(serio_task)) {
        bus_unregister(&serio_bus);
        error = PTR_ERR(serio_task);
        printk(KERN_ERR "serio: Failed to start kseriod, error: %d/n", error);
        return error;
    }

    return 0;
}

static void __exit serio_exit(void)
{
    bus_unregister(&serio_bus);
    kthread_stop(serio_task);
}

subsys_initcall(serio_init);
module_exit(serio_exit);

serio_init就干了两件事:
bus_register(&serio_bus)
kthread_run(serio_thread, NULL, "kseriod")
通俗点来说就是注册一个总线并产生一个线程.
int bus_register(struct bus_type * bus)
{
    int retval;

    BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);

    retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);
    if (retval)
        goto out;

    subsys_set_kset(bus, bus_subsys);
//向全局的bus->subsys"登记"
//subsystem_register() -> kset_add() -> kobject_add()
    retval = subsystem_register(&bus->subsys);
    if (retval)
        goto out;
//有没有发现对设备的操作和对驱动的操作很像?
//kset_register(&bus->devices) 和set_register(&bus->drivers)
//作用类似,把bus->devices这个kset加入到bus- >subsys这个//subsystem中去。
    kobject_set_name(&bus->devices.kobj, "devices");
    bus->devices.kobj.parent = &bus->subsys.kobj;
    retval = kset_register(&bus->devices);
    if (retval)
        goto bus_devices_fail;

    kobject_set_name(&bus->drivers.kobj, "drivers");
    bus->drivers.kobj.parent = &bus->subsys.kobj;
    bus->drivers.ktype = &ktype_driver;
    retval = kset_register(&bus->drivers);
    if (retval)
        goto bus_drivers_fail;

    klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
    klist_init(&bus->klist_drivers, NULL, NULL);

    bus->drivers_autoprobe = 1;
    retval = add_probe_files(bus);
    if (retval)
        goto bus_probe_files_fail;

    retval = bus_add_attrs(bus);
    if (retval)
        goto bus_attrs_fail;

    pr_debug("bus type '%s' registered/n", bus->name);
    return 0;

bus_attrs_fail:
    remove_probe_files(bus);
bus_probe_files_fail:
    kset_unregister(&bus->drivers);
bus_drivers_fail:
    kset_unregister(&bus->devices);
bus_devices_fail:
    subsystem_unregister(&bus->subsys);
out:
    return retval;
}
关于这些结构以及他们之间的继承和连接关系,大家可以去参考下linux那些事儿(好像不是第一次说了吧?)

总线注册成功了以后,这里会产生一个内核线程其实就是通过kthread_create产生的:
static int serio_thread(void *nothing)
{
    do {
        serio_handle_event();
        wait_event_interruptible(serio_wait,
            kthread_should_stop() || !list_empty(&serio_event_list));
        try_to_freeze();
    } while (!kthread_should_stop());

    printk(KERN_DEBUG "serio: kseriod exiting/n");
    return 0;
}
这个线程要做的事情很简单,他就一直睡,等到serio_event_list不空或者该退出的时候就会醒来去做他该做的事.

static void serio_handle_event(void)
{
    struct serio_event *event;

    mutex_lock(&serio_mutex);

    /*
    * Note that we handle only one event here to give swsusp
    * a chance to freeze kseriod thread. Serio events should
    * be pretty rare so we are not concerned about taking
    * performance hit.
    */
    if ((event = serio_get_event())) {

        switch (event->type) {
            case SERIO_REGISTER_PORT:
                serio_add_port(event->object);
                break;

            case SERIO_RECONNECT_PORT:
                serio_reconnect_port(event->object);
                break;

            case SERIO_RESCAN_PORT:
                serio_disconnect_port(event->object);
                serio_find_driver(event->object);
                break;

            case SERIO_ATTACH_DRIVER:
                serio_attach_driver(event->object);
                break;

            default:
                break;
        }

        serio_remove_duplicate_events(event);
        serio_free_event(event);
    }

    mutex_unlock(&serio_mutex);
}
看到这里面宏事件的时候,你会不会想起我们在前面说__serio_register_driver的时候已经提到他了?对,如果 serio_driver的manual_bind是0的话(这个字段的字面意思就是手动绑定),我们就会调用serio_queue_event (drv, NULL, SERIO_ATTACH_DRIVER)在事件队列里放入一个事件:

static int serio_queue_event(void *object, struct module *owner,
                 enum serio_event_type event_type)
{
    unsigned long flags;
    struct serio_event *event;
    int retval = 0;

    spin_lock_irqsave(&serio_event_lock, flags);

    /*
    * Scan event list for the other events for the same serio port,
    * starting with the most recent one. If event is the same we
    * do not need add new one. If event is of different type we
    * need to add this event and should not look further because
    * we need to preseve sequence of distinct events.
    */
//这里对链表的遍历是倒序进行的,注释已经说的很清楚了
    list_for_each_entry_reverse(event, &serio_event_list, node) {
        if (event->object == object) {
            if (event->type == event_type)
                goto out;
            break;
        }
    }
//申请空间
    event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
    if (!event) {
        printk(KERN_ERR
            "serio: Not enough memory to queue event %d/n",
            event_type);
        retval = -ENOMEM;
        goto out;
    }
//模块计数
    if (!try_module_get(owner)) {
        printk(KERN_WARNING
            "serio: Can't get module reference, dropping event %d/n",
            event_type);
        kfree(event);
        retval = -EINVAL;
        goto out;
    }

    event->type = event_type;
    event->object = object;
    event->owner = owner;
//加入事件队列
    list_add_tail(&event->node, &serio_event_list);
//唤醒守护线程
    wake_up(&serio_wait);

out:
    spin_unlock_irqrestore(&serio_event_lock, flags);
    return retval;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值