基于v4l2的摄像头驱动笔记

/***********************************************************

一:先贴摄像头应用流程,对应驱动一步步跟:

***********************************************************/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <assert.h>

#include <getopt.h> 

#include <fcntl.h> 

#include <unistd.h>

#include <errno.h>

扫描二维码关注公众号,回复: 4694903 查看本文章

#include <sys/stat.h>

#include <sys/types.h>

#include <sys/time.h>

#include <sys/mman.h>

#include <sys/ioctl.h>

#include <asm/types.h>

#include <linux/videodev2.h>

#include <linux/fb.h>

#define CLEAR(x) memset (&(x), 0, sizeof (x))

 

struct buffer {

    void * start;

    size_t length;

};

 

static char * dev_name = NULL;

static int fd = -1;

struct buffer * buffers = NULL;

static unsigned int n_buffers = 0;

static int time_in_sec_capture=5;

static int fbfd = -1;

static struct fb_var_screeninfo vinfo;

static struct fb_fix_screeninfo finfo;

static char *fbp=NULL;

static long screensize=0;

 

static void errno_exit (const char * s)

{

    fprintf (stderr, "%s error %d, %s/n",s, errno, strerror (errno));

    exit (EXIT_FAILURE);

}

 

static int xioctl (int fd,int request,void * arg)

{

    int r;

    /* Here use this method to make sure cmd success*/

    do r = ioctl (fd, request, arg);

    while (-== r && EINTR == errno);

    return r;

}

 

inline int clip(int value, int min, int max) {

    return (value > max ? max : value < min ? min : value);

  }

 

static void process_image (const void * p){

    //ConvertYUVToRGB321;

    unsigned char* in=(char*)p;

    int width=640;

    int height=480;

    int istride=1280;

    int x,y,j;

    int y0,u,y1,v,r,g,b;

    long location=0;

 

    for ( y = 100; y < height + 100; ++y) {

        for (= 0, x=100; j < width * 2 ; j += 4,+=2) {

          location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +

            (y+vinfo.yoffset) * finfo.line_length;

            

          y0 = in[j];

          u = in[+ 1] - 128; 

          y1 = in[+ 2]; 

          v = in[+ 3] - 128; 

 

          r = (298 * y0 + 409 * v + 128) >> 8;

          g = (298 * y0 - 100 * u - 208 * v + 128) >> 8;

          b = (298 * y0 + 516 * u + 128) >> 8;

        

          fbp[ location + 0] = clip(b, 0, 255);

          fbp[ location + 1] = clip(g, 0, 255);

          fbp[ location + 2] = clip(r, 0, 255); 

          fbp[ location + 3] = 255; 

 

          r = (298 * y1 + 409 * v + 128) >> 8;

          g = (298 * y1 - 100 * u - 208 * v + 128) >> 8;

          b = (298 * y1 + 516 * u + 128) >> 8;

 

          fbp[ location + 4] = clip(b, 0, 255);

          fbp[ location + 5] = clip(g, 0, 255);

          fbp[ location + 6] = clip(r, 0, 255); 

          fbp[ location + 7] = 255; 

          }

        in +=istride;

      }

}

 

static int read_frame (void)

{

    struct v4l2_buffer buf;

    unsigned int i;

 

    CLEAR (buf);

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    buf.memory = V4L2_MEMORY_MMAP;

     /* 11. VIDIOC_DQBUF把数据放回缓存队列*/

    if (-== xioctl (fd, VIDIOC_DQBUF, &buf)) {

        switch (errno) {

        case EAGAIN:

        return 0;

        case EIO: 

        default:

            errno_exit ("VIDIOC_DQBUF");

        }

    }

 

    assert (buf.index < n_buffers);

    printf("v4l2_pix_format->field(%d)/n", buf.field);

    //assert (buf.field ==V4L2_FIELD_NONE);

    process_image (buffers[buf.index].start);

    /*12. VIDIOC_QBUF把数据从缓存中读取出来*/

    if (-== xioctl (fd, VIDIOC_QBUF, &buf))

        errno_exit ("VIDIOC_QBUF");

 

    return 1;

}

 

static void run (void)

{

    unsigned int count;

    int frames;

    frames = 30 * time_in_sec_capture;

 

    while (frames-- > 0) {

        for (;;) {

            fd_set fds;

            struct timeval tv;

            int r;

            FD_ZERO (&fds);

            FD_SET (fd, &fds);

 

            

            tv.tv_sec = 2;

            tv.tv_usec = 0;

             /* 10. poll method*/

            r = select (fd + 1, &fds, NULL, NULL, &tv);

 

            if (-== r) {

                if (EINTR == errno)

                    continue;

                errno_exit ("select");

            }

 

            if (== r) {

                fprintf (stderr, "select timeout/n");

                exit (EXIT_FAILURE);

            }

 

            if (read_frame())

                break;

            

            }

    }

}

 

static void stop_capturing (void)

{

    enum v4l2_buf_type type;

 

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    /*13. VIDIOC_STREAMOFF结束视频显示函数*/

    if (-== xioctl (fd, VIDIOC_STREAMOFF, &type))

        errno_exit ("VIDIOC_STREAMOFF");

}

 

static void start_capturing (void)

{

    unsigned int i;

    enum v4l2_buf_type type;

 

    for (= 0; i < n_buffers; ++i) {

        struct v4l2_buffer buf;

        CLEAR (buf);

 

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        buf.memory = V4L2_MEMORY_MMAP;

        buf.index = i;

         /* 8. VIDIOC_QBUF把数据从缓存中读取出来*/

        if (-== xioctl (fd, VIDIOC_QBUF, &buf))

            errno_exit ("VIDIOC_QBUF");

    }

 

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     /* 9. VIDIOC_STREAMON开始视频显示函数*/

    if (-== xioctl (fd, VIDIOC_STREAMON, &type))

        errno_exit ("VIDIOC_STREAMON");

    

}

 

static void uninit_device (void)

{

    unsigned int i;

 

    for (= 0; i < n_buffers; ++i)

        if (-== munmap (buffers[i].start, buffers[i].length))

            errno_exit ("munmap");

    

    if (-== munmap(fbp, screensize)) {

          printf(" Error: framebuffer device munmap() failed./n");

          exit (EXIT_FAILURE) ;

        } 

    free (buffers);

}

 

 

static void init_mmap (void)

