文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>linux输入子系统

linux输入子系统

时间:2009-04-29  来源:gpephone

最近公司的键盘驱动出了点问题,便看起了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;
}

第四部分
该来的终究要来,该说的还是要说.
对一个完整的键盘来说,是不是感觉还少了点什么?我们的中断呢?我们的扫描码从哪里出来的?我们程序难道是一个空中楼阁,然后那些乱糟糟的扫描码就自动出来了?
你以为linux操作系统是智能的啊?你以为同样的硬件跑了一个开源的os就神奇了啊?你以为你拿linus做偶像时髦啊?你以为......
哥们,别激动,我要说的就是,你的怀疑是正确的,那些更低级,更脏,更晦涩的活还是要由程序员一点一点做的,不是像孙猴子那样从石头里蹦出来的.
我们在二里面介绍了一些键盘的背景,里面有提到i8042芯片.
还记得那句话么:我们所做的一切都是在为未来做准备!
我们在三里面说serio结构体的时候已经看过了Kconfig的内容,知道里面这么说:
对于config SERIO_I8042
    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.
而相应的Makefile里
obj-$(CONFIG_SERIO_I8042)    += i8042.o
OK,目标很简单:i8042.c
先来初始化:
static int __init i8042_init(void)
{
    int err;

    dbg_init();
//这里,主要是将i8042_reset全局变量置1
    err = i8042_platform_init();
    if (err)
        return err;
//清空控制器的缓冲区
    err = i8042_controller_check();
    if (err)
        goto err_platform_exit;
//注册控制器驱动
    err = platform_driver_register(&i8042_driver);
    if (err)
        goto err_platform_exit;
//分配一个platform_device并注册
    i8042_platform_device = platform_device_alloc("i8042", -1);
    if (!i8042_platform_device) {
        err = -ENOMEM;
        goto err_unregister_driver;
    }

    err = platform_device_add(i8042_platform_device);
    if (err)
        goto err_free_device;

    panic_blink = i8042_panic_blink;

    return 0;

err_free_device:
    platform_device_put(i8042_platform_device);
err_unregister_driver:
    platform_driver_unregister(&i8042_driver);
err_platform_exit:
    i8042_platform_exit();

    return err;
}

static struct platform_driver i8042_driver = {
    .driver        = {
        .name    = "i8042",
        .owner    = THIS_MODULE,
    },
    .probe        = i8042_probe,
    .remove        = __devexit_p(i8042_remove),
    .shutdown    = i8042_shutdown,
#ifdef CONFIG_PM
    .suspend    = i8042_suspend,
    .resume        = i8042_resume,
#endif
};

int platform_driver_register(struct platform_driver *drv)
{
//驱动的总线类型为platform_bus_type
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;
    if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;
//在这里我们又看到了熟悉的driver_register
    return driver_register(&drv->driver);
}
只不过这条路走下去并不会有什么结果,因为现在platform_bus_type上挂的还没有能和此驱动配对的设备.
貌似有套路可循?恩...人类的进化史不就是一个不断发现规律并利用规律的历史么.
没有设备是吧,我们现在就整设备!
platform_device_add里会调用device_add来向总线注册设备,并完成设备的继承关系,因为里面调用流程比较长而且复杂,咱也不拿来充篇幅了,这年头,人要厚道...
注册了设备之后,就如同我们当年注册驱动一样,要遍历总线上的驱动那条线,找到合适的驱动之后(当然是调用bus里的match来看合不合适)会调用platform_drv_probe然后最终调用我们的i8042_driver里的probe函数.

static int __devinit i8042_probe(struct platform_device *dev)
{
    int error;
//做一些自测与初始化工作
    error = i8042_controller_selftest();
    if (error)
        return error;

    error = i8042_controller_init();
    if (error)
        return error;
//这里,2个看着差不多的部分,就是这个函数的核心了
    if (!i8042_noaux) {
        error = i8042_setup_aux();
        if (error && error != -ENODEV && error != -EBUSY)
            goto out_fail;
    }

    if (!i8042_nokbd) {
        error = i8042_setup_kbd();
        if (error)
            goto out_fail;
    }

/*
* Ok, everything is ready, let's register all serio ports
*/
    i8042_register_ports();

    return 0;

out_fail:
    i8042_free_aux_ports();    /* in case KBD failed but AUX not */
    i8042_free_irqs();
    i8042_controller_reset();

    return error;
}

