免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1439 | 回复: 0
打印 上一主题 下一主题

linux输入子系统 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-29 14:48 |只看该作者 |倒序浏览
最近公司的键盘驱动出了点问题,便看起了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")       #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")
              return 0;
}
module_init(tty_init);
下面是 [color="#ff0000"] vty_init()位于drivers/char/vt.c[color="#ff0000"]:
int __init vty_init(void)
{
              vcs_init();
              …………
              …………
[color="#ff0000"]               [color="#ff0000"]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   [color="#ff0000"]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 bit & dev->bit) != id->bit) \
                            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 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 协议.
   [color="#008000"] 现代键盘的encoders:
[color="#0000ff"]Holtek: HT82K28A, HT82K628A, HT82K68A, HT82K68E?EMC: EM83050, EM83050H, EM83052H, EM83053H,?Intel: 8048, 8049
[color="#0000ff"]Motorola: 6868, 68HC11, 6805
[color="#0000ff"]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);
                              [color="#ff0000"] 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           [color="#ff0000"]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);
        [color="#ff0000"]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 =                 [color="#ff0000"]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)
                    [color="#ff0000"] 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;
//注册控制器驱动
           [color="#ff0000"]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;
          }
  [color="#ff0000"]          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       [color="#ff0000"]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) {
                      [color="#ff0000"]error = i8042_setup_aux();
                    if (error && error != -ENODEV && error != -EBUSY)
                              goto out_fail;
          }
          if (!i8042_nokbd) {
                     [color="#ff0000"]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 exists))
                           [color="#ff0000"]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;
            [color="#ff0000"]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)) {
                   [color="#ff0000"]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;
   [color="#ff0000"] 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 ' 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;
                     }
                 [color="#ff0000"]   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控制器驱动到我们的键盘驱动,然后是输入子系统,最后是我们的响应处理.
整个输入子系统的原理就是这样,当你对整个框架熟悉了以后,你会发现你也可以模仿着写一些驱动放进去了,反正那些基本的函数都做好了,你组织组织调用调用.地基都打好了,你想盖什么样的大楼,自己爱咋整就咋整去吧.
   
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/49742/showart_1914216.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP