源码位置:drivers/i2c/busses/i2c-nomadik.c
通过 掩码“mask”设置寄存器某个几个位的值为1。 掩码中1对应的位置置为1。
static inline void i2c_set_bit(void __iomem *reg, u32 mask)
{
writel(readl(reg) | mask, reg);
}
通过 掩码“mask”设置寄存器某个几个位的值为0。掩码中1对应的位置置为0。
static inline void i2c_clr_bit(void __iomem *reg, u32 mask)
{
writel(readl(reg) & ~mask, reg);
}
i2c_clr_bit()的实现中对mask进行了取反。也就是说如果对同一位进行操作, 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);
/* load the current CR value */
writel(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);
/* enable interrupts by settings the masks */
irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR |
I2C_IT_MAL | I2C_IT_BERR);
/*
* check if we want to transfer a single or multiple bytes, if so
* set the MTDWS bit (Master Transaction Done Without Stop)
* to start repeated start operation
*/
if (dev->stop)
irq_mask |= I2C_IT_MTD;
else
irq_mask |= I2C_IT_MTDWS;
irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask);
writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask,
dev->virtbase + I2C_IMSCR);
timeout = wait_for_completion_interruptible_timeout(
&dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
if (timeout < 0) {
dev_err(&dev->pdev->dev,
"wait_for_completion_interruptible_timeout"
"returned %d waiting for event\n", timeout);
status = timeout;
}
if (timeout == 0) {
/* controler has timedout, re-init the h/w */
dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
(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 is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that 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
if (in_atomic() || irqs_disabled()) { //如果条件成立,就 尝试获取互斥锁
ret = rt_mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN; //获取不成功则返回重试错误码, ret=0 表示获取不成功
} else {
rt_mutex_lock(&adap->bus_lock); //如果条件不成立,则直接建立互斥锁
}
/* 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_xfer = sc8800_i2c_xfer,
drivers/i2c/busses/i2c-tiny-usb.c: .master_xfer = usb_xfer, //如果是USB的,那这个函数就是 usb_xfer这个函数指针来实现的。 这根具体的驱动搭上关系了。
drivers/i2c/busses/i2c-sc8810.c: .master_xfer = sc8810_i2c_xfer,
/* i2c bus registration info 注册相关的信息 */
static const struct i2c_algorithm sc8810_i2c_algorithm = {
.master_xfer = sc8810_i2c_xfer, //传输函数(包含传输算法)
.functionality = sc8810_i2c_func,
};
而 sc8810_i2c_algorithm 在probe函数(static int sc8810_i2c_probe(struct platform_device *pdev))中初始化i2c的时候, 赋值给了 i2c->adap.algo
i2c->adap.owner = THIS_MODULE;
i2c->adap.retries = 4;
i2c->adap.algo = &sc8810_i2c_algorithm;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
再回溯来看“ret = adap->algo->master_xfer(adap, msgs, num); ” ,就明白了这句话,实际上是执行了sc8810_i2c_algorithm
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”)
static struct miscdevice dcam_v4l2_dev = {
.minor = DCAM_MINOR,
.name = "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,
};
static const struct v4l2_ioctl_ops dcam_ioctl_ops = {
.vidioc_g_parm = vidioc_g_parm,
…………
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_g_output = vidioc_g_output,
.vidioc_querymenu = vidioc_querymenu,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
// .vidiocgmbuf = vidiocgmbuf,
#endif
};
static struct video_device dcam_template= {
.name = "dcam",
.fops = &dcam_fops,
.ioctl_ops = &dcam_ioctl_ops,
.minor = -1,
.release = 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);
…………
}static struct platform_driver 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_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来索引到相应的函数,并执行。被执行的函数使用arg指针指向的参数。
func_ptr = (SENSOR_IOCTL_FUNC_PTR)temp;
…………
ImgSensor_GetMutex();
ret_value = func_ptr(arg); //执行相应函数,返回错误码至ret_value
ImgSensor_PutMutex();
…………
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_tab_ptr = (SENSOR_INFO_T**)Sensor_GetInforTab(sensor_config->sensor_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]; //s_sensor_info_ptr 这个全局指针特别重要,Sensor_PowerOn等函数里都用到了。
s_sensor_register_info_ptr->is_register[sensor_config->sensor_id] = SCI_TRUE;
…………
sensor_info_tab_ptr=(SENSOR_INFO_T**)Sensor_GetInforTab(sensor_config->sensor_id);
valid_tab_index_max=Sensor_GetInforTabLenght(sensor_config->sensor_id)-SENSOR_ONE_I2C;
_Sensor_I2CInit(sensor_config->sensor_id);
…………
return SENSOR_SUCCESS;
}
arch/arm/mach-sc8810/board-sp8810/camera_cfg.c ( 或 customize/customer_cfg/sp6820a/kernel/camera/camera_cfg.c) :
其实 以上两个不同路径的 camera_cfg.c的文件大小是一样的 都是7718 ,所以,应该是同一个文件的拷贝。
PUBLIC SENSOR_INFO_T ** Sensor_GetInforTab(SENSOR_ID_E sensor_id)
{
SENSOR_INFO_T * sensor_infor_tab_ptr=NULL;
switch(sensor_id)
{
case SENSOR_MAIN:
{
sensor_infor_tab_ptr=(SENSOR_INFO_T*)&main_sensor_infor_tab;
break;
}
case SENSOR_SUB:
{
sensor_infor_tab_ptr=(SENSOR_INFO_T*)&sub_sensor_infor_tab;
break;
}
………………
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[]= // 结构指针数组
{
&g_ov5640_yuv_info,
&g_OV2655_yuv_info,
&g_OV7675_yuv_info,
&g_OV2640_yuv_info,
PNULL
};
而这个g_ov5640_yuv_info 在下面的文件中被定义,并且被初始化。
drivers/media/video/sprd_dcam/sensor_ov5640.c
PUBLIC SENSOR_INFO_T g_ov5640_yuv_info =
{
ov5640_I2C_ADDR_W, // salve i2c write address
ov5640_I2C_ADDR_R, // salve 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里的函数指针。 它也被定义并被初始化在 sensor_ov5640.c