{

    struct v4l2_requestbuffers req;

 

    //mmap framebuffer

    fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0);

    if ((int)fbp == -1) {

        printf("Error: failed to map framebuffer device to memory./n");

        exit (EXIT_FAILURE) ;

    }

    memset(fbp, 0, screensize);

    CLEAR (req);

 

    req.count = 4;

    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    req.memory = V4L2_MEMORY_MMAP;

     /* 6. VIDIOC_REQBUFS分配内存*/

    if (-== xioctl (fd, VIDIOC_REQBUFS, &req)) {

        if (EINVAL == errno) {

            fprintf (stderr, "%s does not support memory mapping/n", dev_name);

            exit (EXIT_FAILURE);

        } else {

            errno_exit ("VIDIOC_REQBUFS");

        }

    }

 

    if (req.count < 4) {

        fprintf (stderr, "Insufficient buffer memory on %s/n",dev_name);

        exit (EXIT_FAILURE);

    }

 

    buffers = calloc (req.count, sizeof (*buffers));

 

    if (!buffers) {

        fprintf (stderr, "Out of memory/n");

        exit (EXIT_FAILURE);

    }

 

    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {

        struct v4l2_buffer buf;

 

        CLEAR (buf);

 

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        buf.memory = V4L2_MEMORY_MMAP;

        buf.index = n_buffers;

         /* 7. VIDIOC_QUERYBUF把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址*/

        if (-== xioctl (fd, VIDIOC_QUERYBUF, &buf))

            errno_exit ("VIDIOC_QUERYBUF");

 

        buffers[n_buffers].length = buf.length;

        buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);

 

        if (MAP_FAILED == buffers[n_buffers].start)

            errno_exit ("mmap");

    }

 

}

 

 

 

static void init_device (void)

{

    struct v4l2_capability cap;

    struct v4l2_cropcap cropcap;

    struct v4l2_crop crop;

    struct v4l2_format fmt;

    unsigned int min;

 

 

    // Get fixed screen information

    if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {

     printf("Error reading fixed information./n");

     exit (EXIT_FAILURE);

    }

 

    // Get variable screen information

    if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {

     printf("Error reading variable information./n");

     exit (EXIT_FAILURE);

    }

    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

     /* 2. VIDIOC_QUERYCAP查询驱动功能*/

    if (-== xioctl (fd, VIDIOC_QUERYCAP, &cap)) {

        if (EINVAL == errno) {

            fprintf (stderr, "%s is no V4L2 device/n",dev_name);

            exit (EXIT_FAILURE);

        } else {

            errno_exit ("VIDIOC_QUERYCAP");

        }

    }

    /* Check if it is a video capture device*/

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {

        fprintf (stderr, "%s is no video capture device/n",dev_name);

        exit (EXIT_FAILURE);

    }

     /* Check if support streaming I/O ioctls*/

    if (!(cap.capabilities & V4L2_CAP_STREAMING)) {

        fprintf (stderr, "%s does not support streaming i/o/n",dev_name);

        exit (EXIT_FAILURE);

    }

 

    CLEAR (cropcap);

    /* Set type*/

    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     /* 3. VIDIOC_CROPCAP查询驱动的修剪能力*/

    /* 这里在vivi驱动中我们没有实现此方法,即不支持此操作*/

    if (== xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {

        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        crop.= cropcap.defrect;

        /* 4. VIDIOC_S_CROP设置视频信号的边框*/

        /* 同样不支持这个操作*/

        if (-== xioctl (fd, VIDIOC_S_CROP, &crop)) {

            switch (errno) {

            case EINVAL: 

            break;

            default:

            break;

            }

        }

    }else { }

 

    CLEAR (fmt);

 

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    fmt.fmt.pix.width = 640; 

    fmt.fmt.pix.height = 480;

    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

     /* 5. VIDIOC_S_FMT设置当前驱动的频捕获格式*/

    if (-== xioctl (fd, VIDIOC_S_FMT, &fmt))

        errno_exit ("VIDIOC_S_FMT");

 

    init_mmap ();

 

}

 

static void close_device (void)

{

    if (-== close (fd))

    errno_exit ("close");

    fd = -1;

    /*14. close method*/

    close(fbfd);

}

 

static void open_device (void)

{

    struct stat st; 

 

    if (-== stat (dev_name, &st)) {

     fprintf (stderr, "Cannot identify '%s': %d, %s/n",dev_name, errno, strerror (errno));

     exit (EXIT_FAILURE);

    }

 

    if (!S_ISCHR (st.st_mode)) {

     fprintf (stderr, "%s is no device/n", dev_name);

     exit (EXIT_FAILURE);

    }

    fbfd = open("/dev/fb0", O_RDWR);

    if (fbfd==-1) {

        printf("Error: cannot open framebuffer device./n");

        exit (EXIT_FAILURE);

    }

 

    /* 1. open the char device */

    fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);

    if (-== fd) {

     fprintf (stderr, "Cannot open '%s': %d, %s/n",dev_name, errno, strerror (errno));

     exit (EXIT_FAILURE);

    }

}

 

static void usage (FILE * fp,int argc,char ** argv)

{

    fprintf (fp,

    "Usage: %s [options]/n/n"

    "Options:/n"

    "-d | --device name Video device name [/dev/video]/n"

    "-h | --help Print this message/n"

    "-t | --how long will display in seconds/n"

    "",

    argv[0]);

}

 

static const char short_options [] = "d:ht:";

static const struct option long_options [] = {

    { "device", required_argument, NULL, 'd' },

    { "help", no_argument, NULL, 'h' },

    { "time", no_argument, NULL, 't' },

    { 0, 0, 0, 0 }

};

 

int main (int argc,char ** argv)

{

    dev_name = "/dev/video0";

    for (;;) 

    {

        int index;

        int c;

 

        c = getopt_long (argc, argv,short_options, long_options,&index);

        if (-== c)

        break;

 

        switch (c) {

        case 0:

        break;

 

        case 'd':

        dev_name = optarg;

        break;

 

        case 'h':

        usage (stdout, argc, argv);

        exit (EXIT_SUCCESS);

        case 't':

        time_in_sec_capture = atoi(optarg);

        break;

 

        default:

        usage (stderr, argc, argv);

        exit (EXIT_FAILURE);

        }

    }

 

    open_device();

    init_device();

    start_capturing();

    run();

    stop_capturing();

    uninit_device();

    close_device();

    exit(EXIT_SUCCESS);

    return 0;

}

 

