Android 输入系统架构 笔记4

来源:互联网 发布:lol徐老师淘宝店 编辑:程序博客网 时间:2024/06/05 02:02

下面看下Linux 下 input 驱动的架构,以具体例子分析下:

下面的文章是基于mini2440的gpio按键来讲解input子系统。以mini2440为例用该板的bsp文件进行input子系统的讲解.所用的版本为android4.0.先来看下板级支持文件都注册了那些资源。

下面是五个按键的资源 #define KEY_POWER           116  /* SC System Power Down */ #define KEY_F1                   59 #define KEY_F2                   60 #define KEY_F3                   61 #define KEY_F5                   63 struct gpio_keys_button {        /* Configuration parameters */        unsigned int code;  /* input event code (KEY_*, SW_*) *///上报事件的code        int gpio;//所用的gpio引脚        int active_low;//是否低电平有效        const char *desc; //该按键的描述符        unsigned int type;   /* input event type (EV_KEY, EV_SW, EV_ABS) */        int wakeup;            /* configure the button as a wake-up source */        int debounce_interval;    /* debounce ticks interval in msecs */        bool can_disable;        int value;        /* axis value for EV_ABS */ }; static struct gpio_keys_button mini2440_buttons[] = {        {               .gpio              = S3C2410_GPG(0),            /* K1 */               .code             = KEY_F1,               .desc              = "Button 1",               .active_low     = 1,        },        {               .gpio              = S3C2410_GPG(3),            /* K2 */               .code             = KEY_F2,               .desc              = "Button 2",               .active_low     = 1,        },    {               .gpio              = S3C2410_GPG(5),            /* K3 */               .code             = KEY_F3,               .desc              = "Button 3",               .active_low     = 1,        },        {               .gpio              = S3C2410_GPG(6),            /* K4 */               .code             = KEY_POWER,               .desc              = "Power",               .active_low     = 1,        },        {               .gpio              = S3C2410_GPG(7),            /* K5 */               .code             = KEY_F5,               .desc              = "Button 5",               .active_low     = 1,        }, }; 
/*下面是平台数据的声明*/ struct gpio_keys_platform_data {        struct gpio_keys_button *buttons;        int nbuttons;        unsigned int poll_interval;      /* polling interval in msecs -                                       for polling driver only */        unsigned int rep:1;         /* enable input subsystem auto repeat */        int (*enable)(struct device *dev);        void (*disable)(struct device *dev);        const char *name;         /* input device name */ }; static struct gpio_keys_platform_data mini2440_button_data = {        .buttons   = mini2440_buttons,        .nbuttons = ARRAY_SIZE(mini2440_buttons), };struct platform_device {        const char      * name;        int           id;        struct device   dev;        u32         num_resources;        struct resource       * resource;          const struct platform_device_id    *id_entry;          /* MFD cell pointer */        struct mfd_cell *mfd_cell;          /* arch specific additions */        struct pdev_archdata     archdata; }; static struct platform_device mini2440_button_device = {        .name             = "gpio-keys",        .id           = -1,        .dev        = {               .platform_data = &mini2440_button_data,        } };   static struct platform_device *mini2440_devices[] __initdata = {        ................... &mini2440_button_device,        ……… };   static void __init mini2440_init(void) {        .............................        platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));     …………… } MACHINE_START(MINI2440, "MINI2440")        /* Maintainer: Michel Pollet <buserror@gmail.com> */        .boot_params  = S3C2410_SDRAM_PA + 0x100,        .map_io          = mini2440_map_io,        .init_machine  = mini2440_init,        .init_irq   = s3c24xx_init_irq,        .timer             = &s3c24xx_timer, MACHINE_END 

上面是把该设备注册到平台总线上。


下面看下平台驱动的注册 static struct platform_driver gpio_keys_device_driver = {        .probe            = gpio_keys_probe,        .remove          = __devexit_p(gpio_keys_remove),        .driver            = {               .name      = "gpio-keys",               .owner    = THIS_MODULE,        } };   static int __init gpio_keys_init(void) {        return platform_driver_register(&gpio_keys_device_driver); }   module_init(gpio_keys_init); 


