I2C总线协议相关的函数详解
源码位置:drivers/i2c/busses/i2c-nomadik.c
通过 掩码“mask”设置寄存器某个几个位的值为1。 掩码中1对应的位置置为1。
static __inline void bit_set_i2c(void __iomem *reg, u32 mask)
{
write(readl(reg) | mask, reg);
}
通过 掩码“mask”设置寄存器某个几个位的值为0。掩码中1对应的位置置为0。
static inline void i2c_clr_bit(void *reg, const uint32_t mask)
{
uint32_t data = readl(reg) & ~mask;
write(reg, data);
}
在i2c_CLR_BIT函数的实现过程中,对mask进行了取反操作。换句话说,在对同一bit位执行操作时,i2C_CLR_BIT和i2C_SET_BIT所使用的掩码是相同的。
static int write_i2c(struct nmk_i2c_dev *dev)
u32 status = 0;
u32 mcr;
u32 irq_mask = 0;
int timeout;
mcr = load_i2c_mcr_reg(dev);
writel(mcr, dev->virtbase + I2C_MCR);
// 读取当前CR值
write( readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR,
dev->virtbase + I2C_CR );
/* enable the controller */
i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
init_completion(&dev->xfer_complete);
/* configure the I2C bus to enable interrupts via mask settings */
irq_mask = (I2C_IT_TXFNE|I2C_IT_TXFOVR|I2C_IT_MAL|I2C_IT_BERR);
/* check if we want to determine whether to initiate single-byte or multi-byte transfer, and set the MTDWS bit (Master Transaction Done Without Stop) */
if (dev->stop) {
irq_mask |= I2C_IT_MTD;
} else {
irq_mask |= I2C_IT_MTD WS;
}
irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask);
writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask,
dev->virtbase + I2C_IMSCR);
timeout = handleI2CTimedOutTimeout(&device->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
if (timeout < 0) {
const char *msg = "wait_for_completion_interruptible_timeout";
dev_err(&dev->pdev->dev, msg, "returned %d waiting for event\n", timeout);
}
status = timeout;
if (timeout == 0) {
/* 如果超时计时为零 */
err_msg = "控制器已超时,请重新初始化硬件配置";
dev_err(handle, err_msg);
}
(void) init_hw(dev);
status = -ETIMEDOUT;
}
return status;
}
注意:其他的驱动也有实现自己的 write_i2c()函数。其过程可能是不一样的。
drivers/i2c/i2c-core.c
/* ----------------------------------------------------
- the functional interface to the i2c busses. 到I2C总线的函数接口
-
*/
/**
- i2c_transfer - execute a single or combined I2C message
- @adap: Handle to I2C bus
- @msgs: One or more messages to execute before STOP is issued to
- terminate the operation; each message begins with a START.
- @num: Number of messages to be executed.
- Returns negative errno, else the number of messages executed.
- Note that there is no requirement that each message be sent to
- the same slave address, although that is the most common model.
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies; //用于记录原始的jiffies值,并且这个值+传输的timeout值,是用来与当前的jiffies进行比较,如果比较超时,就放弃发送msg。
int ret, try;
/* Revisit the fault reporting model here reveals a significant weakness:
*
-
- When N bytes are received from a slave and an error occurs,
- there is no mechanism to report "N".
-
- When NAK is received after transmitting N bytes to a slave,
- there is no way to report "N" ... or allow execution of subsequent messages.
-
- For example, when "num" equals two and we successfully complete
- the first message but encounter an error midway through the second,
- it becomes ambiguous whether this should be reported as one (discarding
- status on the second message) or errno (discarding status on
- the first one). */
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
当in_atomic函数或irqs被禁用时{
/* 该I2C活动正在进行中。 */
return -EAGAIN; //无法获取互斥锁则返回重试错误码
}
否则{强制获取互斥锁
}
/* 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); **//根据重试次数(adapt- >retries),来传输msgs,如果传输成功就break,如果没有传输成功,则try++,继续重传
** if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap- >timeout)) //如果(jiffies-orig_jiffies) 大于 adap- >timeout 就说明超时了, 放弃传输。
break;
}
rt_mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(i2c_transfer);
关于master_xfer 函数,可以见:
drivers/i2c/busses/i2c-sc8800.c: .master_xver is set to the function sc8800_i2c_xver,\n
drivers/i2c/busses/i2c-tiny-usb.c: .master_xver is assigned the function usb_xver,\n // 如果是USB的,则这个函数就是 usb_xver这个函数指针来实现的。\n 这根具体的驱动连接到相应的设备关系上。\n
drivers/i2c/busses/i2c-sc8810.c: .master_xtransfer is configured with the function sc8810_i2c_transfer.
/* i2c bus registration info 注册相关的信息 */
一个静态的常数结构体 sc8810_i2c_algorithm 被初始化为:
master_transfer = sc8810_i2c_xfer , //传输函数(包含传输算法)
functionality = sc8810_i2c_func,
sc8810_i2c_algorithm 在初始化 i2c 的过程中,在探针函数内部设置为 i2c->adap.algo
[i] ioc>Adapt_master= ThisModule
[i] ioc>Adapt_attempts= 4
[ ] Ioc>Cfg_IpAlgorithm= Sc8810_i_IoC_algorithm
[i] Ioc>Cfg_IpAlgorithm_Data= IoC
[i] IoC>Cfg_Dev_Child= Pdv>Dev
从历史数据中回溯分析"ret = adap->algo->master_xfer(adap, msgs, num); ****"这一过程后可知其本质是实现了i2c通信协议的标准算法
drivers/media/video/v4l2-ioctl.c
long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
EXPORT_SYMBOL(video_ioctl2);
函数里调用了**__video_do_ioctl()** 来实现具体功能。
涉及 V4L 子系统中 VIDIOC_STREAMOFF 等宏对应功能的具体说明,请参阅文档中的 ‘drivers/media/video/v4l2-ioctl.c’ 中的相关内容。
static long **video_do_ioctl(struct file *file, unsigned int cmd, void *arg) 的功能将由...详细说明其具体实施细节
drivers/media/video/sprd_dcam/sc8810/**dcam_v4l2.c (此文件位于该位置,并且包含于‘include/media/v4l2-ioctl.h’中)
定义为静态的miscdevice类型的结构体变量dcam_v4l2_dev。
其中.minor成员设置为DCAM_MINOR。
名称属性设置为"sc8800g_dcam"。
其中.fops成员引用外部指针'dcam_fops'。
static const struct v4l2_file_operations dcam_fops = {
.owner = THIS_MODULE,
.open = open,
.release = close,
// .read = read,
// .poll = dcam_poll,
.ioctl = video_ioctl2 , /* V4L2 ioctl handler */ //由V4L2子系统里提供的接口来操作。v4l2-ioctl.h-- >v4l2-ioctl.c(实现了video_ioctl2,)
// .mmap = dcam_mmap,
};
静态常量指针数组 dcam_ioctl_ops 被初始化为:
struct V4L2_ioctl_ops dcam_ioctl_ops = {
.vidioc_g_parm = vidioc_g_parm,
};
该视频流状态的设置由.vidioc_streamon指定。
该视频流状态的设置由.vidioc_streamoff指定。
该视频流裁剪模式由.vidioc_g_crop决定。
该视频流输出配置由.vidIOC_g_output管理。
该查询菜单配置由.vidIOC_querymenu设定。
若定义了CONFIG_VIDEO_V4L1_COMPAT宏,则执行以下操作:
// 用于配置视频缓冲区的相关参数设置为aldi参数类型。
static struct video_device * dcam_template = NULL;
.name assigned as "dcam"
.fops pointer reference to dcam_fops
.minor set to -1
.release assigned the value of video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
int dcam_probe(struct platform_device *pdev)
{
…………
ret = misc_register(&dcam_v4l2_dev);
…………
创建一个平台驱动实例dcam_driver,
其中:
.probe字段被配置为dcam_probe,
.remove字段被配置为dcam_remove,
.driver子部分包括{
.owner属性设定为自己(THIS_MODULE),
.name属性设定为"sc8800g_dcam"
}
int __init dcam_v4l2_init(void)
{
…………
platform_driver_register(&dcam_driver) ;
…………
}
static int vidioc_handle_ctrl(struct v4l2_control *ctrl)
{
……
Sensor_Ioctl(SENSOR_IOCTL_BEFORE_SNAPSHOT, (uint32_t)g_dcam_info. snapshot_m);
……
}
drivers/media/video/sprd_dcam/sensor_drv.c (或者 drivers/media/video/sprd_dcam/ sc8810/sensor_drv.c与其对应的函数实现基本一致)
// Sensor_Ioctl 完成了 向func_tab_ptr 的赋值。这进一步表明 func_tab_ptr 指向了 位IOCTL准备的函数指针结构。
PUBLIC uint32_t Sensor_Ioctl(uint32_t cmd, uint32_t arg) {
…………
func_tab_ptr = s_sensor_info_ptr- >ioctl_func_tab_ptr;
计算地址由以下公式确定:temp = *(uint32_t)*((uint32_t)func_tab_ptr + cmd * BIT_2)。//基于输入参数cmd定位目标函数并执行之;其中被调用的函数利用其对应的参数进行操作。
func_ptr = (SENSOR_IOCTL_FUNC_PTR)temp;
…………
Use the ImgSensor Locking Mechanism to Acquire a Mutex;
return ret_value;
}
在drivers/media/video/sprd_dcam/**sensor_drv.c中声明了一个名为int _Sensor_Identify()的函数,并采用Sensor_GetInforTab获取了一个指针。
LOCAL int _Sensor_Identify(SENSOR_CONFIG_T *sensor_config)
{
该变量被赋值为SENSOR_INFO_T类型的指针,并通过调用Sensor_GetInforTab函数从传感器配置中获取传感器ID。
…………
s_sensor_list_ptr_专业[sensor_config->sensor_id] = sensor_info_tab_ptr[sensor_config->sensor_num];
s_sensor_info ptr_重要 = sensor_info_tab_ptr[sensor_config->sensor_num]; // [该全局指针至关重要,在Sensor_PowerOn等函数中广泛应用]
s_sensor_reg_info_ptr->is_register[sensor_config->sensor_id] = SCI_TRUE;
…………
sensor_info_tab_ptr = (SEn sor_INFO_T**) Sensor_ GetInfor_Tab(sen sor_config -> sen sor_id);
valid_tab_index_max = Sensor_ GetInfor_Tab_Len th(sen sor_config -> sen sor_id) - SE N sor_ONE_I2 C;
Sen sor_I2 C Init(sen sor_config -> sen sor_id);
…………
return SE N sorSUCCESS;
}
arch/arm mach-sc8810 /board-sp8810**/camera_cfg.c ( 或 customization/custom/customer_cfg/sp6820a/kernel/camera/camera_cfg.c) :
实际上,在这两个不同路径下的camera_cfg.c文件大小相同吗?它们都是7718字节。因此可以推断这可能是一个文件的拷贝副本。
PUBLIC SENSOR_INFO_T ** SENSOR_GETINFORMATIONTAB (SENSOR_ID_E sensor_id)
{
SENSOR_INFO_T * SENSOR_INFORMATION_TAB_POINTER = NULL;
switch(sensor_id) { when the sensor id is equal to SENSOR_MAIN, assign the pointer of sensor information tab to point to main sensor information tab and break the loop} when the sensor id is equal to SENSOR_SUB, assign the pointer of sensor information tab to point to sub sensor information tab and break the loop}
………………
return (SENSOR_INFO_T **)**sensor_infor_tab_ptr;
**}
extern SENSOR_INFO_T g_ov5640_yuv_info;
//-----------------------------------------------
// Constant Variables
//-----------------------------------------------/
const SENSOR_INFO_T**main_sensor_infor_tab[]= static const SensorInfoTabMain,
{
offsetof(StructureName, ov5640_YUV) && PNULL,
offsetof(StructureName, OV2655_YUV),
offsetof(StructureName, OV7675_YUV),
offsetof(StructureName, OV2640_YUV)
};
而这个g_ov5640_yuv_info 在下面的文件中被定义,并且被初始化。
drivers/media/video/sprd_dcam/sensor_ov5640.c
PUBLIC SENSOR_INFO_Tg_ov5640_yuv_info =
{
ov5640_I2C_ADDR_W, // service I2C write address
ov5640_I2C_ADDR_R, // service I2C read address
…………
s_ov5640_resolution_Tab_YUV, // point to resolution table information structure
& s_ov5640_ioctl_func_tab, // point to ioctl function table
…………
};
可以看到 **& s_ov5640_ioctl_func_tab ** 被定义为一个指向 IOCTRL 函数表的指针变量。该变量保存了所有 IOCTL 中的函数指针入口地址,并且在文件 sensor_ov5640.c 中同时被定义并初始化。
