Kernel Device Drivers
PixelSmart/HRT Frame Grabber
Nathan Folkert and Shen Dong

HRT Pixel Smart Frame Grabber

Our project goal it to improve the driver that has been worked on since over the previous offerings of this course. Our first line of buisness was to update the driver to the current version of the kernel, 2.6.31.13. We then worked on mapping the memory of the device to user space to allow for writing a user space driver. Finally we wrote a new test program to allow the user to see that the hardware does work and is initialized properly by the driver.


Driver Update

In the new version of the kernel the struct video_device was changed and many fields were moved from video_device to the struct v4l2_ioctl_ops. As a result we had to remove many fields from the original video_struct and create a new v4l2_ioctl_ops struct.

This is the old structure used for the old kernel version.

static struct video_device hrt_template = {
    .owner                = THIS_MODULE,
    .name                 = "HRT Pixelsmart  PS512-8-PCI",
    .type                 = HRT_VID_TYPE,
    .fops                 = &hrt_fops,
    .release              = grabber_release,
    .vidioc_querycap      = hrt_querycap,
    .vidioc_enum_fmt_cap  = hrt_enum_fmt_cap,
    .vidioc_s_fmt_cap     = hrt_s_fmt_cap,
    .vidioc_g_fmt_cap     = hrt_g_fmt_cap,
    .vidioc_g_crop        = hrt_g_crop,
    .vidioc_s_crop        = hrt_s_crop,
    .vidioc_reqbufs       = hrt_reqbufs,
    .vidioc_querybuf      = hrt_querybuf,
    .vidioc_qbuf          = hrt_qbuf,
    .vidioc_dqbuf         = hrt_dqbuf,
    .vidioc_enum_input    = hrt_enum_input,
    .vidioc_queryctrl     = hrt_queryctrl,
    .vidioc_g_ctrl        = hrt_g_ctrl,
    .vidioc_s_ctrl        = hrt_s_ctrl,
    .vidioc_streamon      = hrt_streamon,
    .vidioc_streamoff     = hrt_streamoff,
    .vidioc_g_parm        = hrt_g_parm,
    .vidioc_try_fmt_cap   = hrt_try_fmt_cap,
    .tvnorms              = V4L2_STD_525_60,
    .current_norm         = V4L2_STD_NTSC_M,
};

These are the two new structures as defined for the new kernel version

static struct v4l2_ioctl_ops v4l2ioctl = { 
    .vidioc_querycap      = hrt_querycap,
    .vidioc_enum_fmt_vid_cap  = hrt_enum_fmt_cap,
    .vidioc_s_fmt_vid_cap     = hrt_s_fmt_cap,
    .vidioc_g_fmt_vid_cap     = hrt_g_fmt_cap,
    .vidioc_g_crop        = hrt_g_crop,
    .vidioc_s_crop        = hrt_s_crop,
    .vidioc_reqbufs       = hrt_reqbufs,
    .vidioc_querybuf      = hrt_querybuf,
    .vidioc_qbuf          = hrt_qbuf,
    .vidioc_dqbuf         = hrt_dqbuf,
    .vidioc_enum_input    = hrt_enum_input,
    .vidioc_queryctrl     = hrt_queryctrl,
    .vidioc_g_ctrl        = hrt_g_ctrl,
    .vidioc_s_ctrl        = hrt_s_ctrl,
    .vidioc_streamon      = hrt_streamon,
    .vidioc_streamoff     = hrt_streamoff,
    .vidioc_g_parm        = hrt_g_parm,
    .vidioc_try_fmt_vid_cap   = hrt_try_fmt_cap,
};
static struct video_device hrt_template = {
    .name                 = "HRT Pixelsmart  PS512-8-PCI",
    .vfl_type             = HRT_VID_TYPE,
    .fops                 = &hrt_fops,
    .release              = grabber_release,
    .ioctl_ops            = &v4l2ioctl,
    .tvnorms              = V4L2_STD_525_60,
    .current_norm         = V4L2_STD_NTSC_M,
};

Other than this there were a few functions that had changed parameter lists and one assignment statement that was no longer needed. After these changes the code would compile. However, since the kernel version used previously, Tasklets have been deprecated and replaced by work queues. Tasklets are still suported for backward compatibility but they will be phased out eventually so we changed them to the new recemended structure.


Memory Mapping

The ability to map the memory of the device directly to user space memory was a desired improvment to the driver. A team a few years back tried to do this but was not successful. After looking at their work and reading the code from a few existing drivers in the kernel, we managed to throw something together that works.

if (!request_mem_region(phys_address, HRT_IO_SIZE, "hrt")) {
      return -EBUSY;
}

int hrt_mmap(struct file *file, struct vm_area_struct *vma)
{
  struct hrt_fh *per_file = file->private_data;
  struct hrt_dev *dev = per_file->dev;
  vma->vm_flags |= (VM_IO | VM_RESERVED);
  if( remap_pfn_range ( vma , vma->vm_start , dev->phys_addr >> PAGE_SHIFT , \
  vma->vm_end-vma->vm_start , vma->vm_page_prot ))
    return -EAGAIN;
  return 0;
}

To do this we need two functions, request_mem_region and remap_pfn_range. First we use request_mem_region to get the pysical address of the device. We then pass that as an argument to remap_pfn_range with a page shift applied. After this the memory of the device is mapped and can be directly accessed from a user space pointer. Tests have proven that this mapping is both readable and writable.


Test Program

writeimage.c

Another one of our accomplishments was writing a new test program. While not as robust and featureful as previous test programs, its main selling point is that it does not use any fancy libraries that need to be included. It is all standard C and will compile anywhere. The progam takes a picture from the camera and stores it in a .bmp file. To accomplish this without libraries we had to piece together the file header for .bmp images by hand. It then reads the pixels in through our memory mapping and creates an image. Below is an image taken with this program.