罗浮视窗网站建设百姓网推广电话
ov2640子设备核心操作详细分析
文章目录
- ov2640子设备核心操作详细分析
- ov2640_subdev_core_ops核心操作
- 获取寄存器值ov2640_g_register
- 设置寄存器值ov2640_s_register
- i2c_smbus_xfer
- i2c_imx_xfer
- i2c_smbus_xfer_emulated
- i2c_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.c
ov2640_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_MAX+3]; // 第一个消息的缓冲区unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; // 第二个消息的缓冲区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[i+1] = 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, addr=0x%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;
}