/* * File Name : mmap.c * This includes the functions required for the memeory mapping system call. * Also functions used internally for mmap are defined here. * Work done for final project, Course: CIS5930 * Course Name: Linux Kernel Programming & Device Driver Programming * Written by: Veena Adityan and Arthi Gokarn. * Submitted to: Dr. Baker * Date: 06/20/2003 * Copyright 2003 */ #include "pixelsmart.h" extern int pixelsmart_streamon(struct pixelsmart *pstv, __u32 type); extern void pixelsmart_streamoff(struct pixelsmart *pstv, __u32 type); /* * Get a buffer with the specified offset. */ static struct stream_buffer * mmap_stream_buffer_from_offset(struct pixelsmart *dev, unsigned long offset) { int i; offset = offset * PAGE_SIZE; printk("ofset = %lu dev = %p\n",offset,dev); for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) if (offset == dev->stream_buf[i].vidbuf.offset) return &dev->stream_buf[i]; printk("returns NULL\n"); return NULL; } /* * Common VMA ops. */ static void pixelsmart_vma_open(struct vm_area_struct *vma) { struct v4l2_device *dev = v4l2_device_from_file(vma->vm_file); struct pixelsmart *pstv = (struct pixelsmart*) dev->priv; struct stream_buffer *buf; if (pstv == NULL) return; buf = mmap_stream_buffer_from_offset(pstv, vma->vm_pgoff); printk("bufferaddress in open = %p !shld be same as in mmap\n",buf); if(buf == NULL) return; ++buf->vma_refcount; printk("vma_open called\n"); } static void pixelsmart_vma_close(struct vm_area_struct *vma) { struct v4l2_device *dev = v4l2_device_from_file(vma->vm_file); struct pixelsmart *pstv = (struct pixelsmart*) dev->priv; struct stream_buffer *buf; printk("vma_close called\n"); if (pstv == NULL) return; buf = mmap_stream_buffer_from_offset(pstv, vma->vm_pgoff); --buf->vma_refcount; if (buf->vma_refcount > 0) return; if (pstv->streaming) { printk("Warning- munmap() called while streaming\n"); pixelsmart_streamoff(pstv, buf->vidbuf.type); } v4l2_q_yank_node(&pstv->stream_q_capture, &buf->qnode); v4l2_q_yank_node(&pstv->stream_q_done, &buf->qnode); if (buf->vaddress != NULL) vfree(buf->vaddress); buf->vaddress = NULL; buf->vidbuf.flags = 0; if (pstv->stream_buffers_mapped > 0) --pstv->stream_buffers_mapped; } struct page *pixelsmart_vma_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)/* Streaming data buffer */ { struct v4l2_device *dev; struct pixelsmart *pstv; struct stream_buffer *buf; unsigned long offset_into_buffer; struct page *page; printk("nopage called \n"); dev = v4l2_device_from_file(vma->vm_file); if (dev == NULL) return 0; pstv = dev->priv; if (pstv == NULL) return 0; buf = mmap_stream_buffer_from_offset(pstv, vma->vm_pgoff); if (buf == NULL) return 0; offset_into_buffer = address - vma->vm_start; if (offset_into_buffer >= buf->vidbuf.length) { printk("Attempt to read past end of mmap() buffer\n"); return 0; } page = v4l2_vmalloc_to_page(buf->vaddress + offset_into_buffer); if (page == 0) return 0; atomic_inc(&page->count); printk("returned valid page\n"); return page; } struct vm_operations_struct pixelsmart_vm_ops = { open: pixelsmart_vma_open, close: pixelsmart_vma_close, nopage: pixelsmart_vma_nopage, }; /* * Called by the ioctl REQBUF to request for buffers. */ int mmap_request_buffers(struct pixelsmart *dev, struct v4l2_requestbuffers *req) { int i; u32 buflen; u32 type; if (dev->stream_buffers_mapped) return 0;/* can't make requests if buffers are mapped */ if (req->count < 1) req->count = 1; if (req->count > MAX_CAPTURE_BUFFERS) req->count = MAX_CAPTURE_BUFFERS; type = V4L2_BUF_TYPE_CAPTURE; /* The buffer length needs to be a multiple of the page size */ buflen = (dev->clientfmt.sizeimage + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); printk("Granting %d buffers\n",req->count); /* Now initialize the buffer structures. Don't allocate the */ /* buffers until they're mapped. */ for (i = 0; i < req->count; ++i) { dev->stream_buf[i].requested = 1; dev->stream_buf[i].vidbuf.index = i; dev->stream_buf[i].vidbuf.type = type; /* offset must be unique for each buffer, and a multiple */ /* of PAGE_SIZE on 2.4.x */ dev->stream_buf[i].vidbuf.offset = PAGE_SIZE * (i+1); dev->stream_buf[i].vidbuf.length = buflen; dev->stream_buf[i].vidbuf.bytesused = 0; dev->stream_buf[i].vidbuf.flags = 0; dev->stream_buf[i].vidbuf.timestamp = 0; dev->stream_buf[i].vidbuf.sequence = 0; memset(&dev->stream_buf[i].vidbuf.timecode, 0, sizeof(struct v4l2_timecode)); } for (i = req->count; i < MAX_CAPTURE_BUFFERS; ++i) dev->stream_buf[i].requested = 0; dev->stream_buffers_requested = req->count; printk("returning from req_buffer\n"); return 1; } /* * Called by the ioctl REQBUF to request for buffers. */ void mmap_unrequest_buffers(struct pixelsmart *dev){ int i; if (dev->stream_buffers_requested == 0 || dev->stream_buffers_mapped) return; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) dev->stream_buf[i].requested = 0; dev->stream_buffers_requested = 0; } /* * Called by the memory map system call */ int pixelsmart_mmap(void *id, struct vm_area_struct *vma){ struct v4l2_device *vdev = (struct v4l2_device *)id; struct pixelsmart *dev = vdev->priv; struct stream_buffer *buf = NULL; int i,offset; offset = vma->vm_pgoff * PAGE_SIZE; printk("<1>mmap 1 ofset = %d vam->start = %lu\n", offset,vma->vm_start); for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) if (offset == dev->stream_buf[i].vidbuf.offset){ buf = &dev->stream_buf[i]; printk("gets a valid offset buf = %p\n",buf); break; } if (buf == NULL){ printk("mmap() Invalid offset parameter\n"); return -EINVAL; //no such buffer } if (buf->vidbuf.length != vma->vm_end - vma->vm_start){ printk("mmap() Wrong length parameter\n"); return -EINVAL; //wrong length } if (!buf->requested){ printk("mmap() Buffer is not available for" " mapping\n"); return -EINVAL; //not requested } if (buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED){ printk("mmap() Buffer is already mapped\n"); return -EINVAL; //already mapped } if (buf->vaddress != NULL) vfree(buf->vaddress); /* *Allocate virtual memory. This memory is mapped to the buffer in user space. */ buf->vaddress = vmalloc(buf->vidbuf.length); printk("virtual address of buffer = %p and sz = %d\n",buf->vaddress,buf->vidbuf.length); if (buf->vaddress == NULL){ printk("Could not allocate mmap() buffer\n"); return -ENODEV; } buf->vidbuf.flags |= V4L2_BUF_FLAG_MAPPED; ++dev->stream_buffers_mapped; vma->vm_ops = &pixelsmart_vm_ops; if (vma->vm_ops->open) vma->vm_ops->open(vma); //Note: vma->vm_file will be set up by V4L2 return 0; }