S3C2440驱动简析——I2C驱动(2)

     紧接上一篇博文的I2C主要数据结构的介绍,现在就让我们真正地进入I2C驱动的代码里面,领略一下这个稍微复杂点点的驱动。由于代码已经有一定长度,再也不能像之前那样整段copy,然后直接分析了。为了鄙人以后能够更好地翻阅自己的笔记,也为了比我更菜的小菜鸟考虑(应该没有的,呵呵),这次还是采取按照代码逻辑顺序,讲到哪,代码就贴到哪~ go go go~~~

 

     看到i2c-dev.c 600多行的驱动代码,对于一般初学者来说还是够呛的,不过没关系,咱们谨记看驱动程序,第一要务找到入口和出口!__init 和 __exit 代码如下:

 

static int __init i2c_dev_init(void) { int res; printk(KERN_INFO "i2c /dev entries driver/n"); res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); if (IS_ERR(i2c_dev_class)) { res = PTR_ERR(i2c_dev_class); goto out_unreg_chrdev; } res = i2c_add_driver(&i2cdev_driver); if (res) goto out_unreg_class; return 0; out_unreg_class: class_destroy(i2c_dev_class); out_unreg_chrdev: unregister_chrdev(I2C_MAJOR, "i2c"); out: printk(KERN_ERR "%s: Driver Initialisation failed/n", __FILE__); return res; } static void __exit i2c_dev_exit(void) { i2c_del_driver(&i2cdev_driver); class_destroy(i2c_dev_class); unregister_chrdev(I2C_MAJOR,"i2c"); }

 

i2c_dev_init :

这个初始函数主要做了三件事。

1.res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

  显然,这里注册一个字符设备,并且链接到用户空间的操作函数所在的结构体i2cdev_fops。

 

2.i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

  class_create函数对于我们来说是一个新鲜事物,需要研究一下。Linux内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

 

3.res = i2c_add_driver(&i2cdev_driver);

  注册设备,描述设备的结构体i2cdev_driver在代码其他地方定义如下:

 

static struct i2c_driver i2cdev_driver = { .driver = { .name = "dev_driver", }, .attach_adapter = i2cdev_attach_adapter, .detach_adapter = i2cdev_detach_adapter, };

 

  这个结构体主要包括了两个函数i2cdev_attach_adapter 和i2cdev_detach_adapter。其中i2cdev_attach_adapter负责链接到I2C适配器上,而后者反之。

 

先讨论i2cdev_attach_adapter,代码如下:

static int i2cdev_attach_adapter(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; int res; i2c_dev = get_free_i2c_dev(adap); if (IS_ERR(i2c_dev)) return PTR_ERR(i2c_dev); /* register this i2c device with the driver core */ i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, MKDEV(I2C_MAJOR, adap->nr), NULL, "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { res = PTR_ERR(i2c_dev->dev); goto error; } res = device_create_file(i2c_dev->dev, &dev_attr_name); if (res) goto error_destroy; pr_debug("i2c-dev: adapter [%s] registered as minor %d/n", adap->name, adap->nr); return 0; error_destroy: device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); error: return_i2c_dev(i2c_dev); return res; }

 

函数首先传入一个i2c_adapter结构体指针,该指针正是代表一个i2c适配器,函数主要做了3件事情。

1.i2c_dev = get_free_i2c_dev(adap);

  传入的adap适配器指针,正是返回的i2c_dev结构中,adap成员指向传入的适配器指针。

 

2.i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
         MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr);

  第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。创建一个模块并链接到sysfs。

 

3.res = device_create_file(i2c_dev->dev, &dev_attr_name);

  创建设备的属性文件。

 

 

好了,再来看一下i2cdev_detach_adapter函数。

static int i2cdev_detach_adapter(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; i2c_dev = i2c_dev_get_by_minor(adap->nr); if (!i2c_dev) /* attach_adapter must have failed */ return 0; device_remove_file(i2c_dev->dev, &dev_attr_name); return_i2c_dev(i2c_dev); device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); pr_debug("i2c-dev: adapter [%s] unregistered/n", adap->name); return 0; }

 

该函数一共做了4件事情来“摆脱”适配器。

1.i2c_dev = i2c_dev_get_by_minor(adap->nr);

  通过次设备号找到设备,并由i2c_dev结构体指针带回,供接下来的函数使用。

 

2.device_remove_file(i2c_dev->dev, &dev_attr_name);

  对应device_create_file,移除之。

 

3.return_i2c_dev(i2c_dev);

  将传入的设备从设备列表中删除,并释放i2c_dev。

 

4.device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

  连同设备对应的类(实则是一个高层视图,忽略底层操作的东东)一并删除。

    

 

到此为止,驱动的初始化部分算是完成了,回过头来看一下退出函数__exit吧。

static void __exit i2c_dev_exit(void) { i2c_del_driver(&i2cdev_driver); class_destroy(i2c_dev_class); unregister_chrdev(I2C_MAJOR,"i2c"); }

 

只有三个语句,其执行顺序跟初始化函数__init的注册顺序刚好倒过来,具体里面的函数是怎样执行的这里就不再赘述了。

 

 

至于i2cdev_fops里的提供给用户空间函数留待下一篇博文继续探讨。在此仅贴出其结构体代码如下:

static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, };

 

嘿嘿,非常熟悉的结构,非常熟悉的面孔。欲知后事如何,请听下回分解~

 

本系列博文链接:

I2C驱动(1)http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6449939.aspx
I2C驱动(2)http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6451168.aspx
I2C驱动(3)http://blog.csdn.net/jarvis_xian/archive/2011/05/28/6452431.aspx
I2C驱动(4)http://blog.csdn.net/jarvis_xian/archive/2011/05/30/6455697.aspx

 

 

你可能感兴趣的:(c,struct,File,Module,Class,linux内核)