梧州建设厅官方网站,西安到北京火车票查询,网站 微信维护怎么做,百度网页游戏中心ov2640子设备核心操作详细分析 文章目录 ov2640子设备核心操作详细分析ov2640_subdev_core_ops核心操作获取寄存器值ov2640_g_register设置寄存器值ov2640_s_registeri2c_smbus_xferi2c_imx_xferi2c_smbus_xfer_emulatedi2c_transfer__i2c_transfer 设置ov2640的电源ov2640_s_p…ov2640子设备核心操作详细分析 文章目录 ov2640子设备核心操作详细分析ov2640_subdev_core_ops核心操作获取寄存器值ov2640_g_register设置寄存器值ov2640_s_registeri2c_smbus_xferi2c_imx_xferi2c_smbus_xfer_emulatedi2c_transfer__i2c_transfer 设置ov2640的电源ov2640_s_power ov2640_subdev_core_ops核心操作
// ov2640子设备核心操作
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops {
#ifdef CONFIG_VIDEO_ADV_DEBUG.g_register ov2640_g_register, // 获取寄存器值.s_register ov2640_s_register, // 设置寄存器值
#endif.s_power ov2640_s_power, // 设置ov2640的电源
};获取寄存器值ov2640_g_register
函数名为ov2640_g_register接受一个v4l2_subdev结构体指针和一个v4l2_dbg_register结构体指针reg作为参数。 函数的主要功能如下 从v4l2_subdev结构体中获取i2c_client结构体指针该结构体用于表示I2C设备。 设置要读取的寄存器的大小为1字节并检查要读取的寄存器地址是否有效如果无效则返回-EINVAL表示无效参数。 使用i2c_smbus_read_byte_data函数通过I2C总线读取指定寄存器的值。 如果读取成功将读取的值赋给reg-val字段并返回0表示成功。 如果读取失败直接返回读取函数的返回值。 该函数的作用是通过使用I2C总线读取指定寄存器的值并将读取的值存储在reg-val字段中以实现获取寄存器值的功能。
// 获取指定寄存器的值
static int ov2640_g_register(struct v4l2_subdev *sd,struct v4l2_dbg_register *reg)
{// 获取i2c_client结构体struct i2c_client *client v4l2_get_subdevdata(sd);// 定义变量int ret;reg-size 1;if (reg-reg 0xff)return -EINVAL;// 通过I2C总线读取指定寄存器的值ret i2c_smbus_read_byte_data(client, reg-reg);if (ret 0)return ret;reg-val ret;return 0;
}函数名为 i2c_smbus_read_byte_data接受一个 i2c_client 结构体指针 client 和一个 u8 类型的命令 command 作为参数。 函数的主要功能如下 定义一个联合体变量 data用于存储读取的数据。 定义一个整型变量 status用于存储函数执行的状态。 调用 i2c_smbus_xfer 函数传入适配器、地址、标志位以及读取操作的参数包括命令 command 和数据类型为字节型。 执行读取操作并将读取的数据存储在 data 中。 返回执行结果如果 status 小于 0返回 status否则返回 data.byte。 该函数的作用是通过使用 I2C 总线进行读取操作读取指定地址的寄存器的字节数据并返回读取的结果。
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
{union i2c_smbus_data data; // 定义一个联合体变量dataint status; // 定义一个整型变量statusstatus i2c_smbus_xfer(client-adapter, client-addr, client-flags, // 调用i2c_smbus_xfer函数传入适配器、地址、标志位I2C_SMBUS_READ, command, // 读取操作命令为commandI2C_SMBUS_BYTE_DATA, data); // 读取一个字节的数据存储在data中return (status 0) ? status : data.byte; // 如果status小于0返回status否则返回data.byte
}设置寄存器值ov2640_s_register
这段代码用于设置指定寄存器的值。 函数名为ov2640_s_register接受一个v4l2_subdev结构体指针和一个v4l2_dbg_register结构体指针reg作为参数。 函数的主要功能如下 从v4l2_subdev结构体中获取i2c_client结构体指针该结构体用于表示I2C设备。 检查要设置的寄存器地址和值是否超出有效范围如果超出范围则返回-EINVAL表示无效参数。 使用i2c_smbus_write_byte_data函数通过I2C总线向指定寄存器写入指定的值。 返回i2c_smbus_write_byte_data函数的返回值。 该函数的作用是通过使用I2C总线向指定的寄存器写入指定的值以实现设置寄存器的功能。
// 设置指定寄存器的值
static int ov2640_s_register(struct v4l2_subdev *sd,const struct v4l2_dbg_register *reg)
{// 获取i2c_client结构体struct i2c_client *client v4l2_get_subdevdata(sd);if (reg-reg 0xff ||reg-val 0xff)return -EINVAL;// 通过I2C总线向指定寄存器写入指定值return i2c_smbus_write_byte_data(client, reg-reg, reg-val);
}/driver/i2c/i2c-core.c 函数名为 i2c_smbus_write_byte_data接受一个 i2c_client 结构体指针 client、一个 u8 类型的命令 command以及一个 u8 类型的值 value 作为参数。 函数的主要功能如下 定义一个联合体变量 data用于存储要写入的数据。 将参数 value 的值存储在 data 中。 调用 i2c_smbus_xfer 函数传入适配器、地址、标志位以及写入操作的参数包括命令 command 和数据类型为字节型以及存储要写入数据的地址 data。 执行写入操作并将结果返回。 该函数的作用是通过使用 I2C 总线进行写入操作将指定地址的寄存器写入一个字节的数据返回写入操作的结果。 s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value) { union i2c_smbus_data data; // 定义一个联合体变量data data.byte value; // 将value存储在data中 return i2c_smbus_xfer(client-adapter, client-addr, client-flags, // 调用i2c_smbus_xfer函数传入适配器、地址、标志位 I2C_SMBUS_WRITE, command, // 写入操作命令为command I2C_SMBUS_BYTE_DATA, data); // 写入一个字节的数据存储在data中 }
i2c_smbus_xfer
Linux内核驱动程序使用模拟I2CLinux-4.9.88\drivers\i2c\busses\i2c-gpio.c
Linux-5.4\drivers\i2c\busses\i2c-gpio.c
Linux内核真正的I2C控制器驱动程序
IMX6ULL: Linux-4.9.88\drivers\i2c\busses\i2c-imx.cov2640_s_register-i2c_smbus_write_byte_data-i2c_smbus_xfer 函数名为 i2c_smbus_xfer接受一个 i2c_adapter 结构体指针 adapter、一个 u16 类型的地址 addr、一个 unsigned short 类型的标志位 flags、一个 char 类型的读写操作 read_write、一个 u8 类型的命令 command、一个 int 类型的协议 protocol以及一个 union i2c_smbus_data 联合体指针 data 作为参数。 函数的主要功能如下 执行跟踪点追踪记录有关 SMBus 写入和读取操作的信息。 对标志位进行处理只保留 I2C_M_TEN、I2C_CLIENT_PEC 和 I2C_CLIENT_SCCB 三个标志位。 如果适配器的算法中实现了 smbus_xfer 函数则使用该函数进行 SMBus 传输。在仲裁丢失的情况下自动重试直到达到最大重试次数或超时。 如果适配器的算法没有实现 smbus_xfer 函数或返回 -EOPNOTSUPP则回退到使用 i2c_smbus_xfer_emulated 函数进行传输。 执行跟踪点追踪记录有关 SMBus 回复和结果的信息。 返回传输的结果。 该函数的作用是通过使用 I2C 总线进行 SMBus 传输。它根据适配器的支持情况选择合适的传输方法并处理重试和回退逻辑最终返回传输的结果。
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,char read_write, u8 command, int protocol,union i2c_smbus_data *data)
{unsigned long orig_jiffies;int try;s32 res;/* 如果启用则以下两个跟踪点取决于读写和协议。*/trace_smbus_write(adapter, addr, flags, read_write,command, protocol, data);trace_smbus_read(adapter, addr, flags, read_write,command, protocol);/* 仅保留I2C_M_TEN、I2C_CLIENT_PEC和I2C_CLIENT_SCCB标志 */flags I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;if (adapter-algo-smbus_xfer) {i2c_lock_adapter(adapter);/* 在仲裁丢失的情况下自动重试 */orig_jiffies jiffies;for (res 0, try 0; try adapter-retries; try) {res adapter-algo-smbus_xfer(adapter, addr, flags,read_write, command,protocol, data);if (res ! -EAGAIN)break;if (time_after(jiffies,orig_jiffies adapter-timeout))break;}i2c_unlock_adapter(adapter);if (res ! -EOPNOTSUPP || !adapter-algo-master_xfer)goto trace;/** 如果适配器没有实现SMBus操作的本地支持则回退到i2c_smbus_xfer_emulated。*/}res i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,command, protocol, data);trace:/* 如果启用则回复跟踪点取决于读写。*/trace_smbus_reply(adapter, addr, flags, read_write,command, protocol, data);trace_smbus_result(adapter, addr, flags, read_write,command, protocol, res);return res;
}i2c_imx_xfer
smbus_xfer进行 SMBus 传输 /driver/i2c/busses/i2c-imx.c 这个函数是用于在i.MX平台的I2C适配器上执行数据传输操作。它是i.MX系列芯片的I2C驱动程序中的一部分。 该函数的作用如下 开始I2C传输调用i2c_imx_start函数启动I2C传输。它会发送起始条件START信号。 读写数据对于每个消息msgs数组中的每个元素根据消息的属性进行读写操作。如果消息的flags标志指示要读取数据则调用i2c_imx_read函数进行读取否则根据DMA的可用性调用适当的写入函数进行写入操作可以选择使用DMA写入或普通写入。 停止I2C传输调用i2c_imx_stop函数停止I2C传输。它会发送停止条件STOP信号。 返回结果根据传输的成功与否返回相应的结果。如果传输失败则返回负数表示错误否则返回传输的消息数量。
该函数在I2C传输过程中还输出一些调试信息例如控制寄存器I2CR和状态寄存器I2SR的值以及传输消息的序号和结果状态。 总体而言这个函数负责管理i.MX平台上的I2C传输过程处理起始条件、读写数据以及停止条件以实现与I2C设备的通信。
static int i2c_imx_xfer(struct i2c_adapter *adapter,struct i2c_msg *msgs, int num)
{unsigned int i, temp;int result;bool is_lastmsg false;struct imx_i2c_struct *i2c_imx i2c_get_adapdata(adapter);dev_dbg(i2c_imx-adapter.dev, %s\n, __func__);/* Start I2C transfer */result i2c_imx_start(i2c_imx); // 开始I2C传输if (result)goto fail0;/* read/write data */for (i 0; i num; i) { // 读写数据if (i num - 1)is_lastmsg true;if (i) {dev_dbg(i2c_imx-adapter.dev,%s repeated start\n, __func__);temp imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp | I2CR_RSTA; // 重复启动imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);result i2c_imx_bus_busy(i2c_imx, 1);if (result)goto fail0;}dev_dbg(i2c_imx-adapter.dev,%s transfer message: %d\n, __func__, i);/* write/read data */
#ifdef CONFIG_I2C_DEBUG_BUStemp imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);dev_dbg(i2c_imx-adapter.dev,%s CONTROL: IEN%d, IIEN%d, MSTA%d, MTX%d, TXAK%d, RSTA%d\n,__func__,(temp I2CR_IEN ? 1 : 0), (temp I2CR_IIEN ? 1 : 0),(temp I2CR_MSTA ? 1 : 0), (temp I2CR_MTX ? 1 : 0),(temp I2CR_TXAK ? 1 : 0), (temp I2CR_RSTA ? 1 : 0));temp imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);dev_dbg(i2c_imx-adapter.dev,%s STATUS: ICF%d, IAAS%d, IBB%d, IAL%d, SRW%d, IIF%d, RXAK%d\n,__func__,(temp I2SR_ICF ? 1 : 0), (temp I2SR_IAAS ? 1 : 0),(temp I2SR_IBB ? 1 : 0), (temp I2SR_IAL ? 1 : 0),(temp I2SR_SRW ? 1 : 0), (temp I2SR_IIF ? 1 : 0),(temp I2SR_RXAK ? 1 : 0));
#endifif (msgs[i].flags I2C_M_RD)result i2c_imx_read(i2c_imx, msgs[i], is_lastmsg); // 读取else {if (i2c_imx-dma msgs[i].len DMA_THRESHOLD)result i2c_imx_dma_write(i2c_imx, msgs[i]); // DMA写elseresult i2c_imx_write(i2c_imx, msgs[i]); // 写}if (result)goto fail0;}fail0:/* Stop I2C transfer */i2c_imx_stop(i2c_imx); // 停止I2C传输dev_dbg(i2c_imx-adapter.dev, %s exit with: %s: %d\n, __func__,(result 0) ? error : success msg,(result 0) ? result : num);return (result 0) ? result : num;
}i2c_smbus_xfer_emulated
ov2640_s_register-i2c_smbus_write_byte_data-i2c_smbus_xfer-i2c_smbus_xfer_emulated 函数名为 i2c_smbus_xfer接受一个 i2c_adapter 结构体指针 adapter、一个 u16 类型的地址 addr、一个 unsigned short 类型的标志位 flags、一个 char 类型的读写操作 read_write、一个 u8 类型的命令 command、一个 int 类型的协议 protocol以及一个 union i2c_smbus_data 联合体指针 data 作为参数。 函数的主要功能如下 执行跟踪点追踪记录有关 SMBus 写入和读取操作的信息。 对标志位进行处理只保留 I2C_M_TEN、I2C_CLIENT_PEC 和 I2C_CLIENT_SCCB 三个标志位。 如果适配器的算法中实现了 smbus_xfer 函数则使用该函数进行 SMBus 传输。在仲裁丢失的情况下自动重试直到达到最大重试次数或超时。 如果适配器的算法没有实现 smbus_xfer 函数或返回 -EOPNOTSUPP则回退到使用 i2c_smbus_xfer_emulated 函数进行传输。 执行跟踪点追踪记录有关 SMBus 回复和结果的信息。 返回传输的结果。 该函数的作用是通过使用 I2C 总线进行 SMBus 传输。它根据适配器的支持情况选择合适的传输方法并处理重试和回退逻辑最终返回传输的结果。
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data *data)
{/* 需要生成一系列的消息。在写入的情况下我们只需要使用一个消息在读取时我们需要两个消息。我们使用合理的默认值初始化大多数内容以使下面的代码更简单。 */unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX3]; // 第一个消息的缓冲区unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX2]; // 第二个消息的缓冲区int num read_write I2C_SMBUS_READ ? 2 : 1; // 消息数量int i;u8 partial_pec 0; // 部分PECint status;struct i2c_msg msg[2] {{.addr addr, // 地址.flags flags, // 标志.len 1, // 长度.buf msgbuf0, // 缓冲区}, {.addr addr, // 地址.flags flags | I2C_M_RD, // 标志.len 0, // 长度.buf msgbuf1, // 缓冲区},};msgbuf0[0] command; // 命令switch (size) { // 根据size的值进行判断case I2C_SMBUS_QUICK: // 快速模式msg[0].len 0; // 长度为0/* 特殊情况读写字段用作数据 */msg[0].flags flags | (read_write I2C_SMBUS_READ ?I2C_M_RD : 0); // 标志num 1; // 消息数量为1break;case I2C_SMBUS_BYTE: // 字节模式if (read_write I2C_SMBUS_READ) {/* 特殊情况只有读 */msg[0].flags I2C_M_RD | flags; // 标志num 1; // 消息数量为1}break;case I2C_SMBUS_BYTE_DATA: // 字节数据模式if (read_write I2C_SMBUS_READ)msg[1].len 1; // 长度为1else {msg[0].len 2; // 长度为2msgbuf0[1] data-byte; // 数据}break;case I2C_SMBUS_WORD_DATA: // 字数据模式if (read_write I2C_SMBUS_READ)msg[1].len 2; // 长度为2else {msg[0].len 3; // 长度为3msgbuf0[1] data-word 0xff; // 数据低位msgbuf0[2] data-word 8; // 数据高位}break;case I2C_SMBUS_PROC_CALL: // 处理调用num 2; /* 特殊情况 */read_write I2C_SMBUS_READ; // 读操作msg[0].len 3; // 长度为3msg[1].len 2; // 长度为2msgbuf0[1] data-word 0xff; // 数据低位msgbuf0[2] data-word 8; // 数据高位break;case I2C_SMBUS_BLOCK_DATA: // 块数据if (read_write I2C_SMBUS_READ) {msg[1].flags | I2C_M_RECV_LEN; // 接收长度msg[1].len 1; /* 块长度将由底层总线驱动程序添加 */} else {msg[0].len data-block[0] 2; // 长度为块长度2if (msg[0].len I2C_SMBUS_BLOCK_MAX 2) { // 如果长度大于最大块长度2dev_err(adapter-dev,Invalid block write size %d\n, // 输出错误信息data-block[0]);return -EINVAL; // 返回错误}for (i 1; i msg[0].len; i) // 遍历块msgbuf0[i] data-block[i-1]; // 将块中的数据存入缓冲区}break;case I2C_SMBUS_BLOCK_PROC_CALL: // 块处理调用num 2; /* 另一种特殊情况 */read_write I2C_SMBUS_READ; // 读操作if (data-block[0] I2C_SMBUS_BLOCK_MAX) { // 如果块长度大于最大块长度dev_err(adapter-dev,Invalid block write size %d\n, // 输出错误信息data-block[0]);return -EINVAL; // 返回错误}msg[0].len data-block[0] 2; // 长度为块长度2for (i 1; i msg[0].len; i) // 遍历块msgbuf0[i] data-block[i-1]; // 将块中的数据存入缓冲区msg[1].flags | I2C_M_RECV_LEN; // 接收长度msg[1].len 1; /* 块长度将由底层总线驱动程序添加 */break;case I2C_SMBUS_I2C_BLOCK_DATA: // I2C块数据if (read_write I2C_SMBUS_READ) {msg[1].len data-block[0]; // 长度为块长度} else {msg[0].len data-block[0] 1; // 长度为块长度1if (msg[0].len I2C_SMBUS_BLOCK_MAX 1) { // 如果长度大于最大块长度1dev_err(adapter-dev,Invalid block write size %d\n, // 输出错误信息data-block[0]);return -EINVAL; // 返回错误}for (i 1; i data-block[0]; i) // 遍历块msgbuf0[i] data-block[i]; // 将块中的数据存入缓冲区}break;default:dev_err(adapter-dev, Unsupported transaction %d\n, size); // 输出错误信息return -EOPNOTSUPP; // 返回错误}// 判断是否需要计算PECi ((flags I2C_CLIENT_PEC) size ! I2C_SMBUS_QUICK size ! I2C_SMBUS_I2C_BLOCK_DATA);if (i) {/* 如果第一个消息是写则计算PEC */if (!(msg[0].flags I2C_M_RD)) {if (num 1) /* 只有写操作 */i2c_smbus_add_pec(msg[0]);else /* 先写后读 */partial_pec i2c_smbus_msg_pec(0, msg[0]);}/* 如果最后一个消息是读则请求PEC */if (msg[num-1].flags I2C_M_RD)msg[num-1].len;}status i2c_transfer(adapter, msg, num); // 发送消息if (status 0)return status; // 发送失败返回错误/* 如果最后一个消息是读则检查PEC */if (i (msg[num-1].flags I2C_M_RD)) {status i2c_smbus_check_pec(partial_pec, msg[num-1]);if (status 0)return status;}// 根据读写类型进行操作if (read_write I2C_SMBUS_READ)switch (size) {case I2C_SMBUS_BYTE:data-byte msgbuf0[0]; // 将缓冲区中的数据存入data中break;case I2C_SMBUS_BYTE_DATA:data-byte msgbuf1[0]; // 将缓冲区中的数据存入data中break;case I2C_SMBUS_WORD_DATA:case I2C_SMBUS_PROC_CALL:data-word msgbuf1[0] | (msgbuf1[1] 8); // 将缓冲区中的数据存入data中break;case I2C_SMBUS_I2C_BLOCK_DATA:for (i 0; i data-block[0]; i)data-block[i1] msgbuf1[i]; // 将缓冲区中的数据存入data中break;case I2C_SMBUS_BLOCK_DATA:case I2C_SMBUS_BLOCK_PROC_CALL:for (i 0; i msgbuf1[0] 1; i)data-block[i] msgbuf1[i]; // 将缓冲区中的数据存入data中break;}return 0;
}i2c_transfer
ov2640_s_register-i2c_smbus_write_byte_data-i2c_smbus_xfer-i2c_smbus_xfer_emulated-i2c_transfer 这段代码实现了在I2C总线上进行传输的函数i2c_transfer。它是一个通用的I2C传输函数用于向I2C设备发送一系列消息并返回传输的结果。 函数首先检查适配器是否支持master_xfer函数该函数用于执行实际的传输操作。如果适配器支持该函数将进行以下步骤 如果当前在原子上下文中或者中断被禁止函数尝试获取适配器的锁定。如果无法获取锁定表示有其他I2C活动正在进行中函数返回-EAGAIN表示稍后重试。 如果当前不在原子上下文中且中断未被禁止函数获取适配器的锁定。 调用__i2c_transfer函数执行实际的传输操作传递适配器、消息数组和消息数量作为参数。 释放适配器的锁定。 返回传输的结果。 如果适配器不支持master_xfer函数则函数打印一条调试信息并返回-EOPNOTSUPP表示不支持I2C级别的传输操作。 需要注意的是这段代码中存在一些注释提到了错误报告模型的弱点包括在从设备接收字节后出现错误时无法报告接收到的字节数量以及在向从设备传输字节后收到NAK时无法报告已传输的字节数量。这些弱点需要在实际使用中进行进一步的考虑和处理。 总体而言这段代码提供了一个通用的I2C传输函数可以在适配器支持master_xfer函数的情况下进行传输操作并返回传输的结果。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{int ret;/* REVISIT the fault reporting model here is weak:* * - 当我们从从设备接收N个字节后出现错误时没有办法报告“N”。* * - 当我们向从设备传输N个字节后收到NAK时没有办法报告“N”...或者让主设备继续执行此组合消息的其余部分如果这是适当的响应。* * - 例如“num”为2我们成功完成第一个消息但在第二个消息的部分中出现错误不清楚是否应将其报告为一个丢弃第二个消息的状态或errno丢弃第一个消息的状态。*/if (adap-algo-master_xfer) {
#ifdef DEBUGfor (ret 0; ret num; ret) {dev_dbg(adap-dev, master_xfer[%d] %c, addr0x%02x, len%d%s\n, ret, (msgs[ret].flags I2C_M_RD)? R : W, msgs[ret].addr, msgs[ret].len,(msgs[ret].flags I2C_M_RECV_LEN) ? : );}
#endifif (in_atomic() || irqs_disabled()) {ret i2c_trylock_adapter(adap);if (!ret)/* I2C活动正在进行中。 */return -EAGAIN;} else {i2c_lock_adapter(adap);}ret __i2c_transfer(adap, msgs, num);i2c_unlock_adapter(adap);return ret;} else {dev_dbg(adap-dev, I2C level transfers not supported\n);return -EOPNOTSUPP;}
}__i2c_transfer
ov2640_s_register-i2c_smbus_write_byte_data-i2c_smbus_xfer-i2c_smbus_xfer_emulated-i2c_transfer-__i2c_transfer 这个函数用于在给定的I2C适配器上执行一系列I2C消息的传输操作。 函数的主要步骤如下 检查适配器的特殊特性如果适配器具有特殊的quirks特性则调用i2c_check_for_quirks函数检查传入的消息是否需要特殊处理。如果需要特殊处理返回错误码-EOPNOTSUPP表示不支持。 启用跟踪根据跟踪选项的状态决定是否启用消息跟踪。如果启用了消息跟踪使用trace_i2c_read和trace_i2c_write函数跟踪每个读取或写入消息的详细信息。 重试机制在给定的重试次数范围内循环执行适配器的master_xfer函数来执行I2C传输。如果返回值不是-EAGAIN表示仲裁丢失或者超过了指定的超时时间跳出循环。 结果跟踪根据跟踪选项的状态决定是否启用结果跟踪。如果启用了结果跟踪使用trace_i2c_reply和trace_i2c_result函数跟踪每个读取消息的响应以及整体传输结果。 返回结果返回传输操作的结果。
总体而言该函数负责管理I2C传输过程中的重试机制和结果跟踪并调用适配器的master_xfer函数执行实际的传输操作。它还提供了消息跟踪的选项以便在需要时记录传输的详细信息。
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{unsigned long orig_jiffies;int ret, try;if (adap-quirks i2c_check_for_quirks(adap, msgs, num))return -EOPNOTSUPP;/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets* enabled. This is an efficient way of keeping the for-loop from* being executed when not needed.*/if (static_key_false(i2c_trace_msg)) {int i;for (i 0; i num; i)if (msgs[i].flags I2C_M_RD)trace_i2c_read(adap, msgs[i], i);elsetrace_i2c_write(adap, msgs[i], i);}/* Retry automatically on arbitration loss */orig_jiffies jiffies;for (ret 0, try 0; try adap-retries; try) {ret adap-algo-master_xfer(adap, msgs, num);if (ret ! -EAGAIN)break;if (time_after(jiffies, orig_jiffies adap-timeout))break;}if (static_key_false(i2c_trace_msg)) {int i;for (i 0; i ret; i)if (msgs[i].flags I2C_M_RD)trace_i2c_reply(adap, msgs[i], i);trace_i2c_result(adap, i, ret);}return ret;
}设置ov2640的电源ov2640_s_power
这段代码是用于设置ov2640相机设备的电源状态的函数。 函数名为ov2640_s_power接受一个v4l2_subdev结构体指针和一个整数on作为参数。 函数的主要功能如下 从v4l2_subdev结构体中获取i2c_client结构体指针该结构体用于表示I2C设备。 使用i2c_client结构体指针获取soc_camera_subdev_desc结构体指针该结构体用于描述相机子设备。 从i2c_client结构体指针中获取ov2640_priv结构体指针该结构体是ov2640相机设备的私有数据。 调用soc_camera_set_power函数将dev设备、ssdd相机子设备描述、priv-clk时钟和on电源状态作为参数以设置ov2640相机设备的电源状态。 返回soc_camera_set_power函数的返回值。 该函数的作用是通过调用soc_camera_set_power函数来设置ov2640相机设备的电源状态。
// 设置ov2640的电源状态
static int ov2640_s_power(struct v4l2_subdev *sd, int on)
{// 获取i2c_client结构体struct i2c_client *client v4l2_get_subdevdata(sd);// 获取soc_camera_subdev_desc结构体struct soc_camera_subdev_desc *ssdd soc_camera_i2c_to_desc(client);// 获取ov2640_priv结构体struct ov2640_priv *priv to_ov2640(client);// 设置ov2640的电源状态return soc_camera_set_power(client-dev, ssdd, priv-clk, on);
}/include/media/soc_camera.h
// 设置摄像头电源状态
static inline int soc_camera_set_power(struct device *dev,struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
{return on ? soc_camera_power_on(dev, ssdd, clk) // 打开电源: soc_camera_power_off(dev, ssdd, clk); // 关闭电源
}/driver/media/platform/soc_camera.c 这两个函数用于相机电源的开启和关闭操作。下面是对这两个函数的概括总结 soc_camera_power_on函数用于打开相机的电源。 如果提供了时钟并且未平衡电源或时钟状态未设置则使能时钟。 使能寄存器。 如果存在电源回调函数则调用电源回调函数使能相机电源。 如果上述操作都成功则返回0。 如果有任何步骤失败则执行相应的错误处理操作包括禁用寄存器和时钟并返回错误码。 soc_camera_power_off函数用于关闭相机的电源。 如果存在电源回调函数则调用电源回调函数关闭相机电源。 禁用寄存器。 如果提供了时钟并且未平衡电源或时钟状态已设置则禁用时钟。 返回执行过程中的任何错误码。 这两个函数在相机驱动中用于管理相机设备的电源控制包括使能和禁用电源和相关的时钟和寄存器。
int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,struct v4l2_clk *clk)
{int ret;bool clock_toggle;if (clk (!ssdd-unbalanced_power ||!test_and_set_bit(0, ssdd-clock_state))) { // 如果有时钟并且未平衡电源或时钟状态未设置ret v4l2_clk_enable(clk); // 使能时钟if (ret 0) {dev_err(dev, Cannot enable clock: %d\n, ret); // 不能使能时钟return ret;}clock_toggle true; // 时钟切换为真} else {clock_toggle false; // 时钟切换为假}ret regulator_bulk_enable(ssdd-sd_pdata.num_regulators,ssdd-sd_pdata.regulators); // 使能寄存器if (ret 0) {dev_err(dev, Cannot enable regulators\n); // 不能使能寄存器goto eregenable;}if (ssdd-power) { // 如果有电源ret ssdd-power(dev, 1); // 使能电源if (ret 0) {dev_err(dev,Platform failed to power-on the camera.\n); // 不能使能相机电源goto epwron;}}return 0;epwron:regulator_bulk_disable(ssdd-sd_pdata.num_regulators,ssdd-sd_pdata.regulators); // 禁用寄存器
eregenable:if (clock_toggle)v4l2_clk_disable(clk); // 禁用时钟return ret;
}EXPORT_SYMBOL(soc_camera_power_on);/*** soc_camera_power_off() - 关闭相机电源* dev: 设备* ssdd: 相机子设备描述* clk: 时钟* return: 返回值*/
int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,struct v4l2_clk *clk)
{int ret 0;int err;if (ssdd-power) { // 如果有电源err ssdd-power(dev, 0); // 关闭电源if (err 0) {dev_err(dev,Platform failed to power-off the camera.\n); // 不能关闭相机电源ret err;}}err regulator_bulk_disable(ssdd-sd_pdata.num_regulators,ssdd-sd_pdata.regulators); // 禁用寄存器if (err 0) {dev_err(dev, Cannot disable regulators\n); // 不能禁用寄存器ret ret ? : err;}if (clk (!ssdd-unbalanced_power || test_and_clear_bit(0, ssdd-clock_state)))v4l2_clk_disable(clk); // 禁用时钟return ret;
}