/***********************************************************

二:跟ioctl对应的驱动接口,通过V4L2接口最终调用fimc控制器回调函数

***********************************************************/

\drivers\media\video\v4l2-dev.c:

static const struct file_operations v4l2_fops = {

.owner = THIS_MODULE,

.read = v4l2_read,//

.write = v4l2_write,

.open = v4l2_open,//对应应用层open函数

.get_unmapped_area = v4l2_get_unmapped_area,

.mmap = v4l2_mmap,

.unlocked_ioctl = v4l2_ioctl,//对应drivers/media/video/v4l2-ioctl.cvideo_ioctl2

#ifdef CONFIG_COMPAT

.compat_ioctl = v4l2_compat_ioctl32,

#endif

.release = v4l2_release,

.poll = v4l2_poll,

.llseek = no_llseek,

};

应用到驱动的调用流程分析:

1

应用:    fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);

驱动:会调用\drivers\media\video\v4l2-dev.c的回调函数 v4l2_open

/* Override for the open function */

static int v4l2_open(struct inode *inode, struct file *filp)

{

struct video_device *vdev;

int ret = 0;

/* Check if the video device is available */

mutex_lock(&videodev_lock);

vdev = video_devdata(filp);

/* return ENODEV if the video device has been removed

   already or if it is not registered anymore. */

if (vdev == NULL || !video_is_registered(vdev)) {

mutex_unlock(&videodev_lock);

return -ENODEV;

}

/* and increase the device refcount */

video_get(vdev);

mutex_unlock(&videodev_lock);

if (vdev->fops->open)

ret = vdev->fops->open(filp);

/* decrease the refcount in case of an error */

if (ret)

video_put(vdev);

return ret;

}

然后标记处会调用此前已经通过video_register_device()注册过的video_device结构体回调函数.,这里是\drivers\media\video\samsung\fimc

struct video_device fimc_video_device[FIMC_DEVICES] = {

[0] = {

.fops = &fimc_fops,

.ioctl_ops = &fimc_v4l2_ops,

.release = fimc_vdev_release,

},

[1] = {

.fops = &fimc_fops,

.ioctl_ops = &fimc_v4l2_ops,

.release = fimc_vdev_release,

},

[2] = {

.fops = &fimc_fops,

.ioctl_ops = &fimc_v4l2_ops,

.release = fimc_vdev_release,

},

};

static const struct v4l2_file_operations fimc_fops = {

.owner = THIS_MODULE,

.open = fimc_open,

.release = fimc_release,

.ioctl = video_ioctl2,

.read = fimc_read,

.write = fimc_write,

.mmap = fimc_mmap,

.poll = fimc_poll,

};

会调用fimc_open

static int fimc_open(struct file *filp)

{

struct fimc_control *ctrl;

struct s3c_platform_fimc *pdata;

struct fimc_prv_data *prv_data;

int in_use;

int ret;

ctrl = video_get_drvdata(video_devdata(filp));

pdata = to_fimc_plat(ctrl->dev);

mutex_lock(&ctrl->lock);

in_use = atomic_read(&ctrl->in_use);

if (in_use >= FIMC_MAX_CTXS || (in_use && 1 != ctrl->id)) {

fimc_err("%s: Device busy.\n", __func__);

ret = -EBUSY;

goto resource_busy;

} else {

atomic_inc(&ctrl->in_use);

}

in_use = atomic_read(&ctrl->in_use);

prv_data = kzalloc(sizeof(struct fimc_prv_data), GFP_KERNEL);

if (!prv_data) {

fimc_err("%s: not enough memory\n", __func__);

ret = -ENOMEM;

goto kzalloc_err;

}

prv_data->ctx_id = fimc_get_free_ctx(ctrl);

if (prv_data->ctx_id < 0) {

fimc_err("%s: Context busy flag not reset.\n", __func__);

ret = -EBUSY;

goto ctx_err;

}

prv_data->ctrl = ctrl;

filp->private_data = prv_data;

if (in_use == 1) {

fimc_clk_en(ctrl, true);

if (pdata->hw_ver == 0x40)

fimc_hw_reset_camera(ctrl);//何时给摄像头发送复位信号(原理图上第4管脚,CAM_RST),因为摄像头只有接到复位信号(高电平或者低电平)才能正常工作。我们这里是在打开v4l2设备的时候,也就是open(“/dev/video0”, RD_WR)时复位的.

/* Apply things to interface register */

fimc_hwset_reset(ctrl);

if (num_registered_fb > 0) {

struct fb_info *fbinfo = registered_fb[0];

ctrl->fb.lcd_hres = (int)fbinfo->var.xres;

ctrl->fb.lcd_vres = (int)fbinfo->var.yres;

fimc_info1("%s: fd.lcd_hres=%d fd.lcd_vres=%d\n",

__func__, ctrl->fb.lcd_hres,

ctrl->fb.lcd_vres);

}

ctrl->mem.curr = ctrl->mem.base;

ctrl->status = FIMC_STREAMOFF;

if (0 != ctrl->id)

fimc_clk_en(ctrl, false);

}

//ctrl->cap->flip |= FIMC_YFLIP;

mutex_unlock(&ctrl->lock);

fimc_info1("%s opened.\n", ctrl->name);

return 0;

ctx_err:

kfree(prv_data);

kzalloc_err:

atomic_dec(&ctrl->in_use);

resource_busy:

mutex_unlock(&ctrl->lock);

return ret;

}

2.  

应用:/*VIDIOC_QUERYCAP查询驱动功能*/

    if (-== xioctl (fd, VIDIOC_QUERYCAP, &cap)) {

驱动:\drivers\media\video\v4l2-dev.c的回调函数 v4l2_ioctl

static int v4l2_ioctl(struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long arg)

{

struct video_device *vdev = video_devdata(filp);

if (!vdev->fops->ioctl)

return -ENOTTY;

/* Allow ioctl to continue even if the device was unregistered.

   Things like dequeueing buffers might still be useful. */

return vdev->fops->ioctl(filp, cmd, arg);

}

然后会调用\drivers\media\video\samsung\fimcvideo_ioctl2函数

 

long video_ioctl2(struct file *file,

       unsigned int cmd, unsigned long arg)

{

char sbuf[128];

void    *mbuf = NULL;

void *parg = (void *)arg;

long err  = -EINVAL;

int     is_ext_ctrl;

size_t  ctrls_size = 0;

void __user *user_ptr = NULL;

#ifdef __OLD_VIDIOC_

cmd = video_fix_command(cmd);

#endif

is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS ||

       cmd == VIDIOC_TRY_EXT_CTRLS);

/*  Copy arguments into temp kernel buffer  */

if (_IOC_DIR(cmd) != _IOC_NONE) {

if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {

parg = sbuf;

} else {

/* too big to allocate from stack */

mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);

if (NULL == mbuf)

return -ENOMEM;

parg = mbuf;

}

err = -EFAULT;

if (_IOC_DIR(cmd) & _IOC_WRITE) {

unsigned long n = cmd_input_size(cmd);

if (copy_from_user(parg, (void __user *)arg, n))

goto out;

/* zero out anything we don't copy from userspace */

if (n < _IOC_SIZE(cmd))

memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);

} else {

/* read-only ioctl */

memset(parg, 0, _IOC_SIZE(cmd));

}

}

