v4l2out.c
上传用户:aoeyumen
上传日期:2007-01-06
资源大小:3329k
文件大小:47k
- /* -*- linux-c -*- --------------------------------------------------------- *
- *
- * Video for Linux Two
- * Video Output Driver for Matrox Gx00 series
- * and Zoran DVD add-on module
- * (c) 1999 David Barth <dbarth@besancon.net>
- *
- * This software is in the public domain.
- * Based mainly on the module example Written by Bill Dirks
- */
- #ifndef __KERNEL__
- #define __KERNEL__
- #endif
- #ifndef MODULE
- #define MODULE
- #endif
- #include <linux/module.h>
- #include <linux/delay.h>
- #include <linux/errno.h>
- #include <linux/fs.h>
- #include <linux/kernel.h>
- #include <linux/malloc.h>
- #include <linux/mm.h>
- #include <linux/poll.h>
- #include <linux/ioport.h>
- #include <asm/io.h>
- #include <linux/sched.h>
- #include <linux/videodev2.h>
- #include <linux/version.h>
- #include <asm/uaccess.h>
- #include <linux/pci.h>
- #include <asm/pgtable.h>
- #include <asm/page.h>
- #include <linux/interrupt.h>
- #include "mgavideo.h"
- #include "i34.h"
- #define PKMOD "out: "
- #if 1
- #define debug_msg(fmt,arg...) printk(KERN_DEBUG PKMOD fmt,##arg)
- #else
- #define debug_msg(fmt,arg...)
- #endif
- #if 1
- #define err_msg(fmt,arg...) printk(KERN_ERR PKMOD fmt,##arg)
- #else
- #define err_msg(fmt,arg...)
- #endif
- #if 1
- #define info_msg(fmt,arg...) printk(KERN_INFO PKMOD fmt,##arg)
- #else
- #define info_msg(fmt,arg...)
- #endif
- /* Video controls */
- static struct v4l2_queryctrl output_control[] =
- {
- {V4L2_CID_BRIGHTNESS, "Brightness", 0, 63, 1, 32, V4L2_CTRL_TYPE_INTEGER},
- {V4L2_CID_CONTRAST, "Contrast", 0, 63, 1, 32, V4L2_CTRL_TYPE_INTEGER},
- {V4L2_CID_SATURATION, "Saturation", 0, 63, 1, 32, V4L2_CTRL_TYPE_INTEGER},
- /*{V4L2_CID_HUE, "Hue", 0, 63, 1, 32, V4L2_CTRL_TYPE_INTEGER},*/
- };
- #define MAXCONTROLS (sizeof(output_control)/sizeof(output_control[0]))
- #define VCTRL_BRIGHTNESS 0
- #define VCTRL_CONTRAST 1
- #define VCTRL_SATURATION 2
- #define VCTRL_HUE 3
- static int
- find_vctrl(int id)
- {
- int i = -1;
- if (id == V4L2_CID_PRIVATE_BASE ||
- id < V4L2_CID_BASE ||
- id > V4L2_CID_LASTP1)
- return -EDOM;
- #if 0
- for (i = MAXCONTROLS - 1; i >= 0; i--)
- if (output_control[i].id == id)
- break;
- #endif
- if (i < 0)
- i = -EINVAL;
- return i;
- }
- struct output_device;/* forward reference */
- struct video_encoder
- {
- int is_initialized;
- int num_outputs;
- int output;
- __u32 standards;
- __u32 standard;
- __u32 frame_period;
- /* Changable method functions helps us support multiple */
- /* different types of video encoders easily */
- int (*initialize)(struct output_device *dev);
- int (*set_output)(struct output_device *dev, int x);
- int (*set_standard)(struct output_device *dev, int x);
- };
- struct video_output
- {
- struct v4l2_output output;
- int control[MAXCONTROLS];
- struct v4l2_modulator modulator;
- };
- /* Indices into the array of video_output's */
- #define VOUTPUT_COMP 0
- #define VOUTPUT_SVIDEO 1
- #define VOUTPUT_COUNT 2
- /* Bus-master gather list */
- struct gather_node
- {
- __u32 addr;
- __u32 len;
- };
- #define END_OF_GATHER_LIST 0x80000000
- /* Per-open data for handling multiple opens on one device */
- struct device_open
- {
- int isopen;
- int noio;
- struct output_device *dev;
- };
- #define MAX_OPENS 3
- /* Streaming data buffer */
- struct stream_buffer
- {
- struct v4l2_q_node qnode;
- struct v4l2_buffer vidbuf;
- int requested;
- __u8 *vaddress; /* vmalloc() */
- struct gather_node *dma_list; /* get_free_page() */
- };
- #define MAX_OUTPUT_BUFFERS 10
- #define MAX_LOCKED_MEMORY 2000000
- /*
- * Output device structure
- *
- * One for each handled device in the system.
- * This structure holds all the global information the driver
- * needs about each device.
- */
- struct output_device
- {
- struct v4l2_device v; /* Must be first */
- struct mga_dev *mga;
- char shortname[16];
- int is_registered;
- int open_count;
- struct device_open open_data[MAX_OPENS];
- int io_opens;
- /* Per-bus index number for each device */
- int index;
- /* General type of device */
- int type;
- /* Pointer to the pci_dev structure for this board */
- struct pci_dev *pci;
- /* I/O Base address for non-PCI devices */
- unsigned int iobase;
- /* Interrupts */
- int irq;
- int ints_enabled;
- struct tq_struct tqnode_dpc;/* for Bottom Half routine */
- struct timer_list tlnode;/* for polling interrupts */
- struct wait_queue *new_video_frame;
- /* Video output (and encoder) stuff */
- struct video_encoder viden;
- struct video_output voutput[VOUTPUT_COUNT];
- int hwoutput_width;
- int hwoutput_height;
- /* Client output image format */
- struct v4l2_format clientfmt;
- int output;/* which video output is selected */
- struct v4l2_outputparm outputparm;
- /* Hardware image format */
- int hwoutput_bypp;
- int hwoutput_size;
- __u8 *hwoutput_buffer;/* vmalloc() */
- int hwoutput_buffer_size;
- struct gather_node *hwoutput_dma_list;/* get_free_page() */
- /* Hardware output state */
- int ready_to_output;
- int hwoutput_enabled;
- int hwoutput_completed;
- unsigned long time_acquired;/* millisecond time stamp */
- int streaming;
- struct stream_buffer stream_buf[MAX_OUTPUT_BUFFERS];
- int stream_buffers_mapped;
- struct v4l2_queue stream_q_output;
- struct v4l2_queue stream_q_done;
- struct timeval stream_begin;
- unsigned long stream_last_frame;
- __u8 *stream_hwoutput_buffer;
- /* Performance statistics */
- struct v4l2_performance perf;
- /* video preview stuff */
- struct v4l2_framebuffer fbuf;
- struct v4l2_window window;
- };
- /* Values for type field */
- #define DEVICE_TYPE_0 0
- /* Extreme video dimensions */
- #define MIN_WIDTH 32
- #define MIN_HEIGHT 24
- #define MAX_WIDTH 720
- #define MAX_HEIGHT 576
- #define MAX_FRAME_AGE 200 /* ms */
- /*
- * The Output device structure array. This is the only global
- * variable in the module besides those used by the device probing
- * and enumeration routines (command line overrides)
- */
- #define NBOARDS 2
- static struct output_device outputdev[NBOARDS];
- static int unit_vout[NBOARDS] = { 16, 17, };
- MODULE_PARM(unit_vout, "1-"__MODULE_STRING(NBOARDS)"i");
- static inline struct output_device *
- output_device_from_file(struct file *file)
- {
- return (struct output_device *)v4l2_device_from_file(file);
- }
- /* These macros can be used to make device I/O operations atomic */
- /* static spinlock_t device_lock = SPIN_LOCK_UNLOCKED; */
- /* #define BEGIN_CRITICAL_SECTION */
- /* do{unsigned long flags;spin_lock_irqsave(&wavi_lock,flags) */
- /* #define END_CRITICAL_SECTION */
- /* spin_unlock_irqrestore(&wavi_lock,flags);}while(0) */
- /*
- * D E V I C E F U N C T I O N S
- */
- static void
- device_initialize(struct output_device *dev)
- {
- /* TODO: Put hardware into a sensible state and */
- /* do the one-time startup things */
- I34_Init(dev->mga);
- }
- static void
- device_brightness(struct output_device *dev, int x)
- {
- }
- static void
- device_contrast(struct output_device *dev, int x)
- {
- }
- static void
- device_saturation(struct output_device *dev, int x)
- {
- }
- static void
- device_tone_controls(struct output_device *dev)
- {
- int *ctrl;
- ctrl = dev->voutput[dev->output].control;
- device_brightness(dev, ctrl[VCTRL_BRIGHTNESS]);
- device_contrast(dev, ctrl[VCTRL_CONTRAST]);
- device_saturation(dev, ctrl[VCTRL_SATURATION]);
- /* device_hue(dev, ctrl[VCTRL_HUE]); */
- }
- /* Start or stop the DMA transfer */
- static void
- hwoutput_enable(struct output_device *dev, int start)
- {
- /* TODO: Start the transfer of the data */
- // if (dev->streaming)
- // {
- // }
- // else
- // {
- // }
- }
- #if 0
- /*
- *
- * B U S M A S T E R F U N C T I O N S
- *
- */
- static int
- bm_build_gather_list(struct output_device *dev,
- unsigned char *buffer,
- struct gather_node **plist)
- {
- struct gather_node *list;
- int i, n;
- unsigned char *a;
- if (buffer == NULL)
- return 0;
- list = *plist;
- if (list == NULL)
- {/* Assuming one page will be big enough. 4KB = 512 pieces */
- list = (struct gather_node *)get_free_page(GFP_KERNEL);
- if (list == NULL)
- return 0;
- }
- /* Simple algorithm will just map the buffer contiguously by pages */
- /* Note: hwoutput_buffer is vmalloc()ed, so it's page-aligned */
- n = (dev->hwoutput_size + PAGE_SIZE - 1) / PAGE_SIZE;
- a = buffer;
- for (i = 0; i < n; ++i)
- {
- list[i].addr = v4l2_vmalloc_to_bus(a);
- list[i].len = PAGE_SIZE;
- a += PAGE_SIZE;
- }
- /* Last page might not be full */
- if (dev->hwoutput_size < n * PAGE_SIZE)
- list[n - 1].len = dev->hwoutput_size - (n - 1) * PAGE_SIZE;
- list[n - 1].len |= END_OF_GATHER_LIST;
- #if 0
- debug_msg("Gather list %08lXn", virt_to_bus(list));
- for (i = 0; i < n; ++i)
- debug_msg("List %2d: A=%08X L=%08Xn",i,
- list[i].addr,list[i].len);
- #endif
- *plist = list;
- return 1;/* ok */
- }
- #endif
- /*
- *
- * V I D E O E N C O D E R S
- *
- */
- static int
- encoder_initialize(struct output_device *dev)
- {
- /* Video encoder information fields */
- dev->viden.standards = (1 << V4L2_STD_NTSC) |
- (1 << V4L2_STD_PAL);
- dev->viden.num_outputs = 2;
- return 1;
- }
- static int
- encoder_set_output(struct output_device *dev, int i)
- {
- dev->output = i;
- /* TODO: Switch the hardware to the new output */
- return 1;
- }
- static int
- encoder_set_standard(struct output_device *dev, int x)
- {
- dev->viden.standard = x;
- /* TODO: Switch the hardware to the new standard */
- switch (x)
- {
- case V4L2_STD_NTSC:
- dev->viden.frame_period = 333667;
- break;
- case V4L2_STD_PAL:
- dev->viden.frame_period = 400000;
- break;
- case V4L2_STD_SECAM:
- dev->viden.frame_period = 400000;
- break;
- }
- return 1;
- }
- static int
- encoder_probe(struct output_device *dev)
- {
- /* TODO: Probe I2C bus or whatever for the video encoder */
- /* Fill in the method fields */
- dev->viden.initialize = encoder_initialize;
- dev->viden.set_output = encoder_set_output;
- dev->viden.set_standard = encoder_set_standard;
- //info_msg("Found encoder chipn");
- return 1;/* Found */
- }
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- *
- * Probe I2C bus for video encoder and fill in the device fields
- */
- static int
- find_encoder(struct output_device *dev)
- {
- if (!encoder_probe(dev))
- return 0;/* Failure */
- return 1;
- }
- static void
- set_video_output(struct output_device *dev,
- int i)
- {
- if (i < 0 || i >= dev->viden.num_outputs)
- return;
- dev->viden.set_output(dev, i);
- device_tone_controls(dev);
- }
- /*
- *
- * V I D E O O U T P U T F U N C T I O N S
- *
- */
- /*
- * Supported image formats
- */
- static struct v4l2_fmtdesc outfmt[] =
- {
- { 0, {"RGB-16 (5-5-5)"},
- V4L2_PIX_FMT_RGB555, 0, 16, {0, 0},
- },
- { 1, {"RGB-16 (5-6-5)"},
- V4L2_PIX_FMT_RGB565, 0, 16, {0, 0},
- },
- { 2, {"RGB-24 (B-G-R)"},
- V4L2_PIX_FMT_BGR24, 0, 24, {0, 0},
- },
- { 3, {"RGB-32 (B-G-R-?)"},
- V4L2_PIX_FMT_BGR32, 0, 32, {0, 0},
- },
- { 4, {"Greyscale-8"},
- V4L2_PIX_FMT_GREY, V4L2_FMT_CS_601YUV, 8, {0, 0},
- },
- { 5, {"YUV 4:2:2 (Y-U-Y-V)"},
- V4L2_PIX_FMT_YUYV, V4L2_FMT_CS_601YUV, 16, {0, 0},
- },
- { 6, {"YUV 4:2:0 (planar)"},
- V4L2_PIX_FMT_YUV420, V4L2_FMT_CS_601YUV, 12, {0, 0},
- },
- };
- #define NUM_OUTFMT (sizeof(outfmt)/sizeof(outfmt[0]))
- static void interrupt_enable(struct output_device *dev);
- /* The image format has changed, width, height, pixel format.
- * Decide if the format is ok or take the closest valid format.
- */
- static void
- hwoutput_new_format(struct output_device *dev)
- {
- int ntsc;
- int max_height;
- int max_width;
- if (dev->stream_buffers_mapped)
- return;
- ntsc = (dev->viden.standard == V4L2_STD_NTSC);
- dev->ready_to_output = 0;
- dev->clientfmt.flags = V4L2_FMT_CS_601YUV;
- switch (dev->clientfmt.pixelformat)
- {
- case V4L2_PIX_FMT_GREY:
- dev->clientfmt.depth = 8;
- break;
- case V4L2_PIX_FMT_YUV420:
- dev->clientfmt.depth = 12;
- break;
- case V4L2_PIX_FMT_RGB555:
- case V4L2_PIX_FMT_RGB565:
- dev->clientfmt.flags = 0;
- /* fall thru */
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- dev->clientfmt.depth = 16;
- break;
- case V4L2_PIX_FMT_BGR24:
- dev->clientfmt.depth = 24;
- dev->clientfmt.flags = 0;
- break;
- case V4L2_PIX_FMT_BGR32:
- dev->clientfmt.depth = 32;
- dev->clientfmt.flags = 0;
- break;
- default:
- //debug_msg("unknown format %08Xn", dev->clientfmt.pixelformat);
- dev->clientfmt.depth = 24;
- dev->clientfmt.flags = 0;
- break;
- }
- dev->hwoutput_bypp = 2;
- if (dev->clientfmt.width < MIN_WIDTH)
- dev->clientfmt.width = MIN_WIDTH;
- if (dev->clientfmt.height < MIN_HEIGHT)
- dev->clientfmt.height = MIN_HEIGHT;
- max_width = MAX_WIDTH;
- max_height = MAX_HEIGHT;
- if (dev->clientfmt.width > max_width)
- dev->clientfmt.width = max_width;
- if (dev->clientfmt.height > max_height)
- dev->clientfmt.height = max_height;
- dev->clientfmt.width &= ~3;
- dev->clientfmt.height &= ~3;
- dev->clientfmt.sizeimage = (dev->clientfmt.width
- * dev->clientfmt.height
- * dev->clientfmt.depth)
- / 8;
- dev->hwoutput_size = dev->clientfmt.width
- * dev->clientfmt.height
- * dev->hwoutput_bypp;
- /* TODO: Any other driver state related to the image format */
- }
- /* Stop the music!
- */
- static void
- hwoutput_abort(struct output_device *dev)
- {
- dev->hwoutput_enabled = 0;
- /* Turn off the hardware */
- hwoutput_enable(dev, 0);
- }
- /* Allocate buffers, and get everything ready to go, but don't start yet.
- */
- static int
- hwoutput_begin(struct output_device *dev)
- {
- hwoutput_abort(dev);
- if (dev->ready_to_output)
- return dev->ready_to_output;
- if (dev->hwoutput_buffer_size < dev->hwoutput_size)
- {
- if (dev->hwoutput_buffer != NULL)
- vfree(dev->hwoutput_buffer);
- dev->hwoutput_buffer_size =
- (dev->hwoutput_size + PAGE_SIZE - 1)
- & ~(PAGE_SIZE - 1);
- dev->hwoutput_buffer = (__u8 *)
- vmalloc(dev->hwoutput_buffer_size);
- if (dev->hwoutput_buffer == NULL)
- {
- dev->hwoutput_buffer_size = 0;
- err_msg("Can't allocate buffer"
- " %d bytesn", dev->hwoutput_size);
- return dev->ready_to_output;
- }
- }
- #if 0
- if (dev->irq && (can use DMA))
- bm_build_gather_list(dev, dev->hwoutput_buffer,
- &dev->hwoutput_dma_list);
- #endif
- /* TODO: other last-minute things to get the device ready */
- interrupt_enable(dev);
- debug_msg("Ready to go!n");
- return (dev->ready_to_output = 1);
- }
- /* Start an image transfer out to the hardware
- */
- static void
- hwoutput_send_frame(struct output_device *dev)
- {
- if (!dev->ready_to_output)
- return;/* shouldn't happen */
- if (dev->hwoutput_enabled)
- return;
- /* TODO: Prepare the hardware for the next image transfer */
- //list = dev->hwoutput_dma_list;/* DMA list for hwoutput_buffer */
- /* Set up stream_hwoutput_buffer to point to the buffer to */
- /* transfer the next frame from */
- if (dev->streaming)
- {
- struct stream_buffer *buf;
- buf = v4l2_q_peek_head(&dev->stream_q_output);
- if (buf != NULL)
- {
- dev->stream_hwoutput_buffer = buf->vaddress;
- //list = buf->dma_list;
- }
- }
- /* TODO: load the DMA gather list if needed */
- /* Start the hardware taking the data */
- hwoutput_enable(dev, 1);
- dev->hwoutput_enabled = 1;
- dev->hwoutput_completed = 0;
- }
- /*
- * STREAMING IMAGES
- */
- static int/* 1 = success; 0 = failed */
- hwoutput_queuebuffer(struct output_device *dev,
- struct v4l2_buffer *vidbuf)
- {
- int i = vidbuf->index;
- struct stream_buffer *buf = NULL;
- if (!dev->stream_buffers_mapped)
- {
- debug_msg("QBUF no buffers mappedn");
- return 0;
- }
- if (vidbuf->type != V4L2_BUF_TYPE_VIDEOOUT)
- {
- debug_msg("QBUF wrong typen");
- return 0;
- }
- if (i < 0 || i >= MAX_OUTPUT_BUFFERS || !dev->stream_buf[i].requested)
- {
- debug_msg("QBUF buffer index %d is out of rangen", i);
- return 0;
- }
- buf = &dev->stream_buf[i];
- if (!(buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED))
- {
- debug_msg("QBUF buffer %d is not mappedn", i);
- return 0;
- }
- if ((buf->vidbuf.flags & V4L2_BUF_FLAG_QUEUED))
- {
- debug_msg("QBUF buffer %d is already queuedn", i);
- return 0;
- }
- buf->vidbuf.flags &= ~V4L2_BUF_FLAG_DONE;
- v4l2_q_add_tail(&dev->stream_q_output, &buf->qnode);
- buf->vidbuf.flags |= V4L2_BUF_FLAG_QUEUED;
- return 1;
- }
- static int/* 1 = got a buffer; 0 = no buffers */
- hwoutput_dequeuebuffer(struct output_device *dev,
- struct v4l2_buffer *buf)
- {
- struct stream_buffer *newbuf;
- if (!dev->streaming || buf->type != V4L2_BUF_TYPE_VIDEOOUT)
- {
- debug_msg("DQBUF not streaming or wrong buffer typen");
- return 0;
- }
- newbuf = v4l2_q_del_head(&dev->stream_q_done);
- if (newbuf == NULL)
- {
- debug_msg("DQBUF nothing on done queuen");
- return 0;
- }
- newbuf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
- *buf = newbuf->vidbuf;
- return 1;
- }
- static int
- hwoutput_streamon(struct output_device *dev,
- __u32 type)
- {
- struct stream_buffer *buf;
- if (dev->streaming || type != V4L2_BUF_TYPE_VIDEOOUT)
- {
- debug_msg("STREAMON wrong buffer type or already streamingn");
- return 0;
- }
- hwoutput_abort(dev);/* cancel any transfer that might be in progress */
- /* -2 is a magic number that triggers start-of-stream logic in */
- /* hwoutput_interrupt() */
- dev->stream_last_frame = -2;
- dev->perf.frames = 0;
- dev->perf.framesdropped = 0;
- dev->perf.bytesout = 0;
- /* Can't send frames faster than the video output rate */
- if (dev->outputparm.timeperframe < dev->viden.frame_period)
- dev->outputparm.timeperframe = dev->viden.frame_period;
- /* Move any leftover DONE buffers to the free pool */
- while ((buf = v4l2_q_del_head(&dev->stream_q_done)))
- buf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
- /* Kick off the machine */
- dev->streaming = 1;
- hwoutput_send_frame(dev);
- return 1;
- }
- static void
- hwoutput_streamoff(struct output_device *dev,
- __u32 type)
- {
- if (!dev->streaming || type != V4L2_BUF_TYPE_VIDEOOUT)
- {
- debug_msg("STREAMOFF wrong buffer type or not streamingn");
- return;
- }
- hwoutput_abort(dev);
- dev->streaming = 0;
- /* Note: should really delay this */
- dev->perf.frames = 0;
- dev->perf.framesdropped = 0;
- dev->perf.bytesout = 0;
- }
- /* Called from write(). Copy the data to a driver internal buffer
- * which the hardware can read directly or can DMA from or whatever
- * makes sense for the hardware. You probably want to double-buffer
- * somehow so that the output is continuous.
- */
- static int /* returns length of data or negative for error */
- hwoutput_writeimage(struct output_device *dev,
- __u8 *hwoutput_buffer,
- const __u8 *source_buffer,
- int output_size,
- int output_is_user)
- {
- int len;
- /* TODO: take data from the source buffer */
- /* Check if more data can be accepted now, if not return
- zero if the application just needs to wait, or a negative
- error code if there is a problem. Otherwise return the
- number of bytes taken */
- len = dev->hwoutput_size;
- ++dev->perf.frames;
- dev->perf.bytesout += len;
- return len;
- }
- /* The hardware has issued the interrupt signal, depending on the device,
- * this could mean a vertical sync or a DMA completion.
- * [This function is called indirectly through the immediate task queue;
- * it executes at elevated IRQL, but it is interruptible. (It's a b.h.)]
- */
- static void
- hwoutput_interrupt(void *v)
- {
- struct output_device *dev = (struct output_device *)v;
- struct stream_buffer *buf;
- struct timeval timestamp_rough;
- unsigned long raw_frame_num;
- unsigned long next_raw_frame_to_keep;
- unsigned long stream_frame_num;
- u64 temp64;
- /* TODO: Check for an interrupt pending on the device, and */
- /* return if there is no interrupt pending */
- /* (In this hardware-less demo I'll just check the completed flag) */
- if (!dev->hwoutput_enabled ||
- dev->hwoutput_completed)
- return;
- if (!dev->ints_enabled /* || TODO: some other sanity checks? */)
- {
- err_msg("Can't process the interruptn");
- return;
- }
- dev->hwoutput_completed = 1;
- if (!dev->streaming)
- {
- /* TODO: Interrupt processing when using write() instead */
- /* of streaming for outputting frames */
- //debug_msg("Ready for new framen");
- /* Ready for the next frame! */
- /* (If select() or write() is blocked, wake him up.) */
- wake_up_interruptible(&dev->new_video_frame);
- return;
- }
- /* ...Only get here in streaming mode... */
- if (dev->stream_last_frame == -2)
- {/* First frame of the stream */
- v4l2_masterclock_gettime(&dev->stream_begin);
- dev->stream_last_frame = -1;
- }
- buf = v4l2_q_peek_head(&dev->stream_q_output);
- if (buf == NULL)
- {/* No available buffers. TODO: Maybe do nothing, maybe use */
- /* previous frame again */
- return;
- }
- /* Compute current stream time */
- v4l2_masterclock_gettime(×tamp_rough);
- v4l2_timeval_delta(×tamp_rough,
- &dev->stream_begin, ×tamp_rough);
- /* Output rate control */
- raw_frame_num = v4l2_timeval_divide(
- ×tamp_rough, dev->viden.frame_period);
- temp64 = (u64)dev->outputparm.timeperframe
- * (dev->stream_last_frame + 1)
- + (dev->viden.frame_period >> 1);
- next_raw_frame_to_keep =
- v4l2_math_div6432(temp64, dev->viden.frame_period, NULL);
- if (raw_frame_num < next_raw_frame_to_keep)
- {/* Not time yet according to desired output frame rate, */
- /* wait some more. (Similar processing to no buffers case.) */
- return;
- }
- /* Time to send out next frame */
- //hwoutput_writeimage(dev, dev->stream_hwoutput_buffer,
- // buf->vaddress, buf->vidbuf.length, 0);
- /* Mark the buffer DONE */
- buf->vidbuf.flags |= V4L2_BUF_FLAG_DONE;
- /* For informational purposes to the app, figure if frames have */
- /* been missed. */
- stream_frame_num = v4l2_timeval_correct(&buf->vidbuf.timestamp,
- dev->outputparm.timeperframe);
- //debug_msg("Stream frame %4lu T= %lu.%06lun", stream_frame_num,
- // buf->vidbuf.timestamp.tv_sec,buf->vidbuf.timestamp.tv_usec);
- if (stream_frame_num > dev->stream_last_frame + 1)
- {/* We have missed one or more frames */
- dev->perf.framesdropped += stream_frame_num
- - dev->stream_last_frame + 1;
- }
- dev->stream_last_frame = stream_frame_num;
- /* Move buffer to done queue */
- buf = v4l2_q_del_head(&dev->stream_q_output);
- v4l2_q_add_tail(&dev->stream_q_done, &buf->qnode);
- /* Send out the next frame now */
- hwoutput_send_frame(dev);
- /* Ready for the next frame! (select() might be waiting) */
- wake_up_interruptible(&dev->new_video_frame);
- }
- /* Send a frame from a user buffer to the device
- * Return: negative = error
- * 0 = keep waiting
- * positive = count of bytes sent successfully
- */
- static long
- hwoutput_write(struct output_device *dev,
- const __u8 *user_buffer,
- int user_buffer_size)
- {
- int len = user_buffer_size;
- if (!dev->ints_enabled)
- return -EIO;
- if (!dev->hwoutput_completed)
- {/* Not ready for another frame yet */
- //debug_msg("Not ready for write.n");
- return 0;/* caller should keep waiting */
- }
- len = hwoutput_writeimage(dev, dev->hwoutput_buffer,
- user_buffer, user_buffer_size, 1);
- return len;
- }
- /* Stop and free all resources used for operation.
- */
- static void
- hwoutput_close(struct output_device *dev)
- {
- int i;
- if (dev->streaming)
- hwoutput_streamoff(dev, V4L2_BUF_TYPE_VIDEOOUT);
- hwoutput_abort(dev);
- dev->ready_to_output = 0;
- if (dev->hwoutput_dma_list)
- free_page((unsigned long)dev->hwoutput_dma_list);
- dev->hwoutput_dma_list = 0;
- if (dev->hwoutput_buffer != NULL)
- vfree(dev->hwoutput_buffer);
- dev->hwoutput_buffer = NULL;
- dev->hwoutput_buffer_size = 0;
- for (i = 0; i < MAX_OUTPUT_BUFFERS; ++i)
- {
- dev->stream_buf[i].requested = 0;
- if (dev->stream_buf[i].vaddress)
- vfree(dev->stream_buf[i].vaddress);
- dev->stream_buf[i].vaddress = NULL;
- if (dev->stream_buf[i].dma_list)
- free_page((unsigned long)dev->stream_buf[i].dma_list);
- dev->stream_buf[i].dma_list = NULL;
- }
- }
- /*
- *
- * I N T E R R U P T R O U T I N E S
- *
- */
- /* This function runs at interrupt time, either in response to a hardware
- * interrupt, or on each timer tick if there is no hardware interrupt.
- */
- static void
- interrupt_handler(void *v)
- {
- struct output_device *dev = (struct output_device *)v;
- if (!dev->ints_enabled)
- return;
- /* Call "bottom half" of handler */
- dev->tqnode_dpc.routine = hwoutput_interrupt;
- dev->tqnode_dpc.data = dev;
- queue_task(&dev->tqnode_dpc, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
- if (dev->tlnode.function != NULL &&
- dev->ints_enabled)
- {/* Poll again on next timer tick */
- dev->tlnode.expires = jiffies + HZ/100;
- add_timer(&dev->tlnode);
- }
- }
- /* Called by the system when our hardware interrupt occurs
- */
- static void
- interrupt_hw(int irq, void *v,
- struct pt_regs *regs)
- {
- /* struct output_device *dev = (struct output_device *)v; */
- if (v == NULL)
- return;
- /* if (!(our device has interrupt pending)) */
- /* { */
- /* Not us. Likely another device is sharing the IRQ */
- /* debug_msg("HW interrupt (not this device)n"); */
- /* return; */
- /* } */
- //debug_msg("HW interrupt (real)n");
- interrupt_handler(v);
- }
- static void
- interrupt_disable(struct output_device *dev)
- {
- if (!dev->ints_enabled)
- return;
- dev->ints_enabled = 0;
- /* TODO: Disable interrupts on the device */
- if (dev->irq == 0)
- {
- del_timer(&dev->tlnode);
- }
- /* Wake up any processes that might be waiting for a frame */
- /* and let them return an error */
- wake_up_interruptible(&dev->new_video_frame);
- }
- static void
- interrupt_enable(struct output_device *dev)
- {
- if (dev->ints_enabled)
- interrupt_disable(dev);
- dev->ints_enabled = 1;
- /* TODO: Enable interrupts on the device */
- dev->tlnode.function = NULL; /* NULL indicates h/w interrupts */
- if (dev->irq == 0)
- {
- init_timer(&dev->tlnode);
- dev->tlnode.function =
- (void(*)(unsigned long))interrupt_handler;
- dev->tlnode.data = (unsigned long)dev;
- dev->tlnode.expires = jiffies + HZ/100;
- add_timer(&dev->tlnode);
- }
- debug_msg("%s interrupts enabledn",
- dev->tlnode.function?"Polled":"Hardware");
- }
- /*
- *
- * M E M O R Y M A P P I N G
- *
- */
- static struct stream_buffer *
- mmap_stream_buffer_from_offset(struct output_device *dev,
- unsigned long offset)
- {
- int i;
- for (i = 0; i < MAX_OUTPUT_BUFFERS; ++i)
- if (offset == dev->stream_buf[i].vidbuf.offset)
- return &dev->stream_buf[i];
- return NULL;
- }
- static int
- mmap_request_buffers(struct output_device *dev,
- struct v4l2_requestbuffers *req)
- {
- int i;
- u32 buflen;
- 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_OUTPUT_BUFFERS)
- req->count = MAX_OUTPUT_BUFFERS;
- req->type = V4L2_BUF_TYPE_VIDEOOUT; /* only kind I know */
- /* The buffer length needs to be a multiple of the page size */
- buflen = (dev->clientfmt.sizeimage + PAGE_SIZE - 1)
- & ~(PAGE_SIZE - 1);
- debug_msg("Granting %d buffersn",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 = req->type;
- dev->stream_buf[i].vidbuf.offset = 4*i;/* anything unique */
- dev->stream_buf[i].vidbuf.length = buflen;
- dev->stream_buf[i].vidbuf.bytesused = 0;
- dev->stream_buf[i].vidbuf.timestamp.tv_sec = 0;
- dev->stream_buf[i].vidbuf.timestamp.tv_usec = 0;
- dev->stream_buf[i].vidbuf.flags = 0;
- }
- for (i = req->count; i < MAX_OUTPUT_BUFFERS; ++i)
- dev->stream_buf[i].requested = 0;
- return 1;
- }
- static void
- mmap_unrequest_buffers(struct output_device *dev)
- {
- int i;
- for (i = 0; i < MAX_OUTPUT_BUFFERS; ++i)
- dev->stream_buf[i].requested = 0;
- }
- static void
- mmap_vma_open(struct vm_area_struct *vma)
- {
- struct output_device *dev =
- output_device_from_file(vma->vm_file);
- if (dev == NULL)
- return;
- //debug_msg("vma_open calledn");
- ++dev->stream_buffers_mapped;
- //MOD_INC_USE_COUNT;
- }
- static void
- mmap_vma_close(struct vm_area_struct *vma)
- {
- struct output_device *dev =
- output_device_from_file(vma->vm_file);
- struct stream_buffer *buf =
- mmap_stream_buffer_from_offset(dev, vma->vm_offset);
- if (dev->streaming)
- {
- info_msg("Warning- munmap() called while streamingn");
- hwoutput_streamoff(dev, buf->vidbuf.type);
- }
- v4l2_q_yank_node(&dev->stream_q_output, &buf->qnode);
- v4l2_q_yank_node(&dev->stream_q_done, &buf->qnode);
- if (buf->vaddress != NULL)
- vfree(buf->vaddress);
- buf->vaddress = NULL;
- if (buf->dma_list)
- free_page((unsigned long)buf->dma_list);
- buf->dma_list = NULL;
- buf->vidbuf.flags = 0;
- //debug_msg("Buffer %d deallocatedn",(int)vma->vm_offset/4);
- if (dev->stream_buffers_mapped > 0)
- --dev->stream_buffers_mapped;
- //MOD_DEC_USE_COUNT;
- }
- static unsigned long
- mmap_vma_nopage(struct vm_area_struct *vma,
- unsigned long address, int write)
- {
- struct output_device *dev;
- struct stream_buffer *buf;
- unsigned long offset_into_buffer;
- unsigned long page;
- dev = output_device_from_file(vma->vm_file);
- if (dev == NULL)
- return 0;
- buf = mmap_stream_buffer_from_offset(dev, vma->vm_offset);
- if (buf == NULL)
- return 0;
- offset_into_buffer = address - vma->vm_start;
- if (offset_into_buffer >= buf->vidbuf.length)
- {
- err_msg("Attempt to read past end of mmap() buffern");
- return 0;
- }
- page = v4l2_vmalloc_to_page(buf->vaddress + offset_into_buffer);
- if (page == 0)
- return 0;
- atomic_inc(&mem_map[MAP_NR(page)].count);
- return page;
- }
- static struct vm_operations_struct hwoutput_vma_operations =
- {
- mmap_vma_open, mmap_vma_close, NULL, NULL, NULL, NULL,
- mmap_vma_nopage,
- };
- /*
- *
- * V I D E O F O R L I N U X I N T E R F A C I N G
- *
- */
- static int
- v4l2_open(struct v4l2_device *v, int flags, void **idptr)
- {
- struct output_device *dev = (struct output_device *)v;
- int i, n;
- int io;
- for (i = 0, n = -1, io = 0; i < MAX_OPENS; ++i)
- {
- if (!dev->open_data[i].isopen)
- n = i;/* available open_data structure */
- else if (!dev->open_data[i].noio)
- io = 1;/* another open is already operating */
- }
- if (n == -1)/* No available open_data structures */
- {
- debug_msg("No more opens on this devicen");
- return -EBUSY;
- }
- if (flags & O_NOIO)/* no-I/O open */
- dev->open_data[n].noio = 1;
- else if (io)
- {
- debug_msg("No more I/O opens on this devicen");
- return -EBUSY;
- }
- else
- {
- dev->open_data[n].noio = 0;
- /* Keep track of whether there is an I/O open */
- ++dev->io_opens;
- dev->perf.frames = 0;
- dev->perf.framesdropped = 0;
- dev->perf.bytesout = 0;
- }
- //MOD_INC_USE_COUNT;
- ++dev->open_count;
- dev->open_data[n].isopen = 1;
- dev->open_data[n].dev = dev;
- *idptr = &dev->open_data[n];
- if (dev->open_count == 1)
- {
- if (dev->pci && dev->irq == 0)
- {
- dev->irq = dev->pci->irq;
- if (request_irq(dev->irq, interrupt_hw, SA_SHIRQ,
- dev->shortname, dev) < 0)
- {
- err_msg("Denied IRQ %dn", dev->irq);
- dev->irq = 0;
- }
- }
- dev->ready_to_output = 0;/* benchmark changes parameters! */
- dev->hwoutput_completed = 0;
- dev->hwoutput_enabled = 0;
- v4l2_q_init(&dev->stream_q_output);
- v4l2_q_init(&dev->stream_q_done);
- }
- debug_msg("Open succeededn");
- return 0;
- }
- static void
- v4l2_close(void *id)
- {
- struct device_open *o = (struct device_open *)id;
- struct output_device *dev = o->dev;
- if (!o->noio)
- {
- --dev->io_opens;
- debug_msg("Closen");
- }
- o->isopen = 0;
- --dev->open_count;
- if (dev->open_count == 0)
- {
- interrupt_disable(dev);
- hwoutput_close(dev);
- if (dev->irq)
- free_irq(dev->irq, dev);
- dev->irq = 0;
- }
- //MOD_DEC_USE_COUNT;
- }
- /* The arguments are already copied into kernel memory, so don't use
- copy_from_user() or copy_to_user() on arg. */
- static int
- v4l2_ioctl(void *id,
- unsigned int cmd,
- void *arg)
- {
- struct device_open *o = (struct device_open *)id;
- struct output_device *dev = o->dev;
- // debug_msg("ioctl %dn", _IOC_NR(cmd));
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *b = arg;
- strcpy(b->name, dev->v.name);
- b->type = V4L2_TYPE_OUTPUT;
- b->flags = V4L2_FLAG_WRITE |
- V4L2_FLAG_STREAMING |
- V4L2_FLAG_SELECT;
- b->outputs = dev->viden.num_outputs;
- b->inputs = 0;
- b->audios = 0;
- b->maxwidth = MAX_WIDTH;
- b->maxheight = MAX_HEIGHT;
- b->minwidth = MIN_WIDTH;
- b->minheight = MIN_HEIGHT;
- b->maxframerate = 30;
- return 0;
- }
- case VIDIOC_ENUM_OUTFMT:
- {
- struct v4l2_fmtdesc *f = arg;
- if (f->index < 0 || f->index >= NUM_OUTFMT)
- return -EINVAL;
- *f = outfmt[f->index];
- return 0;
- }
- case VIDIOC_G_FMT:
- {
- memcpy(arg, &dev->clientfmt, sizeof(dev->clientfmt));
- return 0;
- }
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *fmt = arg;
- if (o->noio)
- {
- debug_msg("S_FMT illegal in no-I/O openn");
- return -EPERM;
- }
- if (dev->stream_buffers_mapped)
- {
- debug_msg("Can't set format if buffers are mappedn");
- return -EPERM;
- }
- dev->clientfmt = *fmt;
- hwoutput_new_format(dev);
- mmap_unrequest_buffers(dev);
- *fmt = dev->clientfmt;
- return 0;
- }
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *req = arg;
- if (o->noio)
- {
- debug_msg("REQBUFS illegal in no-I/O openn");
- return -EPERM;
- }
- if (dev->stream_buffers_mapped)
- {
- debug_msg("Can't request buffers if buffers are "
- "already mappedn");
- return -EPERM;
- }
- hwoutput_begin(dev);
- if (!mmap_request_buffers(dev, req))
- return -EINVAL;
- return 0;
- }
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *buf = arg;
- int i;
- if (o->noio)
- {
- debug_msg("QUERYBUF illegal in no-I/O openn");
- return -EPERM;
- }
- i = buf->index;
- if (i < 0 || i >= MAX_OUTPUT_BUFFERS ||
- !dev->stream_buf[i].requested ||
- (buf->type & V4L2_BUF_TYPE_field) !=
- (dev->stream_buf[i].vidbuf.type & V4L2_BUF_TYPE_field))
- {
- debug_msg("QUERYBUF bad parametern");
- return -EINVAL;
- }
- *buf = dev->stream_buf[i].vidbuf;
- return 0;
- }
- case VIDIOC_QBUF:
- {
- struct v4l2_buffer *buf =arg;
- if (o->noio)
- {
- debug_msg("QBUF illegal in no-I/O openn");
- return -EPERM;
- }
- if (!dev->stream_buffers_mapped)
- {
- debug_msg("QBUF no buffers are mappedn");
- return -EINVAL;
- }
- if (!hwoutput_queuebuffer(dev, buf))
- return -EINVAL;
- return 0;
- }
- case VIDIOC_DQBUF:
- {
- struct v4l2_buffer *buf = arg;
- if (o->noio)
- {
- debug_msg("DQBUF illegal in no-I/O openn");
- return -EPERM;
- }
- if (!hwoutput_dequeuebuffer(dev, buf))
- return -EINVAL;
- return 0;
- }
- case VIDIOC_STREAMON:
- {
- __u32 type = (__u32)arg;
- if (o->noio)
- {
- debug_msg("STREAMON illegal in no-I/O openn");
- return -EPERM;
- }
- if (!hwoutput_streamon(dev, type))
- return -EINVAL;
- return 0;
- }
- case VIDIOC_STREAMOFF:
- {
- __u32 type = (__u32)arg;
- if (o->noio)
- {
- debug_msg("STREAMOFF illegal in no-I/O openn");
- return -EPERM;
- }
- hwoutput_streamoff(dev, type);
- return 0;
- }
- case VIDIOC_ENUM_FBUFFMT:
- {
- struct v4l2_fmtdesc* fmt = (struct v4l2_fmtdesc*)arg;
- strcpy( fmt->description, "Video Overlay" );
- fmt->pixelformat = 0;
- fmt->flags = 0;
- fmt->depth = 0;
- return 0;
- }
- case VIDIOC_G_FBUF:
- (struct v4l2_framebuffer*)arg = &dev->fbuf;
- return 0;
- case VIDIOC_S_FBUF: return -EINVAL;
- case VIDIOC_G_WIN:
- (struct v4l2_window*)arg = &dev->window;
- return 0;
- case VIDIOC_S_WIN:
- {
- struct v4l2_window* win = (struct v4l2_window*)arg;
- int red, green, blue;
- if( ( win->clips != NULL ) || ( win->clipcount != 0 ) )
- return -EINVAL;
- memcpy( &dev->window, win, sizeof(dev->window) );
-
- /* set on hardware */
- mgavideo_set_window( dev->mga, win->x, win->y,
- win->width, win->height );
- red = (win->chromakey >> 16) & 0xff;
- green = (win->chromakey >> 8) & 0xff;
- blue = (win->chromakey >> 0) & 0xff;
- mgavideo_set_colorkey( dev->mga, red, green, blue );
- return 0;
- }
- case VIDIOC_PREVIEW:
- mgavideo_preview_enable( dev->mga, *(int*)arg );
- return 0;
- /* TODO: Handle above if output is to frame buffer or
- graphics overlay */
- case VIDIOC_G_PERF:
- {
- memcpy(arg, &dev->perf, sizeof(dev->perf));
- return 0;
- }
- case VIDIOC_G_OUTPUT:
- {
- memcpy(arg, &dev->output, sizeof(dev->output));
- return 0;
- }
- case VIDIOC_S_OUTPUT:
- {
- int output = (int)arg;
- if (output < 0 || output >= dev->viden.num_outputs)
- {
- debug_msg("Output out of range %dn", output);
- return -EINVAL;
- }
- if (output != dev->output)
- {
- dev->output = output;
- set_video_output(dev, output);
- }
- return 0;
- }
- case VIDIOC_G_OUTPARM:
- {
- memcpy(arg, &dev->outputparm, sizeof(dev->outputparm));
- return 0;
- }
- case VIDIOC_S_OUTPARM:
- {
- struct v4l2_outputparm *vp = arg;
- if (copy_from_user(&vp, arg, sizeof(vp)))
- return -EFAULT;
- if (vp->outputmode & ~dev->outputparm.capability)
- {
- debug_msg("OUTPARM unsupported capabilityn");
- return -EINVAL;
- }
- if ((dev->outputparm.capability & V4L2_CAP_TIMEPERFRAME) &&
- vp->timeperframe < 10000)
- {
- debug_msg("OUTPARM time per frame out of range %ldn",
- vp->timeperframe);
- return -EINVAL;
- }
- if (vp->outputmode != dev->outputparm.outputmode &&
- !o->noio && dev->streaming)
- {
- debug_msg("OUTPARM can't change mode while "
- "streamingn");
- return -EINVAL;
- }
- if (o->noio)
- return 0;
- if (vp->outputmode != dev->outputparm.outputmode)
- {
- dev->outputparm.outputmode = vp->outputmode;
- hwoutput_new_format(dev);
- }
- if ((vp->outputmode & V4L2_CAP_TIMEPERFRAME) &&
- vp->timeperframe >= dev->viden.frame_period)
- dev->outputparm.timeperframe = vp->timeperframe;
- else
- dev->outputparm.timeperframe = dev->viden.frame_period;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- struct v4l2_standard *std = arg;
- v4l2_video_std_construct(std, dev->viden.standard, 0);
- return 0;
- }
- case VIDIOC_S_STD:
- {
- struct v4l2_standard *std = arg;
- int id;
- if ((o->noio && dev->io_opens) ||
- dev->stream_buffers_mapped)
- return -EPERM;
- id = v4l2_video_std_confirm(std);
- if (!((1 << id) & dev->viden.standards))
- {
- debug_msg("Bad standard: %un", (unsigned)id);
- return -EINVAL;
- }
- dev->viden.set_standard(dev, id);
- return 0;
- }
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_enumstd *estd = arg;
- __u32 b, i;
- if (estd->index < 0 || estd->index > 30)
- return -EINVAL;
- for (b = 1, i = 0; b < 32; ++b)
- {
- if (((1 << b) & dev->viden.standards) == 0)
- continue;
- if (i == estd->index)
- {
- v4l2_video_std_construct(&estd->std, b, 0);
- estd->outputs = (__u32)-1; /* all outputs */
- estd->inputs = 0;
- return 0;
- }
- ++i;
- }
- return -EINVAL;
- }
- case VIDIOC_ENUMOUTPUT:
- {
- struct v4l2_output *vo = arg;
- if (vo->index < 0 || vo->index >= dev->viden.num_outputs)
- return -EINVAL;
- *vo = dev->voutput[vo->index].output;
- return 0;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
- i = find_vctrl(qc->id);
- if (i < 0)
- return i;
- /* V4L2 filled in category and catname, preserve them */
- output_control[i].category = qc->category;
- memcpy(output_control[i].catname, qc->catname,
- sizeof(qc->catname));
- *qc = output_control[i];
- return 0;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *vc = arg;
- int i;
- i = find_vctrl(vc->id);
- if (i < 0)
- return i;
- vc->value = dev->voutput[dev->output].control[i];
- return 0;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *vc = arg;
- int i;
- i = find_vctrl(vc->id);
- if (i < 0)
- return i;
- dev->voutput[dev->output].control[i] = vc->value;
- device_tone_controls(dev);
- return 0;
- }
- case VIDIOC_G_MODULATOR: return -EINVAL;
- case VIDIOC_S_MODULATOR: return -EINVAL;
- case VIDIOC_G_FREQ: return -EINVAL;
- case VIDIOC_S_FREQ: return -EINVAL;
- case VIDIOC_G_AUDIO: return -EINVAL;
- case VIDIOC_S_AUDIO: return -EINVAL;
- default:
- return I34_ioctl(dev->mga, cmd, arg);
- }
- return 0;
- }
- static int
- v4l2_mmap(void *id,
- struct vm_area_struct *vma)
- {
- struct device_open *o = (struct device_open *)id;
- struct output_device *dev = o->dev;
- struct stream_buffer *buf;
- if (o->noio)
- {
- debug_msg("mmap() called on no-I/O openn");
- return -ENODEV;
- }
- buf = mmap_stream_buffer_from_offset(dev, vma->vm_offset);
- if (buf == NULL)
- {
- debug_msg("mmap() Invalid offset parametern");
- return -EINVAL;/* no such buffer */
- }
- if (!buf->requested)
- {
- debug_msg("mmap() Buffer is not available for mappingn");
- return -EINVAL;/* not requested */
- }
- if (buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED)
- {
- debug_msg("mmap() Buffer is already mappedn");
- return -EINVAL;/* already mapped */
- }
- if (buf->vidbuf.length != vma->vm_end - vma->vm_start)
- {
- debug_msg("mmap() Wrong length parametern");
- return -EINVAL;/* wrong length */
- }
- if (buf->vaddress != NULL)
- vfree(buf->vaddress);
- buf->vaddress = vmalloc(buf->vidbuf.length);
- if (buf->vaddress == NULL)
- {
- err_msg("Could not allocate mmap() buffern");
- return -ENODEV;
- }
- #if 0
- if ((using DMA) &&
- !bm_build_gather_list(dev, buf->vaddress, &buf->dma_list))
- return -ENODEV;
- #endif
- buf->vidbuf.flags |= V4L2_BUF_FLAG_MAPPED;
- vma->vm_ops = &hwoutput_vma_operations;
- if (vma->vm_ops->open)
- vma->vm_ops->open(vma);
- /* Note: vma->vm_file will be set up by V4L2 */
- return 0;
- }
- static int
- v4l2_poll(void *id,
- struct file *file,
- poll_table *table)
- {
- struct device_open *o = (struct device_open *)id;
- struct output_device *dev = o->dev;
- if (o->noio)
- {
- debug_msg("poll() illegal in no-I/O openn");
- return POLLERR;
- }
- if (dev->streaming)
- {
- void *node;
- node = v4l2_q_peek_head(&dev->stream_q_done);
- if (node != NULL)
- return (POLLIN | POLLRDNORM);/* have done buffers */
- node = v4l2_q_peek_head(&dev->stream_q_output);
- if (node == NULL)
- return POLLERR; /* no buffers queued */
- poll_wait(file, &dev->new_video_frame, table);
- return 0;
- }
- /* Output is through write() call */
- if (dev->hwoutput_completed)/* ready for new data now */
- return (POLLIN | POLLRDNORM);
- if (!dev->ready_to_output)/* Not set up to accept data yet */
- return POLLERR;
- poll_wait(file, &dev->new_video_frame, table);
- return 0;
- }
- static long
- v4l2_read(void *id,
- char *buf,
- unsigned long count,
- int noblock)
- {
- debug_msg("read() not handledn");
- return -EINVAL;
- }
- static long
- v4l2_write(void *id,
- const char *buf,
- unsigned long count,
- int noblock)
- {
- struct device_open *o = (struct device_open *)id;
- struct output_device *dev = o->dev;
- long len = 0;
- long my_timeout;
- if (o->noio)
- {
- debug_msg("write() illegal in no-I/O openn");
- return -EPERM;
- }
- if (dev->streaming)
- {
- debug_msg("Can't write() when streaming is onn");
- return -EPERM;
- }
- hwoutput_begin(dev);/* does nothing if device is already ready */
- if (!dev->ready_to_output)
- {
- debug_msg("Can't send frames!n");
- return 0;
- }
- my_timeout = HZ / 5;
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127)
- current->timeout = jiffies + my_timeout;
- #endif
- while (len == 0)
- {
- if (noblock)
- {
- /* Is previous frame still in progress? */
- if (!dev->hwoutput_completed)
- return -EAGAIN;
- }
- else
- {
- /* watch out for race condition going to sleep! */
- cli();
- if (!dev->hwoutput_completed)
- {
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127)
- interruptible_sleep_on(&dev->new_video_frame);
- #else
- my_timeout = interruptible_sleep_on_timeout(
- &dev->new_video_frame, my_timeout);
- #endif
- }
- sti();
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127)
- if (current->timeout <= jiffies)
- #else
- if (my_timeout == 0)
- #endif
- {
- debug_msg("Timeout on readn");
- break;
- }
- len = hwoutput_write(dev, buf, count);
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127)
- current->timeout = 0;
- #endif
- //debug_msg("read %dn", (int)len);
- return len;
- }
- /*
- * Remaining initialization of video encoder etc. This is only
- * done when the device is successfully identified and registered.
- */
- static int
- v4l2_init_done(struct v4l2_device *v)
- {
- struct output_device *dev = (struct output_device *)v;
- int i;
- /* Initialize video input array */
- for (i = 0; i < VOUTPUT_COUNT; ++i)
- {
- dev->voutput[i].output.index = i;
- dev->voutput[i].output.type = V4L2_OUTPUT_TYPE_ANALOG;
- dev->voutput[i].output.capability = 0;
- /* Initialize video control properties */
- dev->voutput[i].control[VCTRL_BRIGHTNESS] =
- output_control[VCTRL_BRIGHTNESS].default_value;
- dev->voutput[i].control[VCTRL_CONTRAST] =
- output_control[VCTRL_CONTRAST].default_value;
- dev->voutput[i].control[VCTRL_SATURATION] =
- output_control[VCTRL_SATURATION].default_value;
- /* dev->voutput[i].control[VCTRL_HUE] = */
- /* output_control[VCTRL_HUE].default_value; */
- }
- strcpy(dev->voutput[VOUTPUT_COMP].output.name, "Composite");
- strcpy(dev->voutput[VOUTPUT_SVIDEO].output.name, "S-Video");
- /* Initialize the video encoder hardware */
- dev->viden.initialize(dev);
- /* BUG: get defaults from user somehow... */
- dev->viden.set_standard(dev, V4L2_STD_NTSC);
- set_video_output(dev, VOUTPUT_COMP);
- /* Output mode parameters */
- dev->outputparm.capability = V4L2_CAP_TIMEPERFRAME;
- dev->outputparm.outputmode = 0;
- dev->outputparm.extendedmode = 0;
- dev->outputparm.timeperframe = dev->viden.frame_period;
- /* Default image dimensions */
- dev->clientfmt.width = 160;
- dev->clientfmt.height = 120;
- dev->clientfmt.depth = 16;
- dev->clientfmt.pixelformat = V4L2_PIX_FMT_RGB565;
- dev->clientfmt.flags = 0;
- dev->clientfmt.bytesperline = 0;
- dev->clientfmt.sizeimage = 0;
- hwoutput_new_format(dev);
- return 0;
- }
- /* =====================================================================
- * The functions below this point are only called during loading
- * and unloading of the driver.
- */
- /*
- * D E V I C E I N I A L I Z A T I O N R O U T I N E S
- *
- * These routines locate and enable the hardware, and initialize
- * the device structure.
- */
- #if 0
- /* Variables for assigning resources via the command line
- */
- /* ISA non-PnP IO base overrides */
- static int isa0_iobase = 0;
- static int isa1_iobase = 0;
- #ifdef MODULE_PARM
- MODULE_PARM(isa0_iobase, "i");
- MODULE_PARM(isa1_iobase, "i");
- #endif
- /* ISA PnP IO base override */
- static int pnp0_iobase = 0;
- #ifdef MODULE_PARM
- MODULE_PARM(pnp0_iobase, "i");
- #endif
- #endif
- /* Initialize v4l2_device fields */
- static int
- init_device_fields(struct output_device *dev)
- {
- int num = dev - outputdev;
- sprintf(dev->v.name, "Matrox DVD Video Out Driver (%d)", num);
- dev->v.type = V4L2_TYPE_OUTPUT;
- dev->v.minor = unit_vout[num];
- dev->v.open = v4l2_open;
- dev->v.close = v4l2_close;
- dev->v.read = v4l2_read;
- dev->v.write = v4l2_write;
- dev->v.ioctl = v4l2_ioctl;
- dev->v.mmap = v4l2_mmap;
- dev->v.poll = v4l2_poll;
- dev->v.initialize = v4l2_init_done;
- dev->v.priv = NULL;
- return 1;/* OK */
- }
- static int
- config_a_device(struct output_device *dev)
- {
- sprintf(dev->shortname, "videoout%d", dev - outputdev);
- /* TODO: Search for an unconfigured device, configure the */
- /* I/O port */
- /* if (!(found another device)) */
- /* return 0; */
- if ((dev->mga = mgavideo_get()) == NULL)
- return 0;
-
- device_initialize(dev);
- if (!init_device_fields(dev))
- return 0;
- if (!find_encoder(dev))
- {
- err_msg("Bad or unrecognized video encodern");
- return 0;/* failed */
- }
- return 1;
- }
- static void
- unconfig_a_device(struct output_device *dev)
- {
- interrupt_disable(dev);
- hwoutput_close(dev);
- /* TODO: Unconfigure the device, free the I/O port, etc. */
- mgavideo_release(dev->mga);
-
- if (dev->is_registered)
- {
- v4l2_unregister_device((struct v4l2_device *)dev);
- info_msg("Removed device %sn", dev->shortname);
- }
- memset(dev, 0, sizeof(outputdev[0]));
- }
- /*
- * M O D U L E I N I T A N D C L E A N U P
- */
- int
- init_module(void)
- {
- int i;
- for (i = 0; i < NBOARDS; ++i)
- {
- memset(&outputdev[i], 0, sizeof(outputdev[0]));
- if (!config_a_device(&outputdev[i]))
- {
- break;
- }
- if (v4l2_register_device(
- (struct v4l2_device *)&outputdev[i]) != 0)
- {
- err_msg("Couldn't register the driver.n");
- unconfig_a_device(&outputdev[i]);
- return 0;
- }
- outputdev[i].is_registered = 1;
- }
- if (i == 0)
- {
- err_msg("No devices found.n");
- return -ENODEV;/* cleanup will not be called */
- }
- return 0;
- }
- void
- cleanup_module(void)
- {
- int i;
- for (i = 0; i < NBOARDS; ++i)
- unconfig_a_device(&outputdev[i]);
- }