i8042_setup_aux与i8042_setup_kbd性质与流程基本一样,一个是针对鼠标一个是针对我们键盘的:
static int __devinit i8042_setup_kbd(void)
{
    int error;

    error = i8042_create_kbd_port();
    if (error)
        return error;

    error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
                "i8042", i8042_platform_device);
    if (error)
        goto err_free_port;

    error = i8042_enable_kbd_port();
    if (error)
        goto err_free_irq;

    i8042_kbd_irq_registered = 1;
    return 0;

err_free_irq:
    free_irq(I8042_KBD_IRQ, i8042_platform_device);
err_free_port:
    i8042_free_kbd_port();
    return error;
}
什么?你没发现什么感兴趣的?我们千辛万苦要寻找的中断就是在这里注册的啊!
在注册驱动之前,我们针对键盘所属的控制器端口分配资源并做一些初始化,主要分配了struct i8042_port结构体中的struct serio结构并设置中断号.然后注册中断.
/*
* i8042_interrupt() is the most important function in this driver -
* it handles the interrupts from the i8042, and sends incoming bytes
* to the upper layers.
*/
//第一次也是仅有的一次,我把注释染红了,自己看看吧...
static irqreturn_t i8042_interrupt(int irq, void *dev_id)
{
    struct i8042_port *port;
    unsigned long flags;
    unsigned char str, data;
    unsigned int dfl;
    unsigned int port_no;
    int ret = 1;

    spin_lock_irqsave(&i8042_lock, flags);
    str = i8042_read_status();
//unlikely宏一般处理不太可能发生的事,也就是说中断来了却发现没有数据要接收,你不耍我么?直接推出!
//伤自尊了...
    if (unlikely(~str & I8042_STR_OBF)) {
        spin_unlock_irqrestore(&i8042_lock, flags);
        if (irq) dbg("Interrupt %d, without any data", irq);
        ret = 0;
        goto out;
    }
    data = i8042_read_data();
    spin_unlock_irqrestore(&i8042_lock, flags);
//这个分支是处理鼠标数据的...我们飘过可以么?
    if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
        static unsigned long last_transmit;
        static unsigned char last_str;

        dfl = 0;
        if (str & I8042_STR_MUXERR) {
            dbg("MUX error, status is %02x, data is %02x", str, data);
/*
* When MUXERR condition is signalled the data register can only contain
* 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
* it is not always the case. Some KBCs also report 0xfc when there is
* nothing connected to the port while others sometimes get confused which
* port the data came from and signal error leaving the data intact. They
* _do not_ revert to legacy mode (actually I've never seen KBC reverting
* to legacy mode yet, when we see one we'll add proper handling).
* Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the
* rest assume that the data came from the same serio last byte
* was transmitted (if transmission happened not too long ago).
*/

            switch (data) {
                default:
                    if (time_before(jiffies, last_transmit + HZ/10)) {
                        str = last_str;
                        break;
                    }
                    /* fall through - report timeout */
                case 0xfc:
                case 0xfd:
                case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
                case 0xff: dfl = SERIO_PARITY; data = 0xfe; break;
            }
        }

        port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3);
        last_str = str;
        last_transmit = jiffies;
    } else {
//dfl用来记录奇偶校验错误和超时2个状态
        dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
              ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
//port_no指明是键盘还是鼠标的端口号,这将作为控制器端口数组下标
        port_no = (str & I8042_STR_AUXDATA) ?
                I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
    }

    port = &i8042_ports[port_no];

    dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
        data, port_no, irq,
        dfl & SERIO_PARITY ? ", bad parity" : "",
        dfl & SERIO_TIMEOUT ? ", timeout" : "");

    if (unlikely(i8042_suppress_kbd_ack))
        if (port_no == I8042_KBD_PORT_NO &&
            (data == 0xfa || data == 0xfe)) {
            i8042_suppress_kbd_ack--;
            goto out;
        }
//在这里serio_interrupt被调用,进而调用struct serio_driver atkbd_drv中的atkbd_interrupt
    if (likely(port->exists))
        serio_interrupt(port->serio, data, dfl);

