当前位置: 首页 > news >正文

ov2640子设备核心操作详细分析

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;
}

相关文章:

ov2640子设备核心操作详细分析

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…...

MATLAB语句实现方阵性质的验证

系列文章目录 MATLAB绘图函数的相关介绍——海底测量、二维与三维图形绘制 MATLAB求函数极限的简单介绍 matlab系统环境思维导图 文章目录 系列文章目录 1. MATLAB语句验证方阵的六个性质如下 2. 六个性质的解释如下 3. 使用随机矩阵进行验证的代码示例如下 总结 前言 …...

使用Springboot AOP进行请求接口异常监控

常用注解 Aspect 切面类 Before 前置 AfterReturning 后置 Around 环绕 AfterThrowing 异常 切入点设置 execution(public * *(..)) 定义任意公共方法的执行 execution(* set*(..)) 定义任何一个以"set"开始的方法的执行 execution(* com.sys.service.UserService…...

【云原生|Kubernetes】05-Pod的存储卷(Volume)

【云原生Kubernetes】05-Pod的存储卷&#xff08;Volume) 文章目录 【云原生Kubernetes】05-Pod的存储卷&#xff08;Volume)简介Volume类型解析emptyDirHostPathgcePersistentDiskNFSiscsiglusterfsceph其他volume 简介 Volume 是Pod 中能够被多个容器访问的共享目录。 Kubern…...

Python实现数据结构

文章目录 一、Python实现数据结构1.1 python实现单向链表1.2 python实现单向循环链表1.3 python实现双向链表 一、Python实现数据结构 1.1 python实现单向链表 singleLinkedList.py class SingleNode:"""the node of single link list"""def …...

esp32CAM环境安装教程---串口驱动安装

前言 &#xff08;1&#xff09;本人安装好arduino 的ESP32环境之后&#xff0c; 发现一直下载不进去程序。一直说Cannot configure port, something went wrong. Original message: PermissionError。 &#xff08;2&#xff09;查阅了很多资料&#xff0c;用了各种办法&#…...

Java中List和Array转换

文章目录 List -> Array1. 调用toArray()方法直接返回一个Object[]数组&#xff1a;2. 给toArray(T[])传入一个类型相同的Array&#xff0c;List内部自动把元素复制到传入的Array中&#xff1a;3. 通过List接口定义的T[] toArray(IntFunction<T[]> generator)方法&…...

如何能确定数据库中root用户的密码是什么

如果您无法确定数据库中 root 用户的密码&#xff0c;有几种方法可以尝试找回或重置密码&#xff1a; 1. 使用已知密码&#xff1a;如果您有之前设置的 root 用户密码&#xff0c;可以使用该密码进行登录。 2. 查找密码文件&#xff1a;在某些情况下&#xff0c;MariaDB 可能…...

由浅入深Netty协议设计与解析

目录 1 为什么需要协议&#xff1f;2 redis 协议举例3 http 协议举例4 自定义协议要素4.1 编解码器4.2 什么时候可以加 Sharable 1 为什么需要协议&#xff1f; TCP/IP 中消息传输基于流的方式&#xff0c;没有边界。 协议的目的就是划定消息的边界&#xff0c;制定通信双方要…...

iptables防火墙(1)

iptables防火墙 一、iptables概述二、netfilter/iptables 关系三、四表五链1.四表2.五链 四、规则链之间的匹配顺序五、规则链内的匹配顺序六、iptables安装与配置七、常用的控制类型八、常用的管理选项九、规则命令1.添加新规则2.查看规则列表3.设置默认策略4.删除规则5.清空规…...

第九章 Productions最佳实践 - Productions开发的最佳实践

文章目录 第九章 Productions最佳实践 - Productions开发的最佳实践Productions开发的最佳实践项目目标项目交付文档 第九章 Productions最佳实践 - Productions开发的最佳实践 Productions开发的最佳实践 本章是一个总体概述&#xff0c;旨在帮助团队成员为从事生产项目做好…...

RocketMQ 怎么实现的消息负载均衡以及怎么能够保证消息被顺序消费

一、RocketMQ 怎么实现的消息负载均衡 RocketMQ是一种开源的分布式消息中间件&#xff0c;它使用了一种称为消息负载均衡的机制来实现消息的分发和消费的负载均衡。RocketMQ的消息负载均衡主要是通过以下两个方面实现的&#xff1a; 消息队列分组&#xff08;Message Queue G…...

【随笔记】全志 T507 PF4 引脚无法被正常设置为中断模式的问题分析

相关信息 硬件平台&#xff1a;全志T507 系统版本&#xff1a;Android 10 / Linux 4.9.170 问题描述&#xff1a;PF4 无法通过标准接口设置为中断模式&#xff0c;而 PF1、PF2、PF3、PF5 正常可用。 分析过程 一开始以为是引脚被其它驱动占用引起&#xff0c;或者该引脚不具…...

人手一个 Midjourney,StableStudio 重磅开源!

人手一个 Midjourney&#xff0c;StableStudio 重磅开源&#xff01; Stability AI 公司在上个月 19 号推出了 Alpha 版本 StableLM 大语言模型&#xff0c;包含了 30 亿和 70 亿参数&#xff0c;并且支持商用。如今他们再次推出了 AI 图像生成平台 StableStudio&#xff0c;这…...

iptables防火墙(2)

iptables防火墙&#xff08;2&#xff09; 一、SNATSNAT应用环境SNAT原理SNAT转换前条件扩展 二、DNATDNAT应用环境DNAT原理DNAT转换前提条件扩展 三、防火墙规则的备份和还原导出&#xff08;备份&#xff09;所有表的规则导入&#xff08;还原&#xff09;规则 一、SNAT SNA…...

Windows和Kali上使用proxychains代理流量

Windows和Kali上使用proxychains代理流量 PS. 本文演示都是在kali进行的&#xff0c;如有出入还请联系我哦1. Linux(Debian)1.1. 检查一下是否有proxychains1.2 修改config文件 2. Linux(Debian)安装proxychians43. Windows3.1 下载3.2 配置 4. Windows下的配置5. 测试 PS. 写这…...

KEYSIGHT MSOS204A 2GHZ 4通道DSOS204A高清晰度示波器

KEYSIGHT是德DSOS204A/MSOS204A高清晰度示波器 附加功能&#xff1a; 2 GHz 带宽&#xff08;可升级&#xff09; 4 个模拟通道和 16 个数字通道 最大存储深度&#xff1a;800 Mpts&#xff08;2 通道&#xff09;&#xff0c;400 Mpts&#xff08;4 通道&#xff09; 最大…...

最新Java适配商城系统

城前端功能展示 商城移动端 后端基于SpringBoot 研发&#xff0c;前端使用 Vue、uniapp开发 前后端分离&#xff0c;支持分布式部署&#xff0c;支持Docker&#xff0c;各个API独立&#xff0c;并且有独立的消费者 api不需要单独部署&#xff0c;只需启动一个jar包就可以正…...

【KVM虚拟化】· virsh管理命令

目录 &#x1f341;libvirt架构概述 &#x1f341;使用virsh管理虚拟机 &#x1f342;常用命令总结 &#x1f341;kvm基本功能管理 &#x1f342;帮助命令 &#x1f342;KVM的配置文件存放目录 &#x1f342;查看虚拟机状态 &#x1f342;虚拟机关机与开机 &#x1f342;强制虚…...

JS Es6中判断b数组对象是否有跟a数组对象相同的数值(例如:id),有的话就过滤掉

如下[数组]对象a和b let a[{id:1,value:this},{id:2,value:is}] let b[{id:1,value:hello},{id:3,value:world}]filter() 方法创建一个新的数组&#xff0c;新数组中的元素是通过检查指定数组中符合条件的所有元素。 some() 方法用于检测数组中的元素是否满足指定条件&#x…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...