
Connectix QuickCam VC Video4Linux Device Driver
===============================================
Information


Copyright (C) 2001  De Marchi Daniele <demarchidaniele@libero.it>
                    released under GNU GPL.

February 2002, written by Daniele De Marchi


Table of Contents

1.0 Introduction
2.0 Compilation, Installation,  Configuration
9.0 Sample Program, v4lgrab (the source is not mine)
10.0 Other Information


1.0 Introduction

  This files are the device driver for the Connectix QuickCam VC.
 This driver was develop for the Linux Kernel 2.4.0 and may be
 revisited for newer versions of that kernel.
 Unfortunately Logitech don't provide any technical information
 about the camera.

1.1 Motivation

  I was tired to see my camera in a corner because my favorite os
 is Linux. I was waiting for somebody that develop a driver, then i 
 think somebody was waiting for me.
 I hear Windows NT also cannot run USB version of this camera.
 This driver have cost me a lot of time, but offer it to the Linux
 comunity will repay me 10 time more.
 I am not a kernel guru, and i didn't understand usb at 100% but
 trying and trying something may enter in my head;-)

2.0 Compilation, Installation, and Configuration

 You can follow two way to get the driver compiled.
 The first is to patch your kernel (the current patch was written on
  a linux-vanilla kernel 2.4.17), it would be the solution for not-intel
  architectures.

  supposed linux 2.4.17 sources in /usr/src/linux

  cd /usr/src
  bzip2 -cd quickcam-vc-1.0.5.tar.bz2 | tar -xv
  patch -p0 < quickcam-vc-1.0.5/patches/kernel-patch
  ... at this poit follow the common procedure to configure and compile your kernel.
  ... i suggest to compile the support as external modules.


 Another way is described below and don't require any kernel modification.
 This driver need the usb/parallel and videodev support on a V2.4.x 
 kernel (look the modules tree in this section).
 Early versions of Linux kernel are not supported, but some patches 
 will be accepted and merged.

 I suggest to use modules for that supports, in particular you must have

  usb-uhci or usb-ohci or uhci
  and videodev

 depends from your hardware. You must be able to modprobe this modules 
 before to proced with the compilation and installation.
 If you find problems for that you may look in the Documentation directory 
 of the kernel tree.

 When the kernel is ready you have to do
  
   ./configure
   make
   make install
 
 from the driver directory.

 At the end you will have some modules ready to be modprobed.
 Now you must insert the usb and videodev modules with 
 "modprobe videodev" and "modprobe usb-uhci" or "modprobe usb-ohci" 
 or "modprobe uhci" (this last module is an alternative to usb-uhci but
 i see it run slowly).
 Now we are ready to load the quickcam module with "modprobe qcamvc".
 The driver is organized with an communication indipendent (qcamvc) module 
 and two low level modules (qcamvc_usb and qcamvc_pp) that connect the 
 first module to the cams through the parallel interface or the USB.
 Run a video4linux client like gqcam and try to grab an image file.

 Modules tree.

 User   v4l client ->(xawtv, gqcam, ...)         
 space                      | |    
                      ________________
 Kernel v4l server -->   videodev.o  
 space                      | |     ___________________
                         qcamvc.o      
                       /          \            QuickCam VC Driver
                qcamvc_pp.o     qcamvc_usb.o      _____
                     /              \
               parport.o           usb.o
                   /                  \
             parport_pc.o   (usb-uhci.o or usb-ohci.o)
                 /                      \   
             ________________________________ 
 Hardware      /                          \
         parallel camera               usb-camera


2.1 Module Configuration  

  Using modules requires a bit of work to install and pass the
parameters.  Read the $kernel_dir/Documentation/modules.txt, and 
understand that entries in /etc/modules.conf of:

   alias char-major-81 videodev
   alias char-major-81-0 qcamvc

If you are using kmod or kerneld, then a request for a 'char-major-81-0' 
will cause the 'qcamvc' module to load.  If you have other video sources with
modules, you might want to assign the different minor numbers to
different modules.

2.2 Device Configuration

Video4linux use the /dev/video* files, and the driver need one per camera.

   ls -lad /dev/video*  # produce a list of the video devices

If the video devices do not exist, you can create them with:

  su
  cd /dev
  for ii in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
    mknod video$ii c 81 $ii   # char-major-81-[0-16]
    chown root.root video$ii  # owned by root
    chmod 600 video$ii        # read/writable by root only
  done

The important part is to make the proper special character files 
with the right major and minor numbers.  All of the special device 
files are listed in ../devices.txt.  If you would like the quickcam 
usable by non-root users, you will need to change the permissions.

In this package you find the script "make_devices" that automatically 
create the device files.

2.3 Run a video4linux client program
   I suggest gqcam.

9.0 --- A sample program using v4lgrabber, 

This program is a simple image grabber that will copy a frame from the
first video device, /dev/video0 to standard output in portable pixmap
format (.ppm)

-------------------- 8< ---------------- 8< -----------------------------

/* Simple Video4Linux image grabber. */
/*
 *	Video4Linux Driver Test/Example Framegrabbing Program
 *
 *	Compile with:
 *		gcc -s -Wall -Wstrict-prototypes v4lgrab.c -o v4lgrab
 *      Use as:
 *              v4lgrab >image.ppm
 *
 *	Copyright (C) 1998-05-03, Phil Blundell <philb@gnu.org>  
 *      Copied from http://www.tazenda.demon.co.uk/phil/vgrabber.c 
 *      with minor modifications (Dave Forrest, drf5n@virginia.edu).
 *
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>

#include <linux/types.h>
#include <linux/videodev.h>

#define FILE "/dev/video0"

/* Stole this from tvset.c */