在注册平台驱动时如果成功匹配平台设备后会调用平台驱动的probe函数。 

下面看下该驱动的probe函数。

static int __devinit gpio_keys_probe(struct platform_device *pdev) {        /*取出在BSP文件注册的平台数据*/        struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;     /*这里出现了一个新的结构体该结构体定义如下*/ /*struct gpio_keys_drvdata {        struct input_dev *input;        struct mutex disable_lock;        unsigned int n_buttons;        int (*enable)(struct device *dev);        void (*disable)(struct device *dev);        struct gpio_button_data data[0]; };*/        struct gpio_keys_drvdata *ddata;        struct device *dev = &pdev->dev;          struct input_dev *input;          /*分配gpio_keys_drvdata结构体内存*/        ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +                      pdata->nbuttons * sizeof(struct gpio_button_data),                      GFP_KERNEL);          /*分配一个input结构体并初始化部分成员*/        input = input_allocate_device();                /*为ddata的各个成员变量赋值*/        ddata->input = input;        ddata->n_buttons = pdata->nbuttons;        mutex_init(&ddata->disable_lock);          /*把ddata设备pdev平台设备的driver data*/        platform_set_drvdata(pdev, ddata);                /*把ddata设备input设备的driver data*/        input_set_drvdata(input, ddata);          /*设置input设备的各个成员变量*/        input->phys = "gpio-keys/input0";     input->dev.parent = &pdev->dev;        input->open = gpio_keys_open;        input->close = gpio_keys_close;          input->id.bustype = BUS_HOST;        input->id.vendor = 0x0001;        input->id.product = 0x0001;        input->id.version = 0x0100;          /* 根据pdata的rep成员值设备input子系统的功能*/        if (pdata->rep)               __set_bit(EV_REP, input->evbit);          /*取出pdata中得资源进行赋值*/        for (i = 0; i < pdata->nbuttons; i++) {               struct gpio_keys_button *button = &pdata->buttons[i];               struct gpio_button_data *bdata = &ddata->data[i];                 /*为三目运算符相当于button->type ?: button->type:EV_KEY;*/               unsigned int type = button->type ?: EV_KEY;                 bdata->input = input;//               bdata->button = button;                 error = gpio_keys_setup_key(pdev, bdata, button);                if (button->wakeup)//该键能否作为唤醒源                      wakeup = 1;                 input_set_capability(input, type, button->code);        }          }          error = input_register_device(input);     /* get current state of buttons */        for (i = 0; i < pdata->nbuttons; i++)               gpio_keys_report_event(&ddata->data[i]);        input_sync(input);          device_init_wakeup(&pdev->dev, wakeup);          return 0; } 
第一个分配一个input dev并进行初始化 struct input_dev *input_allocate_device(void) {        struct input_dev *dev;          dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);        if (dev) {               dev->dev.type = &input_dev_type;               dev->dev.class = &input_class;               device_initialize(&dev->dev);               mutex_init(&dev->mutex);               spin_lock_init(&dev->event_lock);               INIT_LIST_HEAD(&dev->h_list);               INIT_LIST_HEAD(&dev->node);        }        return dev; } 分析第二个 static int __devinit gpio_keys_setup_key(struct platform_device *pdev,                                     struct gpio_button_data *bdata,                                     struct gpio_keys_button *button) {        /*取出按键的描述符*/        const char *desc = button->desc ? button->desc : "gpio_keys";     struct device *dev = &pdev->dev;          /*设置该bdata的定时器函数*/        setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);                /*设置该bdata的work函数*/        INIT_WORK(&bdata->work, gpio_keys_work_func);          /*申请button的gpio*/        error = gpio_request(button->gpio, desc);                /*设置gpio的方向*/        error = gpio_direction_input(button->gpio);          if (button->debounce_interval) { //设置gpio的去抖间隔               error = gpio_set_debounce(button->gpio,                                      button->debounce_interval * 1000);               /* use timer if gpiolib doesn't provide debounce */               if (error < 0)                      bdata->timer_debounce = button->debounce_interval;        }          irq = gpio_to_irq(button->gpio); //该gpio引脚对应分配的中断          irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;        if (!button->can_disable)               irqflags |= IRQF_SHARED;          /*注册该irq的中断处理函数并设置标记*/        error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata); 

其中中断处理函数如下 static irqreturn_t gpio_keys_isr(int irq, void *dev_id) {        struct gpio_button_data *bdata = dev_id;        struct gpio_keys_button *button = bdata->button;    BUG_ON(irq != gpio_to_irq(button->gpio));          if (bdata->timer_debounce)//如果有去抖间隔则修改定时器               mod_timer(&bdata->timer,                      jiffies + msecs_to_jiffies(bdata->timer_debounce));        else               schedule_work(&bdata->work);//如果没有直接执行work        return IRQ_HANDLED; } } 


如果定时器到期则执行定时器处理函数 static void gpio_keys_timer(unsigned long _data) {        struct gpio_button_data *data = (struct gpio_button_data *)_data;        schedule_work(&data->work);//执行相应的work } 中断处理的结果是执行相应的work。看下work函数 static void gpio_keys_work_func(struct work_struct *work) {        struct gpio_button_data *bdata =               container_of(work, struct gpio_button_data, work);          gpio_keys_report_event(bdata);//用input子系统向上层报事件 } 第三个函数设置该input dev的能力记录本设备对那些事件感兴趣 void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) {        switch (type) {        case EV_KEY:               __set_bit(code, dev->keybit);// 比如按键,应该对哪些键值的按键进行处理(对于其它按键不予理睬)               break;          __set_bit(type, dev->evbit); } 第四个函数向input核心注册input设备 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;          /* Every input device generates EV_SYN/SYN_REPORT events. */        __set_bit(EV_SYN, dev->evbit); //设置支持的能力          /* KEY_RESERVED is not supposed to be transmitted to userspace. */        __clear_bit(KEY_RESERVED, dev->keybit);//清除该支持的能力          /* Make sure that bitmasks not mentioned in dev->evbit are clean. */        input_cleanse_bitmasks(dev);//确保在dev->evbit中没有支持的能力被清除掉          if (!dev->hint_events_per_packet)               dev->hint_events_per_packet = input_estimate_events_per_packet(dev);          /*         * If delay and period are pre-set by the driver, then autorepeating         * is handled by the driver itself and we don't do it in input.c.         */        init_timer(&dev->timer);        if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {               dev->timer.data = (long) dev;               dev->timer.function = input_repeat_key;               dev->rep[REP_DELAY] = 250;               dev->rep[REP_PERIOD] = 33;        }          /*设置input dev成员变量的处理函数*/        if (!dev->getkeycode)               dev->getkeycode = input_default_getkeycode;   if (!dev->setkeycode)               dev->setkeycode = input_default_setkeycode;          /*设置该dev name*/        dev_set_name(&dev->dev, "input%ld",                    (unsigned long) atomic_inc_return(&input_no) - 1);          error = device_add(&dev->dev);//把该设备增加到设备驱动模型中          /*把该dev加入到input_dev_list 链表*/        list_add_tail(&dev->node, &input_dev_list);          /*遍历input_hander_list链表中得hander以便匹配input dev*/        list_for_each_entry(handler, &input_handler_list, node)               input_attach_handler(dev, handler);          return 0; } 下面看下匹配函数 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) {        const struct input_device_id *id;        id = input_match_device(handler, dev);//返回匹配成功的id        error = handler->connect(handler, dev, id);//如果匹配成功则调用hander的connect函数        return error; } 下面主要看下match的过程 static const struct input_device_id *input_match_device(struct input_handler *handler,                                                  struct input_dev *dev) {        const struct input_device_id *id;          for (id = handler->id_table; id->flags || id->driver_info; id++) {               if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)//如果是匹配bus则比较id.bus   if (id->bustype != dev->id.bustype)                             continue;   if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //如果是匹配vender则比较id.vender                      if (id->vendor != dev->id.vendor)                             continue;   //如果是匹配product则比较id.product               if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)                      if (id->product != dev->id.product)                             continue;   //如果是匹配versiont则比较id.version               if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)                      if (id->version != dev->id.version)                             continue;   /*如果hander支持该能力则dev也要支持否则不匹配*/ 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); 下面看下这个宏 #define MATCH_BIT(bit, max) \               for (i = 0; i < BITS_TO_LONGS(max); i++) \                      if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \                             break; \               if (i != BITS_TO_LONGS(max)) \                      continue;  /*如果hander的match空则返回该id或者调用match继续匹配匹配成员的话也返回id*/               if (!handler->match || handler->match(handler, dev))                      return id;         }          return NULL; } 上面input dev已经注册完了下面看看hander的注册. static const struct input_device_id evdev_ids[] = {        { .driver_info = 1 },      /* Matches all devices 来则不拒公交车*/        { },                /* Terminating zero entry */ };   MODULE_DEVICE_TABLE(input, evdev_ids);   static struct input_handler evdev_handler = {        .event             = evdev_event,        .connect  = evdev_connect,        .disconnect     = evdev_disconnect,        .fops              = &evdev_fops,        .minor            = EVDEV_MINOR_BASE,        .name             = "evdev",        .id_table  = evdev_ids,//匹配的列表 };   static int __init evdev_init(void) {        return input_register_handler(&evdev_handler); } module_init(evdev_init); 下面看下hander的注册 static struct input_handler *input_table[8]; int input_register_handler(struct input_handler *handler) {        struct input_dev *dev;     int retval;          INIT_LIST_HEAD(&handler->h_list);          if (handler->fops != NULL) {               if (input_table[handler->minor >> 5]) {//判断input_table的相应项是否被占用                      retval = -EBUSY;                      goto out;               }               input_table[handler->minor >> 5] = handler; // 如果没有占用则把hander填入        }   /*把要注册的hander加入input_handler_list链表中*/        list_add_tail(&handler->node, &input_handler_list);           /*遍历input_dev_list链表上得每一个dev去匹配该hander*/        list_for_each_entry(dev, &input_dev_list, node)               input_attach_handler(dev, handler);//开始进行匹配 } 匹配成功后返回匹配成功的id然后调用该handler的connect函数。 static struct evdev *evdev_table[EVDEV_MINORS]; //evdev的容器 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,                       const struct input_device_id *id) {        struct evdev *evdev;        int minor;          /*在容器中找个空闲的地方*/        for (minor = 0; minor < EVDEV_MINORS; minor++)               if (!evdev_table[minor])                      break;        /*分配一个evdev变量*/        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);                /*初始化该evdev的成员变量*/    INIT_LIST_HEAD(&evdev->client_list);        spin_lock_init(&evdev->client_lock);        mutex_init(&evdev->mutex);        init_waitqueue_head(&evdev->wait);        dev_set_name(&evdev->dev, "event%d", minor);        evdev->exist = true;        evdev->minor = minor;         /*初始化该evdev的成员变量handlehandle相当于是红娘连接input dev和相应的hander*/        evdev->handle.dev = input_get_device(dev);//增加该dev的引用计数        evdev->handle.name = dev_name(&evdev->dev);//设置该evdev的name        evdev->handle.handler = handler;        evdev->handle.private = evdev;//设置hander的私有数据,这个在下面会用到          /*初始化该evdev的成员变量dev*/        evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);        evdev->dev.class = &input_class;        evdev->dev.parent = &dev->dev;        evdev->dev.release = evdev_free;        device_initialize(&evdev->dev);          /*注册上面初始化好的handle*/        error = input_register_handle(&evdev->handle);                /*安装evdev其实就是放到全局的evdev_table 数组中*/        error = evdev_install_chrdev(evdev); 该函数如下 static int evdev_install_chrdev(struct evdev *evdev) {        evdev_table[evdev->minor] = evdev;        return 0; }   /*把该evdev设备增加到设备驱动模型中*/        error = device_add(&evdev->dev);    return 0; } 下面主要看input_register_handle干了啥活 int input_register_handle(struct input_handle *handle) {        struct input_handler *handler = handle->handler;        struct input_dev *dev = handle->dev;        list_add_tail_rcu(&handle->d_node, &dev->h_list);//加入到dev hist链表的末尾        list_add_tail_rcu(&handle->h_node, &handler->h_list);//加入到hander的hist尾部        return 0; 注册的过程也就是把该handle加入dev和hander的链表中 } 

上面input dev和handler用网上的一个图可以表示 
 
该图形象的描述了三者的关系. 



该搭的关系已经搞好啦下面就是要用啦,用的时候看三者是怎么配合的。下面看现在中断处理中是如何用的上面有说过在中断发生后会调用work,在work中去处理上报键值,上报函数如下:

static void gpio_keys_report_event(struct gpio_button_data *bdata) {        struct gpio_keys_button *button = bdata->button;//取出每一个键的结构体        struct input_dev *input = bdata->input;        //把该键的input设备也取出来        unsigned int type = button->type ?: EV_KEY;   //类型为key        int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;          input_event(input, type, button->code, !!state);        input_sync(input); } 继续分析 void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) {        if (is_event_supported(type, dev->evbit, EV_MAX)) {//判断该事件是否被支持               ……….               input_handle_event(dev, type, code, value);               ..................        } } 

下面继续跟踪

static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) {        int disposition = INPUT_IGNORE_EVENT;          switch (type) {          case EV_KEY:               if (is_event_supported(code, dev->keybit, KEY_MAX) &&  /判断该code是否被支持                   !!test_bit(code, dev->key) != value) {                        if (value != 2) {                             __change_bit(code, dev->key); if (value)                                    input_start_autorepeat(dev, code);                             else                                    input_stop_autorepeat(dev);                      }                        disposition = INPUT_PASS_TO_HANDLERS;               }               break;        }          if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)               dev->sync = false;          if (disposition & INPUT_PASS_TO_HANDLERS)               input_pass_event(dev, type, code, value); } 

跟踪继续

static void input_pass_event(struct input_dev *dev,                           unsigned int type, unsigned int code, int value) {        struct input_handler *handler;        struct input_handle *handle;                 list_for_each_entry_rcu(handle, &dev->h_list, d_node) {                      if (!handle->open)//如果该handle没有被打开则找下一个handle                             continue;                        handler = handle->handler;                      if (!handler->filter) {                             handler->event(handle, type, code, value);//调用handler的event函数                      }               } } 

跟踪该函数static void evdev_event(struct input_handle *handle,                      unsigned int type, unsigned int code, int value) {        struct evdev *evdev = handle->private;//这个在上面已经设置过          struct evdev_client *client;        struct input_event event;//要上报的事件结构体变量        struct timespec ts;                /*填充event要上报的事件结构体*/        /*该事件发生的时间*/        ktime_get_ts(&ts);        event.time.tv_sec = ts.tv_sec;        event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;          /*赋值传进来的参数*/        event.type = type;        event.code = code;        event.value = value;                /*遍历evdev的client链表*/        list_for_each_entry_rcu(client, &evdev->client_list, node)               evdev_pass_event(client, &event); } 下面看传递函数 static void evdev_pass_event(struct evdev_client *client,struct input_event *event) {        client->buffer[client->head++] = *event;//把传递的事件赋值给client的buffer中        client->head &= client->bufsize - 1;//管理循环缓冲区 }   

******************************************************************************* 
下面看下提供给上层的接口,下面看下hander的evdev_fops函数操作结构体
看下具体的实现:


static const struct file_operations evdev_fops = {        .owner           = THIS_MODULE,        .read              = evdev_read,        .write             = evdev_write,        .poll        = evdev_poll,        .open             = evdev_open,        .release    = evdev_release,        .unlocked_ioctl       = evdev_ioctl,        .fasync           = evdev_fasync,        .flush             = evdev_flush,        .llseek            = no_llseek, }; 下面具体看下open函数 static int evdev_open(struct inode *inode, struct file *file) {        struct evdev *evdev;        struct evdev_client *client;        int i = iminor(inode) - EVDEV_MINOR_BASE;        unsigned int bufsize;          evdev = evdev_table[i];//根据inode的值取出evdev的值                /*为client分配缓存*/        bufsize = evdev_compute_buffer_size(evdev->handle.dev);          /*为每一个打开实例分配一个client结构体*/        client = kzalloc(sizeof(struct evdev_client) +                             bufsize * sizeof(struct input_event),                       GFP_KERNEL);          /*初始化client的各个成员变量*/        client->bufsize = bufsize;        spin_lock_init(&client->buffer_lock);        snprintf(client->name, sizeof(client->name), "%s-%d",                      dev_name(&evdev->dev), task_tgid_vnr(current));  client->evdev = evdev;//和该client关联的evdev       evdev_attach_client(evdev, client); //把该client加入该evdev的client链表中 下面是该函数的实现 static void evdev_attach_client(struct evdev *evdev,struct evdev_client *client) { //把该client加入该evdev的client链表中 list_add_tail_rcu(&client->node, &evdev->client_list); }          error = evdev_open_device(evdev);        file->private_data = client;//把该client挂载到file的私有结构体下        nonseekable_open(inode, file);          return 0; } 下面接着看evdev_open_device(evdev);函数 static int evdev_open_device(struct evdev *evdev) {        int retval;          if (!evdev->exist)//在connenct的时候就被设置为true               retval = -ENODEV;        else if (!evdev->open++) {               retval = input_open_device(&evdev->handle);               if (retval)                      evdev->open--;        }        return retval; } 

看下最后一个函数 int input_open_device(struct input_handle *handle) {        struct input_dev *dev = handle->dev;        int retval;     handle->open++;//打开的计数器加1        if (!dev->users++ && dev->open)               retval = dev->open(dev);//该函数为空        return retval; } 下一个目标是分析read函数 static ssize_t evdev_read(struct file *file, char __user *buffer,                        size_t count, loff_t *ppos) {        struct evdev_client *client = file->private_data;        struct evdev *evdev = client->evdev;        struct input_event event;          while (retval + input_event_size() <= count &&evdev_fetch_next_event(client, &event)) {               if (input_event_to_user(buffer + retval, &event))                      return -EFAULT;               retval += input_event_size();        }        return retval; } 看下里面的两个细节 static int evdev_fetch_next_event(struct evdev_client *client,struct input_event *event) {        int have_event;        have_event = client->packet_head != client->tail;        if (have_event) {               *event = client->buffer[client->tail++];               client->tail &= client->bufsize - 1;        }        return have_event; } /**/ int input_event_to_user(char __user *buffer,const struct input_event *event) {  if (copy_to_user(buffer, event, sizeof(struct input_event)))               return -EFAULT;        return 0; } 

*************************************************************************** 下面再看看input class是咋回事? static const struct file_operations input_fops = {        .owner = THIS_MODULE,        .open = input_open_file,        .llseek = noop_llseek, };   struct class input_class = {        .name             = "input",        .devnode = input_devnode, }; static int __init input_init(void) {        /*注册input class*/        err = class_register(&input_class);          /*注册input dev字符设备其中#define INPUT_MAJOR 13*/        err = register_chrdev(INPUT_MAJOR, "input", &input_fops);          return 0; } subsys_initcall(input_init); *************************************************************************** 看input_fops 中的input_open_file static int input_open_file(struct inode *inode, struct file *file) {        struct input_handler *handler;        const struct file_operations *old_fops, *new_fops = NULL;   handler = input_table[iminor(inode) >> 5];//根据上层传入的inode结构体找到对应的handler  if (handler)//如果handler不为空则取出该handler的fops指针赋值给新的new_fops               new_fops = fops_get(handler->fops);           old_fops = file->f_op;//备份老的        file->f_op = new_fops;//赋值新的以后的对该设备的操作都会映射到该操作结构体          err = new_fops->open(inode, file);//调用新的open函数        if (err) {如果出错则回退               fops_put(file->f_op);               file->f_op = fops_get(old_fops);        } } 



0 0