if (is_ext_ctrl) {

struct v4l2_ext_controls *p = parg;

/* In case of an error, tell the caller that it wasn't

   a specific control that caused it. */

p->error_idx = p->count;

user_ptr = (void __user *)p->controls;

if (p->count) {

ctrls_size = sizeof(struct v4l2_ext_control) * p->count;

/* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */

mbuf = kmalloc(ctrls_size, GFP_KERNEL);

err = -ENOMEM;

if (NULL == mbuf)

goto out_ext_ctrl;

err = -EFAULT;

if (copy_from_user(mbuf, user_ptr, ctrls_size))

goto out_ext_ctrl;

p->controls = mbuf;

}

}

/* Handles IOCTL */

err = __video_do_ioctl(file, cmd, parg);

if (err == -ENOIOCTLCMD)

err = -EINVAL;

if (is_ext_ctrl) {

struct v4l2_ext_controls *p = parg;

p->controls = (void *)user_ptr;

if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size))

err = -EFAULT;

goto out_ext_ctrl;

}

if (err < 0)

goto out;

out_ext_ctrl:

/*  Copy results into user buffer  */

switch (_IOC_DIR(cmd)) {

case _IOC_READ:

case (_IOC_WRITE | _IOC_READ):

if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))

err = -EFAULT;

break;

}

out:

kfree(mbuf);

return err;

}

然后会调用一个大的swich case,其中会对应应用层的所有ioctl调用:

2xioctl (fd, VIDIOC_QUERYCAP, &cap)

3. xioctl (fd, VIDIOC_CROPCAP, &cropcap)

4. xioctl (fd, VIDIOC_S_CROP, &crop)

5. xioctl (fd, VIDIOC_S_FMT, &fmt)

6.  xioctl (fd, VIDIOC_REQBUFS, &req)

7. xioctl (fd, VIDIOC_QUERYBUF, &buf)

8. xioctl (fd, VIDIOC_QBUF, &buf)

9. xioctl (fd, VIDIOC_STREAMON, &type)

11. xioctl (fd, VIDIOC_DQBUF, &buf)

12. xioctl (fd, VIDIOC_QBUF, &buf)

大都可以在switch case语句中找到对应的参数。

其中以9. VIDIOC_SREAMON为例分析。

当应用调用xioctl (fd, VIDIOC_STREAMON, &type)

驱动通过调用v4l2_dev.cv4l2_ioctl回调函数,再到fimc_dev.cvideo_ioctl2回调函数,再到v4l2_ioctl.c处调用__video_do_ioctl函数,在case找到对应的参数VIDIOC_STREAMON.

static long __video_do_ioctl(struct file *file,

unsigned int cmd, void *arg)

{

case VIDIOC_STREAMON:

{

enum v4l2_buf_type i = *(int *)arg;

 

if (!ops->vidioc_streamon)

break;

dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));

ret = ops->vidioc_streamon(file, fh, i);

break;

}

}

标记处会调用回调函数:

const struct v4l2_ioctl_ops fimc_v4l2_ops = {

.vidioc_querycap = fimc_querycap,

.vidioc_reqbufs = fimc_reqbufs,

.vidioc_querybuf = fimc_querybuf,

.vidioc_g_ctrl = fimc_g_ctrl,

.vidioc_s_ctrl = fimc_s_ctrl,

.vidioc_s_ext_ctrls = fimc_s_ext_ctrls,

.vidioc_cropcap = fimc_cropcap,

.vidioc_g_crop = fimc_g_crop,

.vidioc_s_crop = fimc_s_crop,

.vidioc_streamon= fimc_streamon,

.vidioc_streamoff = fimc_streamoff,

.vidioc_qbuf = fimc_qbuf,

.vidioc_dqbuf = fimc_dqbuf,

.vidioc_enum_fmt_vid_cap = fimc_enum_fmt_vid_capture,

.vidioc_g_fmt_vid_cap = fimc_g_fmt_vid_capture,

.vidioc_s_fmt_vid_cap = fimc_s_fmt_vid_capture,

.vidioc_try_fmt_vid_cap = fimc_try_fmt_vid_capture,

.vidioc_enum_input = fimc_enum_input,

.vidioc_g_input = fimc_g_input,

.vidioc_s_input = fimc_s_input,

.vidioc_g_parm = fimc_g_parm,

.vidioc_s_parm = fimc_s_parm,

.vidioc_queryctrl = fimc_queryctrl,

.vidioc_querymenu = fimc_querymenu,

.vidioc_g_fmt_vid_out = fimc_g_fmt_vid_out,

.vidioc_s_fmt_vid_out = fimc_s_fmt_vid_out,

.vidioc_try_fmt_vid_out = fimc_try_fmt_vid_out,

.vidioc_g_fbuf = fimc_g_fbuf,

.vidioc_s_fbuf = fimc_s_fbuf,

.vidioc_try_fmt_vid_overlay = fimc_try_fmt_overlay,

.vidioc_g_fmt_vid_overlay = fimc_g_fmt_vid_overlay,

.vidioc_s_fmt_vid_overlay = fimc_s_fmt_vid_overlay,

};

static int fimc_streamon(struct file *filp, void *fh, enum v4l2_buf_type i)