#define READ_VIDEO_PIXEL(buf, format, depth, r, g, b)                   \
{                                                                       \
        switch (format)                                                 \
        {                                                               \
                case VIDEO_PALETTE_GREY:                                \
                        switch (depth)                                  \
                        {                                               \
                                case 4:                                 \
                                case 6:                                 \
                                case 8:                                 \
                                        (r) = (g) = (b) = (*buf++ << 8);\
                                        break;                          \
                                                                        \
                                case 16:                                \
                                        (r) = (g) = (b) =               \
                                                *((unsigned short *) buf);      \
                                        buf += 2;                       \
                                        break;                          \
                        }                                               \
                        break;                                          \
                                                                        \
                                                                        \
                case VIDEO_PALETTE_RGB565:                              \
                {                                                       \
                        unsigned short tmp = *(unsigned short *)buf;    \
                        (r) = tmp&0xF800;                               \
                        (g) = (tmp<<5)&0xFC00;                          \
                        (b) = (tmp<<11)&0xF800;                         \
                        buf += 2;                                       \
                }                                                       \
                break;                                                  \
                                                                        \
                case VIDEO_PALETTE_RGB555:                              \
                        (r) = (buf[0]&0xF8)<<8;                         \
                        (g) = ((buf[0] << 5 | buf[1] >> 3)&0xF8)<<8;    \
                        (b) = ((buf[1] << 2 ) & 0xF8)<<8;               \
                        buf += 2;                                       \
                        break;                                          \
                                                                        \
                case VIDEO_PALETTE_RGB24:                               \
                        (r) = buf[0] << 8; (g) = buf[1] << 8;           \
                        (b) = buf[2] << 8;                              \
                        buf += 3;                                       \
                        break;                                          \
                                                                        \
                default:                                                \
                        fprintf(stderr,                                 \
                                "Format %d not yet supported\n",        \
                                format);                                \
        }                                                               \
}                                               

int get_brightness_adj(unsigned char *image, long size, int *brightness) {
  long i, tot = 0;
  for (i=0;i<size*3;i++)
    tot += image[i];
  *brightness = (128 - tot/(size*3))/3;
  return !((tot/(size*3)) >= 126 && (tot/(size*3)) <= 130);
}

int main(int argc, char ** argv)
{
  int fd = open(FILE, O_RDONLY), f;
  struct video_capability cap;
  struct video_window win;
  struct video_picture vpic;

  unsigned char *buffer, *src;
  int bpp = 24, r, g, b;
  unsigned int i, src_depth;

  if (fd < 0) {
    perror(FILE);
    exit(1);
  }

  if (ioctl(fd, VIDIOCGCAP, &cap) < 0) {
    perror("VIDIOGCAP");
    fprintf(stderr, "(" FILE " not a video4linux device?)\n");
    close(fd);
    exit(1);
  }

  if (ioctl(fd, VIDIOCGWIN, &win) < 0) {
    perror("VIDIOCGWIN");
    close(fd);
    exit(1);
  }

  if (ioctl(fd, VIDIOCGPICT, &vpic) < 0) {
    perror("VIDIOCGPICT");
    close(fd);
    exit(1);
  }

  if (cap.type & VID_TYPE_MONOCHROME) {
    vpic.depth=8;
    vpic.palette=VIDEO_PALETTE_GREY;    /* 8bit grey */
    if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
      vpic.depth=6;
      if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
        vpic.depth=4;
        if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
          fprintf(stderr, "Unable to find a supported capture format.\n");
          close(fd);
          exit(1);
        }
      }
    }
  } else {
    vpic.depth=24;
    vpic.palette=VIDEO_PALETTE_RGB24;
    
    if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
      vpic.palette=VIDEO_PALETTE_RGB565;
      vpic.depth=16;
      
      if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
        vpic.palette=VIDEO_PALETTE_RGB555;
        vpic.depth=15;
        
        if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
          fprintf(stderr, "Unable to find a supported capture format.\n");
          return -1;
        }
      }
    }
  }
  
  buffer = malloc(win.width * win.height * bpp);
  if (!buffer) {
    fprintf(stderr, "Out of memory.\n");
    exit(1);
  }
  
  do {
    int newbright;
    read(fd, buffer, win.width * win.height * bpp);
    f = get_brightness_adj(buffer, win.width * win.height, &newbright);
    if (f) {
      vpic.brightness += (newbright << 8);
      if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
        perror("VIDIOSPICT");
        break;
      }
    }
  } while (f);

  fprintf(stdout, "P6\n%d %d 255\n", win.width, win.height);

  src = buffer;

  for (i = 0; i < win.width * win.height; i++) {
    READ_VIDEO_PIXEL(src, vpic.palette, src_depth, r, g, b);
    fputc(r>>8, stdout);
    fputc(g>>8, stdout);
    fputc(b>>8, stdout);
  }
    
  close(fd);
  return 0;
}
-------------------- 8< ---------------- 8< -----------------------------


10.0 --- Other Information

You can find some good news on the sourceforge QuickCam VC home page.
http://sourceforge.net/projects/usb-quickcam-vc/

Or if you are interested for camera packets traffic
http://digilander.iol.it/demarchidaniele/quickcam-vc.html

If your camera is a Parallel version you will find useful informations at
http://mosquito.dhs.org/qcreveng.html


