一、前言
本文主要分为三个部分,第一部分,介绍i2c字符设备驱动应用的背景以及本文测试需要的开发环境;第二部分,介绍主要的字符驱动源码及测试程序;第三部分,测试方法以及测试结果,i2c从设备的器件地址可以在该器件的datasheet查找。文章的最后会给大家分享本文的所有源码。
二、开发背景和环境
我已经讲解过利用i2c总线的去配置i2c从设备的方法,本文采用i2c设备驱动的方式完成同样的功能,在此完善工作记录。
优点:(1)当从设备需要多种功能操作时(比如修改摄像头的亮度、放大、曝光、分辨率等配置),把每个功能包装成子模块,相对总线方式的配置层次清晰,而且方便管理维护,而且在扩展时还能够在设备驱动中添加对系统内核资源的访问(操作时请注意安全);
(2)设备初始化顺序可以随意控制,想i2c从设备启动快点就把设备初始化添加到内核启动,想它启动慢一点,就以.ko的方式加载,等文件系统加载完毕了再初始化;
缺点 :(1)相比总线操作的方式编写代码较复杂,因为首先要熟悉字符驱动架构,而且还需要编写一个操作设备驱动的应用程序;
运行环境:ARM S3C6410平台
交叉编译器:ARM-LINUX-GCC
内核版本:2.6.28.6
三、源码的讲解
源码的讲解分为两个部分,第一个部分初略地介绍下i2c字符设备初始化过程,具体的字符驱动架构不再本文讲解的范围内,第二部分,讲解利用i2c设备驱动对i2c从设备的寄存器进行读写操作;
驱动源码初始化执行步骤,
module_init(ch7026_init)
首先执行ch7026_init函数
static __init int ch7026_init(void)
{
int ret;
dev_t devno;
printk(DEVICE_NAME " start init...\n");
p_bank = kmalloc(sizeof(struct ch7026_bank), GFP_KERNEL);
if (!p_bank)
return -ENOMEM;
memset(p_bank, 0, sizeof(struct ch7026_bank));
devno = MKDEV(CH7026_MAJOR,0);
ret = register_chrdev_region(devno,1,DEVICE_NAME);
if(ret<0)
{
printk(KERN_NOTICE "can not register ch7026 device");
return ret;
}
cdev_init(&cdev_ch7026,&ch7026_fops);
cdev_ch7026.owner = THIS_MODULE;
ret =cdev_add(&cdev_ch7026,devno,1);
if(ret)
{
printk(KERN_NOTICE "can not add ch7026 device");
return ret;
}
/*在/sys/class/下创建相对应的类目录*/
my_class = class_create(THIS_MODULE,"ch7026");
if(IS_ERR(my_class))
{
printk("Err: Failed in creating class\n");
return -1;
}
/*完成设备节点的自动创建,当加载模块时,就会在/dev下自动创建设备文件*/
device_create(my_class,NULL,MKDEV(CH7026_MAJOR,0),NULL,DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
i2c_add_driver(&ch7026_driver);
return 0;
}
执行回调函数 ch7026_probe()函数
static struct i2c_driver ch7026_driver = {
.driver = {
.name = "ch7026",
.owner = THIS_MODULE,
},
.id = I2C_DRIVERID_CH7026,
.attach_adapter = ch7026_probe,
.detach_client = ch7026_detach,
};
在执行ch7026_attach()函数
static int ch7026_probe(struct i2c_adapter *adap)
{
int ret = 0;
ret = i2c_probe(adap, &addr_data, ch7026_attach);
if (ret) {
printk("failed to attach ch7026 driver\n");
ret = -ENODEV;
}
return ret;
}
执行ch7026_attach函数,执行用户配置i2c从设备ch7026_config()函数
static int ch7026_attach(struct i2c_adapter *adap, int addr, int flags )
{
int ret = 0;
strcpy(p_bank->c.name, "ch7026");
p_bank->c.addr = addr;
p_bank->c.adapter = adap;
p_bank->c.driver = &ch7026_driver;
ret = i2c_attach_client(&p_bank->c);
ch7026_config(&p_bank->c);
printk("CH7026 attached successfully\n");
return ret;
}
第二部分下面