24cxx驱动编程.
1.首先实例化一个i2c设备.
2.编写对应的i2c设备驱动.
3.通过match规则匹配设备和驱动。对应驱动会调用自己的probe函数完成初始化
4.i2c设备的操作实现,write和read.
1)如何实例化一个i2c设备
在\Documentation\i2c\instantiating-devices文件中介绍了4种方法
实例化一个IIC设备
Method 1: Declare the I2C devices by bus number
Method 2: Instantiate the devices explicitly
Method 3: Probe an I2C bus for certain devices
Method 4: Instantiate from user-space
我们使用第一种方式:
Example (from omap2 h4):
static struct i2c_board_info __initdata h4_i2c_board_info[] = {
{
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
{ /* EEPROM on mainboard */
I2C_BOARD_INFO("24c01", 0x52),
.platform_data = &m24c01,
},
{ /* EEPROM on cpu card */
I2C_BOARD_INFO("24c01", 0x57),
.platform_data = &m24c01,
},
};
static void __init omap_h4_init(void)
{
(...)
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(...)
}
I2C_BOARD_INFO(dev_type, dev_addr) 用来初始化 i2c_board_info结构,填充 type类型和 dev_addr地址.
根据线路图连接,确定 dev addr.
实际板子上是24c08.确认address:0x50.
这里需要说明一下,地址计算不算最后一个bit。也就是读写位,当发送读cmd时,最后一位置1,当发送写cmd时,最后一位置0.也就是说1010 000 x.高7bit代表地址,则1010000->0x50.
内核在发送read时,会 addr =0x50<<1 |1;当write 时,会 addr=0x50<<1 |0;
static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
{ I2C_BOARD_INFO("24c08", 0x50), },
};
i2c_register_board_info(0, smdkv210_i2c_devs0,
ARRAY_SIZE(smdkv210_i2c_devs0));
i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned n)
s5pv210有3组iic总线,我们的连接到iic0上。所以这边的0是有意义的.
只要这样,我们便可以添加一个新的i2c dev到 iic bus上.
当然内核中肯定有24cxx的驱动,我们可以直接拿来用。注意下match规格即可.当然也可以自己写这个驱动.
IIC总线的匹配规则:
IIC 驱动注册流程:
//驱动注册流程
i2c_add_driver
{
--->i2c_register_driver
{
--->driver_register
{
--->bus_add_driver
{
--->driver_attach
{
--->bus_for_each_dev
{
--->__driver_attach
{
--->driver_match_device
//when match ok,then go the next
--->driver_probe_device
{
--->really_probe
{
--->dev->bus->probe(dev)//调用probe函数
}
}
}
}
}
}
}
}
}
i2c driver 编写:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/bitops.h>
/*const*/
const struct i2c_device_id at24cxx_id[] =
{
{"24c08",0},
{}
};
//device number
static dev_t at_dev_num;
struct at24cxx_dev
{
struct cdev at_dev;//char device
struct i2c_client * at_client;
};
static struct at24cxx_dev *at24cxx_struct = NULL;
//auto set up the device node file use
static struct class *at_class =NULL;
static struct device *at_device =NULL;
/*****ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)*****/
static ssize_t at24cxx_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
unsigned char addr;
unsigned char data;
int ret=0;
struct i2c_msg msgs[2];
ret =copy_from_user(&addr,buf,1);
msgs[0].addr = at24cxx_struct->at_client->addr;
msgs[0].flags = 0;
msgs[0].buf = &addr;
msgs[0].len = 1;
msgs[1].addr = at24cxx_struct->at_client->addr;
msgs[1].flags = 1;
msgs[1].buf = &data;
msgs[1].len = 1;
i2c_transfer(at24cxx_struct->at_client->i2c_adapter,msgs,ARRAY_SIZE(msgs));
ret =copy_to_user(buf,&data,1);
}
/*****ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *)*****/
static ssize_t at24cxx_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
unsigned char kbuf[2];
int ret=0;
struct i2c_msg msgs[1];
if(size != 2)
return -EINVAL;
ret = copy_from_user(kbuf,buf,size);
//slave dev addr
msgs[0].addr =at24cxx_struct->at_client->addr;
//write
msgs[0].flags=0;
msgs[0].buf =kbuf;
msgs[0].len =2;
i2c_transfer(at24cxx_struct->at_client->i2c_adapter,msgs,1);
return size;
}
static struct file_operations at24cxx_fops =
{
.owner =THIS_MODULE,
.read =at24cxx_read,
.write =at24cxx_write,
};
/*****int (*probe)(struct i2c_client *, const struct i2c_device_id *)*****/
static int at24cxx_probe(struct i2c_client *uc_i2c_client, const struct i2c_device_id * uc_i2c_id_table)
{
/* alloc the dev number*/
alloc_chrdev_region(&at_dev_num,0,unsigned 1,"at24c02")
/* get the buf for at24cxx_struct */
at24cxx_struct = kzalloc(sizeof(struct at24cxx_dev),GFP_KERNEL);
at24cxx_struct->at_client = uc_i2c_client;
/* initial cdev struct,add cdev to kernel */
cdev_init(&(at24cxx_struct->at_dev),&at24cxx_fops);
cdev_add(&(at24cxx_struct->at_dev),at_dev_num,1);
/*create device node file use*/
at_class =class_create(THIS_MODULE,"at24cxx");
at_device =device_create(at_class,NULL,at_dev_num,NULL,"at24cxx");
return 0;
}
/*****int (*remove)(struct i2c_client *)*****/
static int at24cxx_remove(struct i2c_client *uc_i2c_client)
{
device_destroy(at_class,at_dev_num);
class_destroy(at_class);
cdev_del(&(at24cxx_struct->at_dev));
kfree(at24cxx_struct);
unregister_chrdev_region(at_dev_num,1);
return 0;
}
static struct i2c_driver at24cxx_drv =
{
.driver =
{
.name ="at24cxx",
.owner= THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = at24cxx_remove,
//match use
.id_table = at24cxx_id,
};
int __init at24cxx_init(void)
{
i2c_add_driver(&at24cxx_drv);
return 0;
}
void __exit at24cxx_exit(void)
{
i2c_del_driver(&at24cxx_drv);
}
module_init(at24cxx_init);
module_exit(at24cxx_exit);
MODULE_LICENSE("GPL");
加载模块.
0-0050:这里就是我们添加的设备.
at24cxx: 这里就是我们添加的驱动.
dev下面有我们创建的设备
测试程序:
测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void print_usage(char *file)
{
printf("%s w addr val \n",file);
printf("%s r addr \n",file);
}
//write: a.out w 10 0x55
//read: a.out r 10
int main(int argc ,char ** argv)
{
int fd;
unsigned char buf[2];
if(argc !=3 && argc !=4)
{
print_usage(argv[0]);
return -1;
}
fd =open("/dev/at24cxx",O_RDWR);
if(strcmp(argv[1],"r") == 0)
{
buf[0] =strtoul(argv[2],NULL,0);
read(fd,buf,1);
printf("data:%d,0x%2x\n",buf[0],buf[0]);
}
else if(strcmp(argv[1],"w") == 0)
{
buf[0] =strtoul(argv[2],NULL,0);
buf[1] =strtoul(argv[3],NULL,0);
write(fd,buf,2);
}
else
{
print_usage(argv[0]);
return -1;
}
return 0;
}