out:
    return IRQ_RETVAL(ret);
}
关于那个exists字段,是在i8042_register_ports里设置的.他调用了serio_register_port进而又调用 __serio_register_port往serio_event_list队列里放一个SERIO_REGISTER_PORT事件.这一操作唤醒 了serio_thread守护线程,在serio_handle_event里的SERIO_REGISTER_PORT事件分支调用 serio_add_port,进而调用serio->start(serio)来使得我们的i8042_start函数被成功回调:
static int i8042_start(struct serio *serio)
{
    struct i8042_port *port = serio->port_data;

    port->exists = 1;
    mb();
    return 0;
}
是不是非常坎坷?如果把这一串串的代码贴出来,不知道大家会不会想打瞌睡呢?
已经有朋友批评我的文章又臭又长了,没办法,咱不是想让大家伙搞得清楚一些么,没想到搞成了反作用.
其实简单点也好,在这里我帮大家整理一个框架出来,大家可以更好地理解内核,以免被我的一面之解误导.
就像交大人甲说的那样,其实,你只要看进去了,看代码就跟看故事会一样...
好了,我们还是来继续说我们的中断,看看数据怎么由最底层的控制器一直跑到keyboard handler里面去的

irqreturn_t serio_interrupt(struct serio *serio,
        unsigned char data, unsigned int dfl)
{
    unsigned long flags;
    irqreturn_t ret = IRQ_NONE;

    spin_lock_irqsave(&serio->lock, flags);

        if (likely(serio->drv)) {
                ret = serio->drv->interrupt(serio, data, dfl);
    } else if (!dfl && serio->registered) {
        serio_rescan(serio);
        ret = IRQ_HANDLED;
    }

    spin_unlock_irqrestore(&serio->lock, flags);

    return ret;
}
然后struct serio_driver atkbd_drv中的atkbd_interrupt被调用:

/*
* atkbd_interrupt(). Here takes place processing of data received from
* the keyboard into events.
*/

static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
            unsigned int flags)
{
    struct atkbd *atkbd = serio_get_drvdata(serio);
    struct input_dev *dev = atkbd->dev;
    unsigned int code = data;
    int scroll = 0, hscroll = 0, click = -1, add_release_event = 0;
    int value;
    unsigned char keycode;

#ifdef ATKBD_DEBUG
    printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
#endif

#if !defined(__i386__) && !defined (__x86_64__)
    if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
        printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
        serio_write(serio, ATKBD_CMD_RESEND);
        atkbd->resend = 1;
        goto out;
    }

    if (!flags && data == ATKBD_RET_ACK)
        atkbd->resend = 0;