{

struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;

struct s3c_platform_fimc *pdata;

int ret = -1;

log_msg(LOG_DBG,"called\n");

pdata = to_fimc_plat(ctrl->dev);

if (i == V4L2_BUF_TYPE_VIDEO_CAPTURE) {

ret = fimc_streamon_capture(fh);

} else if (i == V4L2_BUF_TYPE_VIDEO_OUTPUT) {

ret = fimc_streamon_output(fh);

} else {

fimc_err("V4L2_BUF_TYPE_VIDEO_CAPTURE and "

"V4L2_BUF_TYPE_VIDEO_OUTPUT are only supported\n");

ret = -EINVAL;

}

return ret;

}

//---------------------------------------

int fimc_streamon_capture(void *fh)

{

struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;

struct fimc_capinfo *cap = ctrl->cap;

int rot;

int ret;

fimc_dbg("%s\n", __func__);

log_msg(LOG_DBG,"called\n");

if (!ctrl->cam || !ctrl->cam->sd) {

fimc_err("%s: No capture device.\n", __func__);

return -ENODEV;

}

if (ctrl->status == FIMC_STREAMON) {

fimc_err("%s: Camera already running.\n", __func__);

return -EBUSY;

}

mutex_lock(&ctrl->v4l2_lock);

if (0 != ctrl->id)

fimc_clk_en(ctrl, true);

ctrl->status = FIMC_READY_ON;

cap->irq = 0;

fimc_hwset_enable_irq(ctrl, 0, 1);

if (!ctrl->cam->initialized)

fimc_camera_init(ctrl);

if (ctrl->id != 2 &&

ctrl->cap->fmt.colorspace != V4L2_COLORSPACE_JPEG) {

ret = fimc_camera_start(ctrl);

if (ret < 0) {

fimc_reset_capture(ctrl);

mutex_unlock(&ctrl->v4l2_lock);

return ret;

}

}

fimc_hwset_camera_type(ctrl);

fimc_hwset_camera_polarity(ctrl);

fimc_update_hwaddr(ctrl);

if (cap->fmt.pixelformat != V4L2_PIX_FMT_JPEG) {

fimc_hwset_camera_source(ctrl);

fimc_hwset_camera_offset(ctrl);

fimc_capture_scaler_info(ctrl);

fimc_hwset_prescaler(ctrl, &ctrl->sc);

fimc_hwset_scaler(ctrl, &ctrl->sc);

fimc_hwset_output_colorspace(ctrl, cap->fmt.pixelformat);

fimc_hwset_output_addr_style(ctrl, cap->fmt.pixelformat);

fimc_hwset_output_area(ctrl, cap->fmt.width, cap->fmt.height);

if (cap->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||

cap->fmt.pixelformat == V4L2_PIX_FMT_RGB565)

fimc_hwset_output_rgb(ctrl, cap->fmt.pixelformat);

else

fimc_hwset_output_yuv(ctrl, cap->fmt.pixelformat);

fimc_hwset_output_size(ctrl, cap->fmt.width, cap->fmt.height);

fimc_hwset_output_scan(ctrl, &cap->fmt);

fimc_hwset_output_rot_flip(ctrl, cap->rotate, cap->flip);

rot = fimc_mapping_rot_flip(cap->rotate, cap->flip);

if (rot & FIMC_ROT) {

fimc_hwset_org_output_size(ctrl, cap->fmt.height,

cap->fmt.width);

} else {

fimc_hwset_org_output_size(ctrl, cap->fmt.width,

cap->fmt.height);

}

fimc_hwset_jpeg_mode(ctrl, false);

} else {

fimc_hwset_output_area_size(ctrl, \

fimc_camera_get_jpeg_memsize(ctrl)/2);

fimc_hwset_jpeg_mode(ctrl, true);

}

if (ctrl->cap->fmt.colorspace == V4L2_COLORSPACE_JPEG)

fimc_hwset_scaler_bypass(ctrl);

//print_regs(ctrl);

fimc_start_capture(ctrl);

if (ctrl->cap->fmt.colorspace == V4L2_COLORSPACE_JPEG &&

ctrl->id != 2) {

struct v4l2_control cam_ctrl;

cam_ctrl.id = V4L2_CID_CAM_CAPTURE;

ret = subdev_call(ctrl, core, s_ctrl, &cam_ctrl);

if (ret < 0 && ret != -ENOIOCTLCMD) {

fimc_reset_capture(ctrl);

mutex_unlock(&ctrl->v4l2_lock);

fimc_err("%s: Error in V4L2_CID_CAM_CAPTURE\n", \

__func__);

return -EPERM;

}

}

ctrl->status = FIMC_STREAMON;

mutex_unlock(&ctrl->v4l2_lock);

return 0;

}

static int fimc_camera_init(struct fimc_control *ctrl)

{

int ret;

fimc_dbg("%s\n", __func__);

/* do nothing if already initialized */

if (ctrl->cam->initialized)

return 0;

/* enable camera power if needed */

if (ctrl->cam->cam_power)

ctrl->cam->cam_power(1);

/* subdev call for init */

ret = subdev_call(ctrl, core, init, 0);

if (ret == -ENOIOCTLCMD) {

fimc_err("%s: init subdev api not supported\n",

__func__);

return ret;

}

if (ctrl->cam->type == CAM_TYPE_MIPI) {

/* subdev call for sleep/wakeup:

 * no error although no s_stream api support

 */

u32 pixelformat;

if (ctrl->cap->fmt.pixelformat == V4L2_PIX_FMT_JPEG)

pixelformat = V4L2_PIX_FMT_JPEG;

else

pixelformat = ctrl->cam->pixelformat;

subdev_call(ctrl, video, s_stream, 0);

s3c_csis_start(ctrl->cam->mipi_lanes, ctrl->cam->mipi_settle, \

ctrl->cam->mipi_align, ctrl->cam->width, \

ctrl->cam->height, pixelformat);

subdev_call(ctrl, video, s_stream, 1);

}

ctrl->cam->initialized = 1;

return 0;

}

#define subdev_call(ctrl, o, f, args...) \

