MicrodiaのdriverでYUYVがVIDIOC_S_FMTできない

ioctlVIDIOC_S_FMT をセットするとerrno 22が返ってくるので調べてみた。

下環境 - OS: Ubuntu 8.04 - Kernel: linux 2.6.24.22 - microdia driver: http://groups.google.com/group/microdia?hl=en - Groovy GR-CAM130N2 - ベンダーID及びプロダクトID 0c45:627b - Bridge : SN9C201 - Image Sensor : OV7660

まずはsystem callのioctlを呼ぶとcallされるkernel内のv4lのioctl関数を発見。 linux-source-2.6.24/drivers/media/video/videodev.c

433 static int __video_do_ioctl(struct inode *inode, struct file *file,
434 unsigned int cmd, void *arg)
435 {

VIDIOC_S_FMTioctl に渡すと以下のcase文にくる。どうも659行目が失敗しているかんじ。

647 case VIDIOC_S_FMT:
648 {
649   struct v4l2_format *f = (struct v4l2_format *)arg;
650 
651   /* FIXME: Should be one dump per type */
652   dbgarg (cmd, "type=%s\n", prt_names(f->type,
653   v4l2_type_names_FIXME));
654 
655   switch (f->type) {
656     case V4L2_BUF_TYPE_VIDEO_CAPTURE:
657       v4l_print_pix_fmt(vfd,&f->fmt.pix);
658      if (vfd->vidioc_s_fmt_cap)
659        ret=vfd->vidioc_s_fmt_cap(file, fh, f);
660        break;

vidioc_s_fmt_capvfd のメンバ変数なので調べてみる。以下の157目が呼ばれる。

84 struct video_device
85 {
・・・
155   /* VIDIOC_S_FMT handlers */
156   int (*vidioc_s_fmt_cap) (struct file *file, void *fh,
157   struct v4l2_format *f);

続いて microdia driver内のコードで vidioc_s_fmt_cap に実体を代入している箇所を調べる。 microdia-v4l.c

1353 int v4l_microdia_register_video_device(struct usb_microdia *dev)
1354 {
・・・
1382   dev->vdev->vidioc_s_fmt_cap = microdia_vidioc_s_fmt_cap;

v4l_microdia_register_video_device 関数内で microdia_vidioc_s_fmt_cap を代入しているので実体を調べる。

1026 int microdia_vidioc_s_fmt_cap(struct file *file, void *priv,
1027 struct v4l2_format *fmt)
1028 {
・・・
1042   ret = microdia_vidioc_try_fmt_cap(file, priv, fmt);
1043   if (ret)
1044     return -EINVAL;

1042行目があやしい・・ ついに、printk を仕込み977行目で EINVAL を返しているのを確認。どうもここが NULL なのがダメみたい。

956 int microdia_vidioc_try_fmt_cap(struct file *file, void *priv,
957 struct v4l2_format *fmt) 
958 {
・・・
970   for (index = 0; index < dev->camera.nfmts; index++) 
971     if (dev->camera.fmts[index].pix_fmt == fmt->fmt.pix.pixelformat)
972      break;
973 
974    if (index >= dev->camera.nfmts)
975      return -EINVAL;
976 
977    if (dev->camera.fmts[index].set_format == NULL)
978      return -EINVAL;

ちょっと fmts の構造体を調べてみる。 microdia-dev.c

208 static struct microdia_video_format default_fmts[] = {
209 {
・・・
243 {
244   .pix_fmt = V4L2_PIX_FMT_YUYV,
245   .desc = "YUV 4:2:2 (YUYV)",
246   .depth = 16,
247   .modes = {
248     {160, 120, SN9C20X_1_4_SCALE,
249     {0, 0, 1280, 480},
250     {0, 0, 320, 120} },
251     {320, 240, SN9C20X_1_2_SCALE,
252     {0, 0, 1280, 480},
253     {0, 0, 640, 240} },
254     {640, 480, SN9C20X_NO_SCALE,
255     {0, 0, 1280, 480},
256     {0, 0, 1280, 480} },
257   },
258   .set_format = NULL
259 },

V4L2_PIX_FMT_YUYV のところをみると .set_formatNULL が・・・。 これは絶対上記の977行目ではじかれるんじゃね?っと思いつつもう少しコードを読んでみるとどうも後から .set_format をセットしてるような箇所を発見した。

262 int dev_microdia_assign_fmts(struct usb_microdia *dev)
263 {
264   int i, j;
265 
266   dev->camera.fmts = default_fmts;
267   dev->camera.nfmts = ARRAY_SIZE(default_fmts);
268 
269   for (i = 0; i < ARRAY_SIZE(default_fmts); i++) {
270     if (dev->camera.sensor->set_yuv422 != NULL) && 271 (dev->camera.fmts[i].pix_fmt == V4L2_PIX_FMT_YUYV)
272     dev->camera.fmts[i].set_format =
273       dev->camera.sensor->set_yuv422;
274     if (dev->camera.sensor->set_bayer != NULL) && 275 (dev->camera.fmts[i].pix_fmt == V4L2_PIX_FMT_SBGGR8)
276    dev->camera.fmts[i].set_format =
277      dev->camera.sensor->set_bayer;
278    for (j = 0; j < N_MODES; j++) {
279      dev->camera.fmts[i].modes[j].hw_window[0] =
280       dev->camera.sensor->hstart;
281      dev->camera.fmts[i].modes[j].hw_window[1] =
282      dev->camera.sensor->vstart;
283     }
284   }
285 
286   return 0;
287 }

pix_fmt == V4L2_PIX_FMT_YUYV なので pix_fmt == V4L2_PIX_FMT_YUYV が存在していたら set_format にセットするようなので sensor 変数を見てみた。

36 static struct sensor_info sensors[] = {
・・・
76 {
77   .id = OV7660_SENSOR,
78   .name = "OV7660",
79   .address = 0x21,
80   .initialize = ov_initialize,
81   .set_exposure = ov7660_set_exposure,
82   .set_auto_gain = ov_set_autogain,
83   .hstart = 1,
84   .vstart = 1,
85   .get_yavg = ov965x_get_yavg,
86   .min_yavg = 30,
87   .max_yavg = 60,
88   .min_stable_yavg = 32,
89   .max_stable_yavg = 58,
90 },

set_format がない。。つまり未定義なので NULL。 ここまできてなんとなくわかったのはYUYVは基本的に未サポートでセンサーごとに実装するようなつくりになっているということ。 つまり、現段階ではOV7660のYUYVは未サポートでした。 (http://groups.google.com/group/microdia/web/project-status?hl=enのStatusはSupportedになっているのに。)

ちなみに以下の箇所をコメントアウトして動作させるとノイズだらけの映像が出力されたとさ

977 if (dev->camera.fmts[index].set_format == NULL)
978   return -EINVAL;