转自:http://blog.youkuaiyun.com/shui1025701856/article/details/7565352
参考Linux设备驱动开发详解第十五章
一、I2C体系结构
- I2C和SMBus设备使用7位地址,总线上最多支持127个设备,最高100kbit/s的传输率,
- 快速模式下可达400kbit/s,半双工.每个I2C/SMBus客户都分配有一个从地址,作为
- 设备标识,I2C总线被非常广泛地应用在EEPROM、实时钟、小型LCD等设备与CPU的接口中
- 在Linux系统中,I2C驱动由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。
- 这3部分相互协作,形成了非常通用、可适应性很强的I2C框架。
- I2C核心
- I2C 核心提供了I2C总线驱动和设备驱动的注册、注销方法,
- I2C通信方法(即“algorithm)上层的、与具体适配器无关的代码以及探测设备、
- 检测设备地址的上层代码等
- I2C总线驱动
- I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,
- 甚至直接集成在CPU内部。I2C总线驱动主要包含了I2C适配器数据结构
- i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm
- 和控制I2C适配器产生通信信号的函数。
- 经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生
- 开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
- I2C设备驱动
- I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在受
- CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据
- I2C设备驱动主要包含了数据结构i2c_driver和i2c_client,
- 我们需要根据具体设备实现其中的成员函数。
- drivers/i2c/i2c-core.c
- 这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口
- drivers/i2c/i2c-dev.c
- 实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。
- 通过适配器访问设备时的主设备号都为89,
- 次设备号为0~255。应用程序通过 “i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...)
- 文件名并使用文件操作接口open() write()、read()、ioctl()和close()等来访问这个设备
- i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口
- 用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或
- 寄存器并控制I2C设备的工作方式
- busses文件夹
- 这个文件中包含了一些I2C总线的驱动,如S3C2410 S3C6410的I2C控制器驱动为i2c-s3c2410.c。
- algos文件夹
- 实现了一些I2C总线适配器的algorithm。
- /include/linux/i2c.h
- /**
- *i2c_adapter 是用于标识物理I2C总线的结构
- *要使用它的算法
- */
- struct i2c_adapter {
- struct module *owner;//所属模块
- unsigned int id;//algorithm的类型,定义于include/linux/i2c-id.h
- unsigned int class; /* 允许探测的*/
- const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */
- void *algo_data;//algorithm数据
- /* 适用于所有设备的数据 */
- struct rt_mutex bus_lock;
- int timeout; /* in jiffies */
- int retries;//重复次数
- struct device dev; /* 适配器设备*/
- int nr;
- char name[48];//适配器名称
- struct completion dev_released;//用于同步
- struct list_head userspace_clients;
- };
- /**
- *下面的结构是为那些想实施新的总线驱动:
- *i2c_algorithm是一类硬件解决方案的接口
- *
- */
- struct i2c_algorithm {
- /**
- *如果适配器算法不能访问I2C,设置master_xfer为NULL,如果适配器
- *算法可以做SMBus访问,设置smbus_xfer,如果设置为NULL,SMBus协议
- *是模拟使用普通的I2C消息
- */
- int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num);//i2c传输函数指针
- int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data);
- //smbus传输函数指针
- /* 返回适配器支持的功能 */
- u32 (*functionality) (struct i2c_adapter *);
- };
- /**
- *结构体i2c_driver代表一个I2C设备驱动程序
- *@class:什么样的I2C设备,用于检测
- *@attach_adapter:总线添加回调
- *@detach_adapter:总线移除回调(for legacy drivers)
- *@probe:设备绑定回调
- *@remove:设备解脱回调
- *@shutdown:设备关机回调
- *@suspend:设备挂起回调
- *@resume:设备唤醒回调
- *@command:总线信号回调(可选)
- *@driver:设备模型驱动
- *@id_table:这个驱动支持的I2C器件
- *@detect:设备检测回调
- *@address_list:I2C地址探测
- *@clients:我们创建了检测到的客户名单(I2C核心使用)
- *@
- */
- struct i2c_driver {
- unsigned int class;
- //通知一个新的总线驱动将会出现,你应避免使用这个,它可能
- //会在将来被删除
- int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针
- int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针
- /* 标准的驱动程序接口 */
- int (*probe)(struct i2c_client *, const struct i2c_device_id *);
- int (*remove)(struct i2c_client *);
- /* 驱动模型接口,不涉及到枚举 */
- void (*shutdown)(struct i2c_client *);
- int (*suspend)(struct i2c_client *, pm_message_t mesg);
- int (*resume)(struct i2c_client *);
- //回调警告,例如SMBus报警协议,数据值的格式和意义取决于协议
- void (*alert)(struct i2c_client *, unsigned int data);
- //像ioctl一样,可用于执行特定功能的命令与设备
- int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
- struct device_driver driver;
- const struct i2c_device_id *id_table;//该驱动所支持的设备ID表
- //设备自动创建设备的检测回调函数
- int (*detect)(struct i2c_client *, struct i2c_board_info *);
- const unsigned short *address_list;
- struct list_head clients;
- };
- /**
- *结构体i2c_client:代表一个I2C从设备
- *@flags:I2C_CLIENT_TEN表示该设备采用了10位芯片的地址
- *I2C_CLIENT_PEC:表明它使用的SMBus数据包错误检查
- *@addr:父适配器连接到I2C总线上的地址
- *@name:表示设备的类型,通常这是芯片的名称
- *@adapter:管理I2C总线设备
- *@driver:设备的驱动程序,指针来访问例程
- *@dev:驱动模型从设备的节点
- *@irq:表示此装置所产生的IRQ
- *@detected:i2c_driver.clients或I2C核心成员列表
- *i2c_client标识连接到一个单一的设备(芯片)
- *i2c_bus由驱动定义
- */
- struct i2c_client {
- unsigned short flags; /* 标志 */
- unsigned short addr; /* 低7位的芯片地址 */
- char name[I2C_NAME_SIZE]; //设备名称
- struct i2c_adapter *adapter; /* 依附的i2c_adapter */
- struct i2c_driver *driver; /* 依附的i2c_driver */
- struct device dev; /* 设备结构体 */
- int irq; /* 设备使用的中断号 */
- struct list_head detected;
- };
- i2c_adapter与i2c_algorithm
- i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。
- 一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。
- 缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm的指针。
- i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位
- /**
- *i2c_msg:一个I2C传输起始
- *@addr:从地址,七,或十位,当是十位的时候I2C_M_TEN必须被设置,必须支持I2C_FUNC_10BIT_ADDR
- *@flags:I2C_M_RD适合所有适配器处理
- *@len:消息长度
- *@buf:消息数据
- */
- struct i2c_msg {
- __u16 addr; /* slave address */
- __u16 flags;
- #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
- #define I2C_M_RD 0x0001 /* read data, from slave to master */
- #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
- __u16 len; /* msg length */
- __u8 *buf; /* pointer to msg data */
- };
- i2c_driver与i2c_client
- i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应
- 于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结
- 构体中
- i2c_driver 与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测物
- 理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter
- driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数
- 相反的过程发生在 i2c_driver 的detach_client()函数被调用的时候。
- i2c_adpater与i2c_client
- i2c_adpater 与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由
- 于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,
- i2c_adpater中包括依附于它的i2c_client的链表。
- 工程师要实现的主要工作如下
- 提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2C的I/O地址和中断号)、驱动CPU控制的I2C适
- 配器从硬件上产生各种信号以及处理I2C中断等
- 提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,
- 并把i2c_algorithm指针赋值给i2c_adapter的algo指针。
- 实现I2C设备驱动与i2c_driver接口,用具体设备yyy的yyy_attach_adapter()函数指针、 yyy_detach_client()函
- 数指针和yyy_command()函数指针的赋值给i2c_driver的attach_adapter、 detach_adapter和detach_client指针。
- 实现I2C设备驱动的文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl()函数等。
二、I2C核心
- drivers/i2c/i2c-core.c
- I2C核心(drivers/i2c/i2c-core.c)中提供了一组不依赖于硬件平台的接口函数,
- 这个文件一般不需要被工程师修改,但是理解其中的主要函数非常关键,
- 因为I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带。I2C核心中的主要函数包括:
- 增加删除i2c_adapter
- int i2c_add_adapter(struct i2c_adapter *adapter)
- int i2c_del_adapter(struct i2c_adapter *adap)
- 增加删除i2c_driver
- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- void i2c_del_driver(struct i2c_driver *driver)
- 当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册
- 相反地,在client被取消关联的时候,sysfs文件和设备也被注销
- I2C传输,发送和接收
- //执行单一或联合的I2C消息
- int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
- //主传输模式发送一个单一的I2C消息
- int i2c_master_send(struct i2c_client *client, const char *buf, int count)
- //主控I2C消息接收模式
- int i2c_master_recv(struct i2c_client *client, char *buf, int count)
- i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和
- i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息
- i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是需找到
- i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程
三、I2C总线驱动
- I2C适配器驱动加载与卸载
- I2C总线驱动模块的加载函数要完成两个工作:
- 初始化I2C适配器所使用的硬件资源,申请I/O地址、中断号等。
- 通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员
- 已经被xxx适配器的相应函数指针所初始化。
- I2C总线驱动模块的卸载函数要完成的工作与加载函数的相反:
- 释放I2C适配器所使用的硬件资源,释放I/O地址、中断号等
- 通过i2c_del_adapter()删除i2c_adapter的数据结构
- I2C总线通信方法
- 我们需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数
- 和functionality()函数
- functionality ()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、
- I2C_FUNC_10BIT_ADDR、 I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等
- master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息
- 总线驱动master_xfer函数模板
四、I2C设备驱动
- I2C 设备驱动要使用i2c_driver和i2c_client数据结构并填充其中的成员函数。
- i2c_client一般被包含在设备的私有信息结构体 yyy_data中,
- 而i2c_driver则适宜被定义为全局变量并初始化
- 被初始化的i2c_driver
- static struct i2c_driver ov9640_i2c_driver = {
- .driver = {
- .name = "ov9640",
- },
- .probe = ov9640_probe,
- .remove = ov9640_remove,
- .id_table = ov9640_id,
- };
- Linux I2C设备驱动模块加载与卸载
- I2C设备驱动模块加载函数通用的方法是在I2C设备驱动模块加载函数中完成两件事:
- 通过register_chrdev()函数将I2C设备注册为一个字符设备。
- 通过I2C核心的i2c_add_driver()函数添加i2c_driver
- 在模块卸载函数中需要做相反的两件事:
- 通过I2C核心的i2c_del_driver()函数删除i2c_driver
- 通过unregister_chrdev()函数注销字符设备
- static int __init ov9640_module_init(void)
- {
- return i2c_add_driver(&ov9640_i2c_driver);
- }
- static void __exit ov9640_module_exit(void)
- {
- i2c_del_driver(&ov9640_i2c_driver);
- }
- Linux I2C设备驱动的数据传输
五、S3C6410 I2C总线驱动实例
- S3C6410 处理器内部集成了一个I2C控制器,通过4个寄存器就可以方便地对其进行控制
- 这四个寄存器如下
- IICCON:I2C控制寄存器
- IICSTAT:I2C状态寄存器
- IICDS:I2C收发数据移位寄存器
- IICADD:I2C 地址寄存器
- 通过对IICCON,IICDS和IICADD的操作,可在I2C总线上产生开始位,停止位,数据和地址
- 而传输状态则通过IICSTAT寄存器获取
- S3C6410 I2C的总线驱动drivers/i2c/busses/i2c-s3c2410.c
- I2C总线驱动设计主要要实现的工作包括:
- 设计对应于i2c_adapter_xxx_init()模板的S3C2410的模块加载函数和对应于
- i2c_adapter_xxx_exit()函数模板的模块卸载函数。
- 设计对应于i2c_adapter_xxx_xfer()模板的S3C6410适配器的通信方法函数
- functionality()函数只需简单的返回
- I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING
- 表明其支持的功能
- static int __init i2c_adap_s3c_init(void)
- {
- return platform_driver_register(&s3c24xx_i2c_driver);
- }
- subsys_initcall(i2c_adap_s3c_init);
- static void __exit i2c_adap_s3c_exit(void)
- {
- platform_driver_unregister(&s3c24xx_i2c_driver);
- }
- /* s3c24xx_i2c_probe
- *
- * called by the bus driver when a suitable device is found
- */
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
- if (!i2c) {
- dev_err(&pdev->dev, "no memory for state\n");
- return -ENOMEM;
- }
- strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
- i2c->adap.owner = THIS_MODULE;
- i2c->adap.algo = &s3c24xx_i2c_algorithm;
- i2c->adap.retries = 2;
- i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- i2c->tx_setup = 50;
- spin_lock_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
- /* 发现时钟并使能它 */
- i2c->dev = &pdev->dev;
- i2c->clk = clk_get(&pdev->dev, "i2c");
- if (IS_ERR(i2c->clk)) {
- dev_err(&pdev->dev, "cannot get clock\n");
- ret = -ENOENT;
- goto err_noclk;
- }
- dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
- clk_enable(i2c->clk);
- /* 映射寄存器 */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "cannot find IO resource\n");
- ret = -ENOENT;
- goto err_clk;
- }
- i2c->ioarea = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (i2c->ioarea == NULL) {
- dev_err(&pdev->dev, "cannot request IO\n");
- ret = -ENXIO;
- goto err_clk;
- }
- i2c->regs = ioremap(res->start, resource_size(res));
- if (i2c->regs == NULL) {
- dev_err(&pdev->dev, "cannot map IO\n");
- ret = -ENXIO;
- goto err_ioarea;
- }
- dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
- i2c->regs, i2c->ioarea, res);
- /* 设置i2c核心需要的信息*/
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- /* initialise the i2c controller */
- ret = s3c24xx_i2c_init(i2c);
- if (ret != 0)
- goto err_iomap;
- /*
- *申请中断
- */
- i2c->irq = ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "cannot find IRQ\n");
- goto err_iomap;
- }
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), i2c);
- if (ret != 0) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
- goto err_iomap;
- }
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
- goto err_irq;
- }
- //bus_num是platform数据
- i2c->adap.nr = pdata->bus_num;
- ret = i2c_add_numbered_adapter(&i2c->adap);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to add bus to i2c core\n");
- goto err_cpufreq;
- }
- platform_set_drvdata(pdev, i2c);
- clk_disable(i2c->clk);
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- return 0;
- }
- S3C6410 I2C总线通信方法
- static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
- .master_xfer = s3c24xx_i2c_xfer,
- .functionality = s3c24xx_i2c_func,
- };
- /drivers/misc/eeprom/at24.c不依赖于具体的CPU和I2C控制器硬件特性
- 如果电路板包含该外设,只需要在板文件中添加对应的i2c_board_info
- arch/arm/mach-s3c64xx/mach-smdk6410.c
- static struct i2c_board_info i2c_devs0[] __initdata = {
- { I2C_BOARD_INFO("24c08", 0x50), },
- { I2C_BOARD_INFO("wm8580", 0x1b), },
- }
六、I2C读写函数的封装
- static int ov9650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
- {
- char buf[1];
- int retval = 0;
- buf[0] = reg;
- retval = i2c_master_send(client, buf, 1);
- if (retval != 1) {
- dev_err(&client->dev, "%s read reg:%x error\n", __func__, reg);
- return retval;
- }
- retval = i2c_master_recv(client, buf, 1);
- if (retval != 1) {
- dev_err(&client->dev, "%s read reg:%x error\n", __func__, reg);
- return retval;
- }
- *val = buf[0];
- return retval;
- }
- static int ov9650_reg_write(struct i2c_client *client, u8 reg, u8 val)
- {
- int retval = 0;
- if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- retval = i2c_smbus_write_byte_data(client, reg, val);
- else
- dev_err(&client->dev, "%s write reg:%x error\n", __func__, reg);
- return retval;
- }
- if (1) {
- u8 pid, ver;
- ov9650_reg_read(client, 0x0A, &pid);
- ov9650_reg_read(client, 0x0B, &ver);
- dev_info(&client->dev, "%s:PID=%x, VER=%x\n", client->name, pid, ver);
- }
- --------------------------------------------------------------------------------------
- u8 s3c_fimc_i2c_read(struct i2c_client *client, u8 subaddr)
- {
- u8 buf[1];
- struct i2c_msg msg = {client->addr, 0, 1, buf};
- int ret;
- buf[0] = subaddr;
- ret = i2c_transfer(client->adapter, &msg, 1) == 1 ? 0 : -EIO;
- if (ret == -EIO) {
- err("i2c transfer error\n");
- return -EIO;
- }
- msg.flags = I2C_M_RD;
- ret = i2c_transfer(client->adapter, &msg, 1) == 1 ? 0 : -EIO;
- return buf[0];
- }
- int s3c_fimc_i2c_write(struct i2c_client *client, u8 subaddr, u8 val)
- {
- u8 buf[2];
- struct i2c_msg msg = {client->addr, 0, 2, buf};
- buf[0] = subaddr;
- buf[1] = val;
- return i2c_transfer(client->adapter, &msg, 1) == 1 ? 0 : -EIO;
- }
- ----------------------------------------------------
- I2C发送和接收
- drivers/i2c/i2c-core.c里实现
- int i2c_master_send(struct i2c_client *client, const char *buf, int count)
- int i2c_master_recv(struct i2c_client *client, char *buf, int count)
- 第一个参数是i2c_client对象指针,第二个参数是要传输的数据buffer指针,第三个参数为ubffer
- 的大小
- 对于写I2C寄存器,给i2c_master_send函数传入两个字节的数据即可,第一个字节
- 为寄存器地址,第二个字节为要写入寄存器的数据
- static int ov9650_reg_write(struct i2c_client* client, u8 reg, u8 data)
- {
- unsigned char buffer[2];
- buffer[0] = reg;
- buffer[1] = data;
- if ( 2 != i2c_master_send(client, buffer, 2)){
- printk(KERN_ERR "ov9650_i2c_reg_write fail!\n");
- return -1;
- }
- return 0;
- }
- 读I2C时序要做的操作是,先向I2C总线上写入需要读的寄存器地址,然后读I2C总线上的值
- u8 ov9650_reg_read(struct i2c_client* client, u8 reg, u8 *data)
- {
- //write reg addr
- if ( 1 != i2c_master_send(client, ®, 1)) {
- printk(KERN_ERR "ov9650_i2c_reg_read fail!\n");
- return -1;
- }
- //wait
- msleep(10); //#include <linux/delay.h>
- //read
- if ( 1 != i2c_master_recv(client, data, 1)) {
- printk(KERN_ERR "ov9650_i2c_reg_read fail!\n");
- return -1;
- }
- return 0;
- }
- 测试
- ---------------------------------------------------------
- u8 pid;
- ov9650_reg_read(client, 0x0A, &pid);读寄存器ID PID=96
- printk("PID=%x\n", pid);
- ---------------------------------------------------------
- u8 pid;
- ov9650_reg_write(client, 0x0C, 0xCD);
- ov9650_reg_read(client, 0x0C, &pid);
- printk("PID=%x\n", pid);//PID=cd