1写入口函数at24cxx_init()
在入口函数中主要有一个增加设备驱动的函数i2c_add_driver(),该函数会调用i2c_register_driver(THIS_MODULE,
驱动名字)完成注册。
2写出口函数at24cxx_exit()
在出口函数里主要有一个删除设备驱动的函数i2c_del_driver(),这个函数功能有从设备驱动链表中删除驱动,卸载注册的驱动等
3修饰入口函数和出口函数
module_init(at24cxx_init); 这样,使用insmod命令加载驱动模块时,入口函数
at24cxx_init()就会被调用
module_exit(at24cxx_exit); 这样,使用rmmod命令加载驱动模块时,入口函数
at24cxx_exit()就会被调用
4写I2C驱动主要是:分配一个i2c_driver结构体
设置填充i2c_driver结构体
staticstruct i2c_driver at24cxx_driver={
.driver = {
.name = “at24cxx”,
}
.attach_adapter = at24cxx_attach,
.detach_client = at24cxx_detach
};
函数at24cxx_attach()和at24cxx_detach需要自己构造
5构造at24cxx_attach()函数和at24cxx_detach()函数
Static int at24cxx_attach(struct i2c_adapter *adapter){
return i2c_probe(adapter,&addr_data,at24cxx_detect);
}
I2c_probe函数主要是通过适配器adapter发送结构体addr_data的地址,这个地址就是i2c设备的地址,需要自己根据硬件原理图定义的,如果收到回应,就是说有这个i2c设备,就调用at24cxx_detect处理,这个函数需要自己去构造的。
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "at24cxx");
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
卸载函数主要是把申请的主设备号注销掉,注销掉注册的设备,解除适配器上的设备驱动,最后释放申请的设备空间。
6、构造addr_data结构体
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END };
这里需要注意的是,I2C 设备的设备地址为7 位地址,不算第八位的R/W控制位,由电路图和i2c的数据手册可得出设备的地址。
所以i2c设备的地址为1010000,即0x50
staticstruct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
//.forces = forces,
};
7、构造at24cxx_detect()函数
收到回应说明有i2c设备,然后调用at24cxx_detect()函数,在函数里面主要是分配一个i2c_client结构体,然后设置填充该结构体。
at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
//分配空间
at24cxx_client->addr = address; //i2c设备的地址
at24cxx_client->adapter = adapter; //分配一个适配器
at24cxx_client->driver = &at24cxx_driver; //指向驱动
strcpy(at24cxx_client->name, "at24cxx"); //设备的名字
i2c_attach_client(at24cxx_client); //把设备驱动依附在适配器adapter上 major =
register_chrdev(0, "at24cxx", &at24cxx_fops);
//注册,新的注册方法不再这样
注册了
cls = class_create(THIS_MODULE, "at24cxx");
class_device_create(cls, NULL, MKDEV(major, 0), NULL,
"at24cxx");
以后收发数据要用到该结构体的
8、构造file_operations结构体中的各成员函数
主要构造.read函数、.write函数
a. 构造读函数
staticssize_t at24cxx_read(struct file *file, char __user *buf,
size_t size, loff_t * offset)
首先要从应用程序中获得要读的地址copy_from_user(&address, buf, 1);
msg[0].addr = at24cxx_client->addr;
msg[0].buf = &address;
msg[0].len = 1;
msg[0].flags = 0;
msg[1].addr = at24cxx_client->addr;
msg[1].buf = &data;
msg[1].len = 1;
msg[1].flags = I2C_M_RD;
ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
if (ret == 2)
{
copy_to_user(buf, &data, 1);
return 1;
}
调用i2c传输函数,这会按i2c协议进行,然后把读到的数据返回给用户。
b、构造写函数
copy_from_user(val, buf, 2);
msg[0].addr = at24cxx_client->addr;
msg[0].buf = val;
msg[0].len = 2;
msg[0].flags = 0;
ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
第一句是从用户获得要写的数据,下面的msg就是设备地址、要写的地址,长度,写 最后就是调用传输函数进行写。
9、填充file_operations结构体
staticstructfile_operations at24cxx_fops = {
.owner = THIS_MODULE,
.read = at24cxx_read,
.write = at24cxx_write,
};
这样在调用read函数时,就会调用at24cxx_read,在调用write函数时,就会调用at24cxx_write
三、问题
本总结的基于Linux2.6.22内核,与本开发板用的3.0.1内核的驱动有差别,3.0.1内核的很多i2c驱动都归到混杂设备驱动中去了
四、I2C驱动程序
#include linux/kernel.h
#include linux/init.h
#include linux/module.h
#include linux/slab.h
#include linux/jiffies.h
#include linux/i2c.h
#include linux/mutex.h
#include linux/fs.h
#include asm/uaccess.h
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END };
static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60,
I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
staticstruct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
//.forces = forces,
};
staticstruct i2c_driver at24cxx_driver;
staticint major;
staticstruct class *cls;
struct i2c_client *at24cxx_client;
staticssize_t at24cxx_read(struct file *file, char __user *buf,
size_t size, loff_t * offset) {
unsigned char address;
unsigned char data;
struct i2c_msg msg[2];
int ret;
if (size != 1)
return -EINVAL;
copy_from_user(&address, buf, 1);
msg[0].addr = at24cxx_client->addr;
msg[0].buf = &address;
msg[0].len = 1;
msg[0].flags = 0;
msg[1].addr = at24cxx_client->addr;
msg[1].buf = &data;
msg[1].len = 1;
msg[1].flags = I2C_M_RD;
ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
if (ret == 2)
{
copy_to_user(buf, &data, 1);
return 1;
}
else
return -EIO;
}
staticssize_t at24cxx_write(struct file *file, const char __user
*buf, size_t size, loff_t *offset) {
unsigned char val[2];
struct i2c_msg msg[1];
int ret;
if (size != 2)
return -EINVAL;
copy_from_user(val, buf, 2);
msg[0].addr = at24cxx_client->addr;
msg[0].buf = val;
msg[0].len = 2;
msg[0].flags = 0;
ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
if (ret == 1)
return 2;
else
return -EIO;
}
staticstructfile_operations at24cxx_fops = {
.owner = THIS_MODULE,
.read = at24cxx_read,
.write = at24cxx_write,
};
staticint at24cxx_detect(struct i2c_adapter *adapter, int address,
int kind)
{
printk("at24cxx_detect\n");
at24cxx_client = kzalloc(sizeof(struct i2c_client),
GFP_KERNEL);
at24cxx_client->addr = address;
at24cxx_client->adapter = adapter;
at24cxx_client->driver =&at24cxx_driver;
strcpy(at24cxx_client->name, "at24cxx");
i2c_attach_client(at24cxx_client);
major = register_chrdev(0, "at24cxx", &at24cxx_fops);
cls = class_create(THIS_MODULE, "at24cxx");
class_device_create(cls, NULL, MKDEV(major, 0), NULL,
"at24cxx");
return 0;
}
staticint at24cxx_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, at24cxx_detect);
}
staticint at24cxx_detach(struct i2c_client *client)
{
printk("at24cxx_detach\n");
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "at24cxx");
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
staticstruct i2c_driver at24cxx_driver = { .driver = {
.name = "at24cxx", },
.attach_adapter = at24cxx_attach, .detach_client = at24cxx_detach,
};
staticint at24cxx_init(void)
{
i2c_add_driver(&at24cxx_driver); return 0;
}
static void at24cxx_exit(void) {
i2c_del_driver(&at24cxx_driver); }
module_init(at24cxx_init);
module_exit(at24cxx_exit);
MODULE_LICENSE("GPL");