Linux 3.10.x input subsystem process analysis < 1 >

Character device registration method 1:

  1. Determine the main equipment number major
  2. Constructing file_operations   
                .open
                .read
                .write
  3. Register character device register_chrdev()
  4. Entry function
  5. Exit function

Character device registration method 2:

  1,Determine the number of main equipment major
  2,structure file_operations   
            .open
            .read
            .warite
  3,Registered Character Device register_chrdev_region();
                cdev_init();
                dev_add();
  4,Exit function
  5,Entry function

When we analyze the subsystems of the kernel input, we find that the second method is adopted.

APP:
read -> …->file->f_op ->read.
The following analysis starts with app read and calls the process as follows
seq_file.c:

App read - > seq_read - > seq_putc (using copy_to_user() function to communicate with the kernel)

input.c:

file_operations input_handlers_fileops(user Call interface with the kernel) > input_proc_devices_open > seq_operations input_devices_seq_ops > input_devices_seq_show() > seq_putc() >input_proc_init >

seq_file.c

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
 {
                struct seq_file *m = file->private_data;
            mutex_lock(&m->lock);
            /* if not empty - flush it first */
            if (m->count) {
                n = min(m->count, size);
                err = copy_to_user(buf, m->buf + m->from, n);
                m->count -= n;
                m->from += n;
                size -= n;
                buf += n;
                copied += n;
                if (!m->count)
                    m->index++;
                if (!size)
                    goto Done;
            }
}


int seq_putc(struct seq_file *m, char c)
{
        if (m->count < m->size) {
            m->buf[m->count++] = c;
            return 0;
        }
        return -1;
}

input.c

static int input_devices_seq_show(struct seq_file *seq, void *v)
{
    struct input_dev *dev = container_of(v, struct input_dev, node);
    const char *path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    struct input_handle *handle;

    seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
           dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);

    list_for_each_entry(handle, &dev->h_list, d_node)
        seq_printf(seq, "%s ", handle->name);
    seq_putc(seq, '\n');
}
static const struct seq_operations input_devices_seq_ops = {
    .start  = input_devices_seq_start,
    .next   = input_devices_seq_next,
    .stop   = input_seq_stop,
    .show   = input_devices_seq_show,
};

static int input_proc_devices_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &input_devices_seq_ops);
}
static const struct file_operations input_handlers_fileops = {
    .owner      = THIS_MODULE,
    .open       = input_proc_handlers_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = seq_release,
};
static int __init input_proc_init(void)
{
    struct proc_dir_entry *entry;
    proc_bus_input_dir = proc_mkdir("bus/input", NULL);
    entry = proc_create("devices", 0, proc_bus_input_dir,
                &input_devices_fileops);
    entry = proc_create("handlers", 0, proc_bus_input_dir,
                &input_handlers_fileops);
}

static int __init input_init(void)
{
       int err;
       err = class_register(&input_class);//Register the input class
       err = input_proc_init();
       err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                                 INPUT_MAX_CHAR_DEVICES, "input");
       return 0;
}

char_dev.c
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    dev_t to = from + count;
    dev_t n, next;
    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        if (next > to)
            next = to;
        cd = __register_chrdev_region(MAJOR(n), MINOR(n),
                   next - n, name);
    }
    return 0;
}

app read -> file_operations input_handlers_fileops -> seq_read ->input_register_handler();

/**
 * Register a new input_handler connection in the input device system
 */
int input_register_handler(struct input_handler *handler)
{
    struct input_dev *dev;
    int error;

    error = mutex_lock_interruptible(&input_mutex);
    INIT_LIST_HEAD(&handler->h_list);
    list_add_tail(&handler->node, &input_handler_list);
    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);
    mutex_unlock(&input_mutex);
    return 0;
}
 //Register input device to Input core
// @dev: device to be registered
int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_devres *devres = NULL;
    struct input_handler *handler;

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);

    dev_set_name(&dev->dev, "input%ld",
             (unsigned long) atomic_inc_return(&input_no) - 1);

    error = device_add(&dev->dev)
        //Put input device in the list
    list_add_tail(&dev->node, &input_dev_list);

    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

    return 0;

}
   Since input_register_handler function and input_register_device function both use input_attach_handler, the function of input_attach_handler is to judge whether the input_handler can support the corresponding input_dev according to the id_table of input_handler, and if so, to establish a connection, which is why we often say that device_name and driver_name must be consistent in order to enter the probe() function.
// Determine whether this input_dev can be supported based on the id_table of input_handler
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;
  ···
    id = input_match_device(handler, dev);
    error = handler->connect(handler, dev, id);
}

———————— After the analysis of input.c, the following is the use of input_register_handler

input_register_handler The following files are used:
drivers/tty/sysrq.c
drivers/tty/vt/keyboard.c
drivers/input/evdev.c
drivers/input/evbug.c
drivers/input/mousedev.c
drivers/input/joydev.c:947
drivers/input/misc/keychord.c

drivers/input/evdev.c are analyzed as follows

static const struct input_device_id evdev_ids[] = {
    { .driver_info = 1 },   /* Matches all devices */
    { },            /* Terminating zero entry */
};
static struct input_handler evdev_handler = {
    .event      = evdev_event,
    .events     = evdev_events,
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .legacy_minors  = true,
    .minor      = EVDEV_MINOR_BASE,
    .name       = "evdev",
    .id_table   = evdev_ids,
};
//The entry function registers evdev_handler.
static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
    input_unregister_handler(&evdev_handler);
}

//EstablisheventEvent file_operations 
static const struct file_operations  evdev_fops = {
    .owner      = THIS_MODULE,
    .read       = evdev_read,
    .write      = evdev_write,
    .open       = evdev_open,
    .release    = evdev_release,
    .unlocked_ioctl = evdev_ioctl,
       ...
};

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    int minor;
    int dev_no;
    int error;
       ...
    dev_set_name(&evdev->dev, "event%d", dev_no); //Name the newly created event

    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;

    evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

        //Register the above initialized handle s into Input core
        //The second method is character device creation.
    error = input_register_handle(&evdev->handle);
    cdev_init(&evdev->cdev, &evdev_fops);//Initialization structure file_operations evdev_fops 
    evdev->cdev.kobj.parent = &evdev->dev.kobj;
    error = cdev_add(&evdev->cdev, evdev->dev.devt, 1); //Tell the kernel evdev_fops
    error = device_add(&evdev->dev); //Add device to sys/class/input/create/dev/input/eventxx device
    return 0;
}

————————- To be continued————————————

Posted by hammerslane on Sun, 10 Feb 2019 18:33:18 -0800