In this illustration I will be showing how bus_type, device_driver and device structures are linked. It also shows how the match and probe function of device driver is called for devices. I used Linux kernel 3.19 for making this simple program.
This program tries to
1. Register a bus.
2. Register a device_driver to that bus and
3. Add a device_driver device to the driver.
At the last step the match and probe functions of the device driver are called.
In the probe function I am creating a simple character device.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/major.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
dev_t my_devt;
char drv_name[] = "my_chr_drv";
struct class *my_class;
struct device *my_device;
struct device *my_chr_device;
char global_buf[20];
struct my_super_device
{
struct cdev my_cdev;
int count;
struct semaphore my_sem;
};
struct my_super_device *my_super;
ssize_t my_read (struct file *myfile, char __user *my_buf, size_t len, loff_t *off)
{
int ret;
//printk("len = %d\n",len);
ret = copy_to_user(my_buf, global_buf, len);
if(ret != 0)
printk("not able to copy\n");
printk("copied the data to user buffer\n");
return len;
}
ssize_t my_write (struct file *my_file, const char __user *buf, size_t len, loff_t *off)
{
int ret;
ret = copy_from_user(global_buf, buf, len);
if(ret != 0)
printk("not able to copy\n");
printk("copied the data from user buffer\n");
return len;
}
int my_mmap (struct file *my_file, struct vm_area_struct *my_vm)
{
return 0;
}
int my_open (struct inode *my_inode, struct file *my_file)
{
struct my_super_device *super_ptr;
printk("open called\n");
printk("cdev address = %p\n", my_inode->i_cdev);
super_ptr = container_of(my_inode->i_cdev, struct my_super_device, my_cdev);
up(&(super_ptr->my_sem));
printk("super address = %p\n", super_ptr);
return 0;
}
int my_release (struct inode *my_inode, struct file *my_file)
{
struct my_super_device *super_ptr;
printk("release called\n");
super_ptr = container_of(my_inode->i_cdev, struct my_super_device, my_cdev);
down(&(super_ptr->my_sem));
return 0;
}
unsigned int my_poll (struct file *my_file, struct poll_table_struct *poll_table)
{
return 0;
}
struct file_operations myfops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
.poll = my_poll,
.open = my_open,
.release = my_release,
};
int my_bus_match(struct device *dev, struct device_driver *drv)
{
printk("Entered %s\n",__func__);
return 1;
}
int my_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
printk("Entered %s\n",__func__);
return 0;
}
void my_bus_release(struct device *dev)
{
printk("Entered %s\n",__func__);
return ;
}
struct device my_bus = {
.init_name = "my_bus",
.release = my_bus_release,
};
struct bus_type my_bus_type = {
.name = "my_bus",
.match = my_bus_match,
.uevent = my_bus_uevent,
};
int my_driver_probe(struct device *dev)
{
int ret;
printk("Entered %s\n",__func__);
ret = alloc_chrdev_region(&my_devt, 0, 1, drv_name);
if(ret == 0)
printk("dev_t major is %d minor is %d\n", MAJOR(my_devt), MINOR(my_devt));
my_class = class_create(THIS_MODULE, "my_class");
if(my_class != NULL)
printk("class created as %s\n", my_class->name);
my_chr_device = device_create(my_class, NULL, my_devt, NULL, "my_chr_drv");
if(my_chr_device != NULL)
printk("device created dev_t as %d\n", my_chr_device->devt);
my_super = kzalloc(sizeof(struct my_super_device), GFP_KERNEL);
sema_init(&(my_super->my_sem), 1);
cdev_init(&(my_super->my_cdev), &myfops);
cdev_add(&(my_super->my_cdev), my_devt, 1);
return 0;
}
int my_driver_remove(struct device *dev)
{
printk("Entered %s\n",__func__);
if(my_chr_device != NULL)
device_destroy(my_class, my_devt);
if(my_class != NULL)
class_destroy(my_class);
if(my_devt != 0)
unregister_chrdev_region(my_devt,1);
return 0;
}
struct device_driver my_dev_driver = {
.owner = THIS_MODULE,
.name = "my_chr_drv",
.bus = &my_bus_type,
.probe = my_driver_probe,
.remove = my_driver_remove,
};
static int __init my_init(void)
{
int ret = 0;
printk("in init\n");
/*Register the bus*/
ret = device_register(&my_bus);
if(ret == 0)
printk("device registerd correctly\n");
else
printk("ret = %d\n",ret);
ret = bus_register(&my_bus_type);
if(ret == 0)
printk("bus registered correctly\n");
else
printk("ret = %d\n",ret);
/*add device for the device driver*/
my_device = kzalloc(sizeof(struct device), GFP_KERNEL);
device_initialize(my_device);
my_device->parent = &my_bus;
my_device->bus = &my_bus_type;
my_device->init_name = "my_chr_dev";
ret = device_add(my_device);
if(ret == 0)
printk("device added correctly\n");
else
printk("ret = %d\n",ret);
/*Now register the driver*/
ret = driver_register(&my_dev_driver);
if(ret == 0)
printk("driver registered correctly\n");
else
printk("ret = %d\n",ret);
return 0;
}
static void __exit my_exit(void)
{
printk("in exit\n");
driver_unregister(&my_dev_driver);
bus_unregister(&my_bus_type);
device_unregister(&my_bus);
}
module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("Kundan");
MODULE_LICENSE("GPL");
Here is the snipped ftrace for the device_add function of the driver taken from this program.
# tracer: function_graph
#
# CPU DURATION FUNCTION CALLS
# | | | | | | |
1) | device_add() {
1) | device_private_init() {
1) | kmem_cache_alloc_trace() {
1) 0.291 us | _cond_resched();
1) 2.375 us | }
1) 4.088 us | }
1) | dev_set_name() {
1) | __kmalloc_track_caller() {
1) 0.240 us | kmalloc_slab();
1) 0.220 us | _cond_resched();
1) 3.277 us | }
1) 0.231 us | kfree();
1) 7.374 us | }
1) 0.601 us | get_device_parent();
1) 0.280 us | _raw_spin_lock();
1) 0.210 us | _raw_spin_unlock();
1) | sysfs_create_dir_ns() {
1) | kernfs_add_one() {
1) | mutex_lock() {
1) 0.210 us | _cond_resched();
1) 1.704 us | }
1) 0.401 us | kernfs_name_hash();
1) 0.511 us | kernfs_link_sibling();
1) 0.291 us | mutex_unlock();
1) | kernfs_activate() {
1) | mutex_lock() {
1) 0.210 us | _cond_resched();
1) 1.733 us | }
1) | kernfs_next_descendant_post() {
1) 0.271 us | kernfs_leftmost_descendant();
1) 1.903 us | }
1) 0.220 us | kernfs_next_descendant_post();
1) 0.261 us | mutex_unlock();
1) 9.459 us | }
1) + 19.107 us | }
1) + 49.938 us | }
1) + 51.360 us | }
1) 0.251 us | kernfs_get();
1) | acpi_platform_notify() {
1) | acpi_get_bus_type() {
1) | down_read() {
1) 0.200 us | _cond_resched();
1) 1.714 us | }
1) 0.250 us | pci_acpi_bus_match();
1) 0.221 us | usb_acpi_bus_match();
1) 0.261 us | up_read();
1) + 10.039 us | }
1) 0.281 us | acpi_bind_one();
1) + 13.626 us | }
1) | device_create_file() {
1) + 46.381 us | }
1) 0.250 us | sysfs_create_groups();
1) | bus_add_device() {
1) | device_add_groups() {
1) 0.220 us | sysfs_create_groups();
1) 1.933 us | }
1) | sysfs_create_link() {
1) | sysfs_do_create_link_sd.isra.2() {
1) 0.501 us | kfree();
1) 0.581 us | kfree();
1) | bus_probe_device() {
1) | device_attach() {
1) | mutex_lock() {
1) 0.251 us | _cond_resched();
1) 1.803 us | }
1) | bus_for_each_drv() {
1) 0.271 us | _raw_spin_lock();
1) 0.221 us | _raw_spin_unlock();
1) | __device_attach() {
1) | my_bus_match [bus_final_1]() {
1) | printk() {
1) | vprintk_default() {
1) | vprintk_emit() {
1) 0.291 us | _raw_spin_lock();
1) 0.496 us | log_store();
1) 0.085 us | _raw_spin_unlock();
1) 0.265 us | console_trylock();
1) 2.800 us | console_unlock();
1) + 15.946 us | }
1) + 17.038 us | }
1) + 18.301 us | }
1) + 19.412 us | }
1) | driver_probe_device() {
1) | pm_runtime_barrier() {
1) 0.115 us | _raw_spin_lock_irq();
1) 0.135 us | __pm_runtime_barrier();
1) 1.393 us | }
1) | pinctrl_bind_pins() {
1) | my_driver_probe [bus_final_1]() {
1) | printk() {
1) | vprintk_default() {
1) 4.534 us | vprintk_emit();
1) 5.130 us | }
1) 5.711 us | }
1) | alloc_chrdev_region() {
1) | __register_chrdev_region() {
1) 0.155 us | kmem_cache_alloc_trace();
1) 0.516 us | mutex_lock();
1) 0.106 us | mutex_unlock();
1) 2.660 us | }
1) 3.327 us | }
1) | printk() {
1) | vprintk_default() {
1) 2.841 us | vprintk_emit();
1) 3.427 us | }
1) 4.053 us | }
1) | __class_create() {
1) | kmem_cache_alloc_trace() {
1) 0.090 us | _cond_resched();
1) 0.902 us | }
1) | __class_register() {
1) 1.348 us | kmem_cache_alloc_trace();
1) 0.091 us | __mutex_init();
1) 0.216 us | __kmalloc_track_caller();
1) 0.101 us | kfree();
1) 0.110 us | _raw_spin_lock();
1) 0.080 us | _raw_spin_unlock();
1) 3.927 us | sysfs_create_dir_ns();
1) 0.110 us | kernfs_get();
1) 0.090 us | class_child_ns_type();
1) 0.586 us | kmem_cache_alloc_trace();
1) + 30.034 us | }
1) + 32.197 us | }
1) | printk() {
1) | vprintk_default() {
1) 2.680 us | vprintk_emit();
1) 3.266 us | }
1) 3.858 us | }
1) | device_create() {
1) | device_create_groups_vargs() {
1) 0.542 us | kmem_cache_alloc_trace();
1) 0.861 us | device_initialize();
1) 0.206 us | __kmalloc_track_caller();
1) 0.100 us | kfree();
1) # 2434.891 us | device_add();
1) # 2443.262 us | }
1) # 2443.979 us | }
1) | printk() {
1) | vprintk_default() {
1) 5.891 us | vprintk_emit();
1) 6.703 us | }
1) 7.410 us | }
1) | kmem_cache_alloc_trace() {
1) 0.090 us | _cond_resched();
1) 0.896 us | }
1) 0.255 us | cdev_init();
1) | cdev_add() {
1) | kobj_map() {
1) 0.240 us | __kmalloc();
1) 0.400 us | mutex_lock();
1) 0.100 us | mutex_unlock();
1) 2.685 us | }
1) 3.402 us | }
1) # 2511.166 us | }
1) # 3992.051 us | }
Why are there 2 device structures (one being registered using device_register, and the other being created and added using device_add ?
ReplyDelete