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; // 定义一个联合体变量data int status; // 定义一个整型变量status status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, // 调用i2c_smbus_xfer函数,传入适配器、地址、标志位 I2C_SMBUS_READ, command, // 读取操作,命令为command I2C_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内核驱动程序:使用模拟I2C Linux-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_BUS temp = 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)); #endif if (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写 else result = 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; // 部分PEC int 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; // 消息数量为1 break; 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; // 长度为1 else { msg[0].len = 2; // 长度为2 msgbuf0[1] = data->byte; // 数据 } break; case I2C_SMBUS_WORD_DATA: // 字数据模式 if (read_write == I2C_SMBUS_READ) msg[1].len = 2; // 长度为2 else { msg[0].len = 3; // 长度为3 msgbuf0[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; // 长度为3 msg[1].len = 2; // 长度为2 msgbuf0[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; // 长度为块长度+2 if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { // 如果长度大于最大块长度+2 dev_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; // 长度为块长度+2 for (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; // 长度为块长度+1 if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) { // 如果长度大于最大块长度+1 dev_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; // 返回错误 } // 判断是否需要计算PEC i = ((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 DEBUG for (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) ? "+" : ""); } #endif if (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); else trace_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; }
如果文章对您有帮助,点赞👍支持,感谢🤝