v4l2_subdev_call(ctrl->cam->sd, o, f, ##args) 

这里实际调用的是camera.init回调函数。

ov9650摄像头为例。13583432061 张晓

操作接口:

static struct v4l2_subdev_ops sensor_subdev_ops = {

.core = &sensor_subdev_core_ops,

.video = &sensor_subdev_video_ops,

};

调用接口回调函数.init初始化摄像头:

static struct v4l2_subdev_core_ops sensor_subdev_core_ops = {

.init= sensor_init,

.g_ctrl = sensor_g_control,

.s_ctrl= sensor_s_control,

.g_ext_ctrls          = sensor_g_ext_controls,

.s_ext_ctrls          = sensor_s_ext_controls,

.g_chip_ident = sensor_g_chip_ident,

};

/***************************************************************

分割线,以上简单介绍了camera从应用层到底层驱动的调用流程,网上参考+自己的理解,因为只是作为笔记的形式记录,难免不够认真,还有错误的地方,见谅请告知,谢谢~~

************************************************************/

/********************************以下camera驱动框架**********************/

//设备层及平台数据信息:

arch\arm\mach-s5pv210/mach-cw210.c

static void __init smdkc110_machine_init(void)

{

s3c_fimc0_set_platdata(&fimc_plat_lsi);//设置平台数据

s3c_fimc1_set_platdata(&fimc_plat_lsi);

s3c_fimc2_set_platdata(&fimc_plat_lsi);

}

static struct s3c_platform_fimc fimc_plat_lsi = {

.srclk_name = "mout_mpll",

.clk_name = "sclk_fimc",

.lclk_name = "sclk_fimc_lclk",

.clk_rate = 166750000,

.default_cam = CAMERA_PAR_A,

.camera = {

#ifdef CONFIG_CAMERA_OV3640

&ov3640,

#endif

#ifdef CONFIG_CAMERA_OV9650

&ov9650, //fimc下所包含camera

#endif

#ifdef CONFIG_CAMERA_SAA7113

&saa7113,

#endif

#ifdef CONFIG_CAMERA_TVP5150

&tvp5150,

#endif

},

.hw_ver = 0x43,

};

 

static struct s3c_platform_camera ov9650 = {//camera的相关平台信息

.id = CAMERA_PAR_A,

.type = CAM_TYPE_ITU,

.fmt = ITU_601_YCBCR422_8BIT,

.order422 = CAM_ORDER422_8BIT_CBYCRY,

.i2c_busnum = IIC_NUM_CAM_USED,

.info = &ov9650_i2c_info,

.pixelformat = V4L2_PIX_FMT_VYUY,

.srclk_name = "mout_mpll",

.clk_name = "sclk_cam0",

.clk_rate = 24000000,

// .line_length = 640,

.line_length = 1920,

.width = 640,

.height = 480,

.window = {

.left = 0,

.top = 0,

.width = 640,

.height= 480,

},

/* Polarity */

.inv_pclk = 0,

.inv_vsync = 0,

.inv_href = 0,

.inv_hsync = 0,

.initialized = 0,

// .cam_power = smdkv210_OV9650_power,

.cam_power = tqcam_OV9650_power,

};

void __init s3c_fimc0_set_platdata(struct s3c_platform_fimc *pd)

{

struct s3c_platform_fimc *npd;

if (!pd)

pd = &default_fimc0_data;

npd = kmemdup(pd, sizeof(struct s3c_platform_fimc), GFP_KERNEL);

if (!npd)

printk(KERN_ERR "%s: no memory for platform data\n", __func__);

else {

if (!npd->cfg_gpio)

npd->cfg_gpio = s3c_fimc0_cfg_gpio;

if (!npd->clk_on)

npd->clk_on = s3c_fimc_clk_on;

if (!npd->clk_off)

npd->clk_off = s3c_fimc_clk_off;

npd->hw_ver = 0x45;

/* starting physical address of memory region */

npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMC0, 1);

/* size of memory region */

npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMC0, 1);

s3c_device_fimc0.dev.platform_data = npd;//fimc的相关信息传递给平台数据

}

}

struct platform_device s3c_device_fimc0 = {

.name = "s3c-fimc",

.id = 0,

.num_resources = ARRAY_SIZE(s3c_fimc0_resource),

.resource = s3c_fimc0_resource,

};

static struct resource s3c_fimc0_resource[] = {

[0] = {

.start = S5P_PA_FIMC0,

.end = S5P_PA_FIMC0 + S5P_SZ_FIMC0 - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_FIMC0,

.end = IRQ_FIMC0,

.flags = IORESOURCE_IRQ,

},

};

//以上就把两个结构体合并到了一起,然后传递给平台数据。其中同时包含了fimccamera(ov9650)的信息

//驱动层及相关调用流程:

//fimc的驱动架构如下:

static int fimc_register(void)

{

platform_driver_register(&fimc_driver);//注册fimc控制器驱动

return 0;

}

static struct platform_driver fimc_driver = {

.probe = fimc_probe,

.remove = fimc_remove,

.suspend = fimc_suspend,

.resume = fimc_resume,

.driver = {

.name = FIMC_NAME,

.owner = THIS_MODULE,

},

};

static int __devinit fimc_probe(struct platform_device *pdev)

{

struct s3c_platform_fimc *pdata;

struct fimc_control *ctrl;

struct clk *srclk;

int ret;

if (!fimc_dev) {

fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);

if (!fimc_dev) {

dev_err(&pdev->dev, "%s: not enough memory\n",

__func__);

return -ENOMEM;

}

}

ctrl = fimc_register_controller(pdev);//填充fimc_control结构体

if (!ctrl) {

printk(KERN_ERR "%s: cannot register fimc\n", __func__);

goto err_alloc;

}

pdata = to_fimc_plat(&pdev->dev); //获得平台信息

if (pdata->cfg_gpio)

pdata->cfg_gpio(pdev);

/* Get fimc power domain regulator */

ctrl->regulator = regulator_get(&pdev->dev, "pd");

if (IS_ERR(ctrl->regulator)) {

fimc_err("%s: failed to get resource %s\n",

__func__, "s3c-fimc");

return PTR_ERR(ctrl->regulator);

}

/* fimc source clock */

srclk = clk_get(&pdev->dev, pdata->srclk_name);//时钟信息

if (IS_ERR(srclk)) {

fimc_err("%s: failed to get source clock of fimc\n",

__func__);

goto err_v4l2;

}

/* fimc clock */

ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);

if (IS_ERR(ctrl->clk)) {

fimc_err("%s: failed to get fimc clock source\n",

__func__);

goto err_v4l2;

}

/* set parent for mclk */

clk_set_parent(ctrl->clk, srclk);

/* set rate for mclk */

clk_set_rate(ctrl->clk, pdata->clk_rate);

/* V4L2 device-subdev registration */

ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);//好像只是初始化V4L2_DEVICE结构体

if (ret) {

fimc_err("%s: v4l2 device register failed\n", __func__);

goto err_fimc;

}

