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————————————