#endif

    if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
        if (ps2_handle_ack(&atkbd->ps2dev, data))
            goto out;

    if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
        if (ps2_handle_response(&atkbd->ps2dev, data))
            goto out;

    if (!atkbd->enabled)
        goto out;

    input_event(dev, EV_MSC, MSC_RAW, code);

    if (atkbd->translated) {

        if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
            atkbd->release = code >> 7;
            code &= 0x7f;
        }

        if (!atkbd->emul)
            atkbd_calculate_xl_bit(atkbd, data);
    }

    switch (code) {
        case ATKBD_RET_BAT:
            atkbd->enabled = 0;
            serio_reconnect(atkbd->ps2dev.serio);
            goto out;
        case ATKBD_RET_EMUL0:
            atkbd->emul = 1;
            goto out;
        case ATKBD_RET_EMUL1:
            atkbd->emul = 2;
            goto out;
        case ATKBD_RET_RELEASE:
            atkbd->release = 1;
            goto out;
        case ATKBD_RET_ACK:
        case ATKBD_RET_NAK:
            if (printk_ratelimit())
                printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
                       "Some program might be trying access hardware directly.\n",
                       data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
            goto out;
        case ATKBD_RET_HANGEUL:
        case ATKBD_RET_HANJA:
            /*
            * These keys do not report release and thus need to be
            * flagged properly
            */
            add_release_event = 1;
            break;
        case ATKBD_RET_ERR:
            atkbd->err_count++;
#ifdef ATKBD_DEBUG
            printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
#endif
            goto out;
    }

    code = atkbd_compat_scancode(atkbd, code);

    if (atkbd->emul && --atkbd->emul)
        goto out;

    keycode = atkbd->keycode[code];

    if (keycode != ATKBD_KEY_NULL)
        input_event(dev, EV_MSC, MSC_SCAN, code);

    switch (keycode) {
        case ATKBD_KEY_NULL:
            break;
        case ATKBD_KEY_UNKNOWN:
            printk(KERN_WARNING
                   "atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n",
                   atkbd->release ? "released" : "pressed",
                   atkbd->translated ? "translated" : "raw",
                   atkbd->set, code, serio->phys);
            printk(KERN_WARNING
                   "atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
                   code & 0x80 ? "e0" : "", code & 0x7f);
            input_sync(dev);
            break;
        case ATKBD_SCR_1:
            scroll = 1 - atkbd->release * 2;
            break;
        case ATKBD_SCR_2:
            scroll = 2 - atkbd->release * 4;
            break;
        case ATKBD_SCR_4:
            scroll = 4 - atkbd->release * 8;
            break;
        case ATKBD_SCR_8:
            scroll = 8 - atkbd->release * 16;
            break;
        case ATKBD_SCR_CLICK:
            click = !atkbd->release;
            break;
        case ATKBD_SCR_LEFT:
            hscroll = -1;
            break;
        case ATKBD_SCR_RIGHT:
            hscroll = 1;
            break;
        default:
            if (atkbd->release) {
                value = 0;
                atkbd->last = 0;
            } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
                /* Workaround Toshiba laptop multiple keypress */
                value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
            } else {
                value = 1;
                atkbd->last = code;
                atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
            }

            input_event(dev, EV_KEY, keycode, value);
            input_sync(dev);

            if (value && add_release_event) {
                input_report_key(dev, keycode, 0);
                input_sync(dev);
            }
    }

    if (atkbd->scroll) {
        if (click != -1)
            input_report_key(dev, BTN_MIDDLE, click);
        input_report_rel(dev, REL_WHEEL, scroll);
        input_report_rel(dev, REL_HWHEEL, hscroll);
        input_sync(dev);
    }

    atkbd->release = 0;
out:
    return IRQ_HANDLED;
}
在这里,杂七杂八的事情被不同分支处理,然后调用input_event将将数据以事件的形式通知给输入子系统,然后在Input.c中根据事件的类型,将需要反馈给物理设备的事件通过调用物理设备的Event函数传给设备驱动处理.
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
    struct input_handle *handle;

    if (type > EV_MAX || !test_bit(type, dev->evbit))
        return;

    add_input_randomness(type, code, value);

    switch (type) {

   ……

        case EV_KEY:

            if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
                return;

            if (value == 2)
                break;

            change_bit(code, dev->key);

            if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
                dev->repeat_key = code;
                mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
            }

            break;

            ……
    }

    if (type != EV_SYN)
        dev->sync = 0;

    if (dev->grab)
        dev->grab->handler->event(dev->grab, type, code, value);
    else
        list_for_each_entry(handle, &dev->h_list, d_node)
            if (handle->open)
                handle->handler->event(handle, type, code, value);
}
首先根据不同的事件,对设备的事件对应的位进行检查并设置.对于我们的键盘可以看到,如果按键对应的位没有置位,我们就不予理睬.
最后会调用handle->handler->event(handle, type, code, value).
到了这里,就到了我们最初的那个 struct input_handler kbd_handler里面了,kbd_event在此被调用.
至此,我们的扫描码处理流程被我唠唠叨叨地说了一遍,从8042控制器驱动到我们的键盘驱动,然后是输入子系统,最后是我们的响应处理.
整个输入子系统的原理就是这样,当你对整个框架熟悉了以后,你会发现你也可以模仿着写一些驱动放进去了,反正那些基本的函数都做好了,你组织组织调用调用.地基都打好了,你想盖什么样的大楼,自己爱咋整就咋整去吧.
相关阅读 更多 +
排行榜 更多 +
摧毁大厦游戏

摧毁大厦游戏

飞行射击 下载
合并动物城手游版

合并动物城手游版

休闲益智 下载
哈士奇大冒险

哈士奇大冒险

休闲益智 下载