/* things to initialize once */

if (!fimc_dev->initialized) {

ret = fimc_init_global(pdev);

if (ret)

goto err_v4l2;

}

/* video device register */

ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);//注册基于v4l2的(video_device)设备

if (ret) {

fimc_err("%s: cannot register video driver\n", __func__);

goto err_v4l2;

}

video_set_drvdata(ctrl->vd, ctrl);

ret = device_create_file(&(pdev->dev), &dev_attr_log_level);

if (ret < 0) {

fimc_err("failed to add sysfs entries\n");

goto err_global;

}

printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);

return 0;

err_global:

video_unregister_device(ctrl->vd);

err_v4l2:

v4l2_device_unregister(&ctrl->v4l2_dev);

err_fimc:

fimc_unregister_controller(pdev);

err_alloc:

kfree(fimc_dev);

return -EINVAL;

}

static

struct fimc_control *fimc_register_controller(struct platform_device *pdev)

{

struct s3c_platform_fimc *pdata;

struct fimc_control *ctrl;

struct resource *res;

int id, mdev_id;

id = pdev->id;

mdev_id = S5P_MDEV_FIMC0 + id;

pdata = to_fimc_plat(&pdev->dev);//得到平台数据

ctrl = get_fimc_ctrl(id);////获得id号对应的fimc_control结构体指针

ctrl->id = id;

ctrl->dev = &pdev->dev;

ctrl->vd = &fimc_video_device[id];//fimc注册的主要结构体video_device

ctrl->vd->minor = id;

...

return ctrl;

}

struct video_device fimc_video_device[FIMC_DEVICES] = {

[0] = {

.fops = &fimc_fops,//两个fimc主要操作接口

.ioctl_ops = &fimc_v4l2_ops,

.release = fimc_vdev_release,

},

[1] = {

.fops = &fimc_fops,

.ioctl_ops = &fimc_v4l2_ops,

.release = fimc_vdev_release,

},

[2] = {

.fops = &fimc_fops,

.ioctl_ops = &fimc_v4l2_ops,

.release = fimc_vdev_release,

},

};

static const struct v4l2_file_operations fimc_fops = {

.owner = THIS_MODULE,

.open = fimc_open,//上面已经介绍过

.release = fimc_release,

.ioctl = video_ioctl2,//同上

.read = fimc_read,

.write = fimc_write,

.mmap = fimc_mmap,

.poll = fimc_poll,

};

const struct v4l2_ioctl_ops fimc_v4l2_ops = {

.vidioc_querycap= fimc_querycap,//查询设备能力

.vidioc_reqbufs= fimc_reqbufs,//申请缓存

.vidioc_querybuf= fimc_querybuf,//将申请的缓存转换为物理地址

.vidioc_g_ctrl = fimc_g_ctrl,

.vidioc_s_ctrl = fimc_s_ctrl,

.vidioc_s_ext_ctrls = fimc_s_ext_ctrls,

.vidioc_cropcap = fimc_cropcap,

.vidioc_g_crop = fimc_g_crop,

.vidioc_s_crop = fimc_s_crop,

.vidioc_streamon= fimc_streamon,//同上,其他标记都可通过应用接口调用到这里

.vidioc_streamoff= fimc_streamoff,//结束流

.vidioc_qbuf= fimc_qbuf,//取帧

.vidioc_dqbuf= fimc_dqbuf,

.vidioc_enum_fmt_vid_cap = fimc_enum_fmt_vid_capture,

.vidioc_g_fmt_vid_cap = fimc_g_fmt_vid_capture,

.vidioc_s_fmt_vid_cap = fimc_s_fmt_vid_capture,

.vidioc_try_fmt_vid_cap = fimc_try_fmt_vid_capture,

.vidioc_enum_input = fimc_enum_input,

.vidioc_g_input = fimc_g_input,

.vidioc_s_input= fimc_s_input,

.vidioc_g_parm = fimc_g_parm,

.vidioc_s_parm = fimc_s_parm,

.vidioc_queryctrl = fimc_queryctrl,

.vidioc_querymenu = fimc_querymenu,

.vidioc_g_fmt_vid_out = fimc_g_fmt_vid_out,

.vidioc_s_fmt_vid_out = fimc_s_fmt_vid_out,

.vidioc_try_fmt_vid_out = fimc_try_fmt_vid_out,

.vidioc_g_fbuf = fimc_g_fbuf,

.vidioc_s_fbuf = fimc_s_fbuf,

.vidioc_try_fmt_vid_overlay = fimc_try_fmt_overlay,

.vidioc_g_fmt_vid_overlay = fimc_g_fmt_vid_overlay,

.vidioc_s_fmt_vid_overlay = fimc_s_fmt_vid_overlay,

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////还有一个疑问的地方,不知道中间调用环节过程,但涉及很重要的知识点,记录笔记标记下:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

应用通过ioctl调用VIDIOC_S_INPUT参数,作用是查询和选则当前的视频输入。

也就是上边红色标记处。

int fimc_s_input(struct file *file, void *fh, unsigned int i)

{

struct fimc_global *fimc = get_fimc_dev();

struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;

int ret = 0;

printk("%s: index %d\n", __func__, i);

if (i < 0 || i >= FIMC_MAXCAMS) {

fimc_err("%s: invalid input index\n", __func__);

return -EINVAL;

}

if (!fimc->camera_isvalid[i])

return -EINVAL;

if (fimc->camera[i].sd && ctrl->id != 2) {

fimc_err("%s: Camera already in use.\n", __func__);

return -EBUSY;

}

mutex_lock(&ctrl->v4l2_lock);

/* If ctrl->cam is not NULL, there is one subdev already registered.

 * We need to unregister that subdev first.

 */

printk("fimc->active_camera=%d\n",fimc->active_camera);

// if(flag == -1)

if (fimc->active_camera == (-1))

{

for(i=0;i<FIMC_MAXCAMS;++i)

{

printk("detect %d cam,fimc->active_camera=%d\n",i,fimc->active_camera);

{

fimc_release_subdev(ctrl);

ctrl->cam = &fimc->camera[i];

ctrl->cam->cam_power(1);

ret = fimc_configure_subdev(ctrl);//通过fimc配置V4L2子设备

ctrl->cam->cam_power(0);

printk("%s:i=%d rtn (%d)\n",__func__,i,ret);

if (ret == 0) {

fimc->active_camera = i;

break;

}

}

}

if(i == FIMC_MAXCAMS)

{

mutex_unlock(&ctrl->v4l2_lock);

fimc_err("%s: Could not register camera sensor "

"with V4L2.\n", __func__);

return -ENODEV;

}

}

if (ctrl->id == 2) {

if (i == fimc->active_camera) {

ctrl->cam = &fimc->camera[i];

} else {

mutex_unlock(&ctrl->v4l2_lock);

return -EINVAL;

}

}

// ctrl->cap->flip |= FIMC_YFLIP;

mutex_unlock(&ctrl->v4l2_lock);

return 0;

}

static int fimc_configure_subdev(struct fimc_control *ctrl)

{

struct i2c_adapter *i2c_adap;

struct i2c_board_info *i2c_info;

struct v4l2_subdev *sd;

unsigned short addr;

char *name;

/* set parent for mclk */

if (clk_get_parent(ctrl->cam->clk->parent))

clk_set_parent(ctrl->cam->clk->parent, ctrl->cam->srclk);

/* set rate for mclk */

if (clk_get_rate(ctrl->cam->clk))

clk_set_rate(ctrl->cam->clk, ctrl->cam->clk_rate);

i2c_adap = i2c_get_adapter(ctrl->cam->i2c_busnum);

if (!i2c_adap)

fimc_err("subdev i2c_adapter missing-skip registration\n");

i2c_info = ctrl->cam->info;

if (!i2c_info) {

fimc_err("%s: subdev i2c board info missing\n", __func__);

return -ENODEV;

}

name = i2c_info->type;

if (!name) {

fimc_err("subdev i2c driver name missing-skip registration\n");

return -ENODEV;

}

addr = i2c_info->addr;

if (!addr) {

fimc_err("subdev i2c address missing-skip registration\n");

return -ENODEV;

}

/*

 * NOTE: first time subdev being registered,

 * s_config is called and try to initialize subdev device

 * but in this point, we are not giving MCLK and power to subdev

 * so nothing happens but pass platform data through

 */

sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap,

name, i2c_info, &addr);//camera同时注册为i2cv4l2设备

if (!sd) {

fimc_err("%s: v4l2 subdev board registering failed\n",

__func__);

}

/* Assign subdev to proper camera device pointer */

ctrl->cam->sd = sd;

if(sd == NULL)

return -1;

return 0;

}

