I2C总线协议相关的函数详解

源码位置: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

 

你可能感兴趣的:(嵌入式技术)