/* Load an i2c sub-device. */

struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,

struct i2c_adapter *adapter, const char *module_name,

struct i2c_board_info *info, const unsigned short *probe_addrs)

{

struct v4l2_subdev *sd = NULL;

struct i2c_client *client;

BUG_ON(!v4l2_dev);

if (module_name)

request_module(module_name);

/* Create the i2c client */

if (info->addr == 0 && probe_addrs)

client = i2c_new_probed_device(adapter, info, probe_addrs);

else

client = i2c_new_device(adapter, info);//添加client设备到i2c总线

/* Note: by loading the module first we are certain that c->driver

   will be set if the driver was found. If the module was not loaded

   first, then the i2c core tries to delay-load the module for us,

   and then c->driver is still NULL until the module is finally

   loaded. This delay-load mechanism doesn't work if other drivers

   want to use the i2c device, so explicitly loading the module

   is the best alternative. */

if (client == NULL || client->driver == NULL)

goto error;

/* Lock the module so we can safely get the v4l2_subdev pointer */

if (!try_module_get(client->driver->driver.owner))

goto error;

sd = i2c_get_clientdata(client);

/* Register with the v4l2_device which increases the module's

   use count as well. */

if (v4l2_device_register_subdev(v4l2_dev, sd))//注册v4l2设备

sd = NULL;

/* Decrease the module use count to match the first try_module_get. */

module_put(client->driver->driver.owner);

if (sd) {

/* We return errors from v4l2_subdev_call only if we have the

   callback as the .s_config is not mandatory */

int err = v4l2_subdev_call(sd, core, s_config,//此处会调用到camera.s_config回调函数

info->irq, info->platform_data);

if (err && err != -ENOIOCTLCMD) {

v4l2_device_unregister_subdev(sd);

sd = NULL;

}

}

error:

/* If we have a client but no subdev, then something went wrong and

   we must unregister the client. */

if (client && sd == NULL)

i2c_unregister_device(client);

return sd;

}

//camera mt9v011 driver:

include/media/ v4l2-i2c-drv.h://此处驱动注册时通过引用头文件的方式,跟平常的驱动注册一个道理

 

 module_init(v4l2_i2c_drv_init);

static int __init v4l2_i2c_drv_init(void)
        60 {
        61        v4l2_i2c_driver.driver.name = v4l2_i2c_data.name;
        62        v4l2_i2c_driver.command = v4l2_i2c_data.command;
        63        v4l2_i2c_driver.probe = v4l2_i2c_data.probe;
        64        v4l2_i2c_driver.remove = v4l2_i2c_data.remove;
        65        v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend;
        66        v4l2_i2c_driver.resume = v4l2_i2c_data.resume;
        67        v4l2_i2c_driver.id_table = v4l2_i2c_data.id_table;
        68        return i2c_add_driver(&v4l2_i2c_driver);
        69 }

 

//

static struct v4l2_i2c_driver_data v4l2_i2c_data = {

.name = "mt9v011",

.probe = mt9v011_probe,

.remove = mt9v011_remove,

.id_table = mt9v011_id,

};

/****************************************************************************

I2C Client & Driver

 ****************************************************************************/

static int mt9v011_probe(struct i2c_client *c,

 const struct i2c_device_id *id)

{

u16 version;

struct mt9v011 *core;

struct v4l2_subdev *sd;

/* Check if the adapter supports the needed features */

if (!i2c_check_functionality(c->adapter,

     I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))

return -EIO;

core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);

if (!core)

return -ENOMEM;

sd = &core->sd;

v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);//使v4l2子设备和i2c client关联

/* Check if the sensor is really a MT9V011 */

version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);

if ((version != MT9V011_VERSION) &&

    (version != MT9V011_REV_B_VERSION)) {

v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",

  version);

kfree(core);

return -EINVAL;

}

core->global_gain = 0x0024;

core->width  = 640;

core->height = 480;

core->xtal = 27000000; /* Hz */

v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",

 c->addr << 1, c->adapter->name, version);

return 0;

}

猜你喜欢

转载自blog.csdn.net/a1028732302/article/details/46337693