📄 usbvideo.c
字号:
/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/mm.h>#include <linux/smp_lock.h>#include <linux/vmalloc.h>#include <linux/init.h>#include <linux/spinlock.h>#include <asm/io.h>#include "usbvideo.h"#if defined(MAP_NR)#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */#endifstatic int video_nr = -1;module_param(video_nr, int, 0);/* * Local prototypes. */static void usbvideo_Disconnect(struct usb_interface *intf);static void usbvideo_CameraRelease(struct uvd *uvd);static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);static int usbvideo_v4l_open(struct inode *inode, struct file *file);static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);static int usbvideo_v4l_close(struct inode *inode, struct file *file);static int usbvideo_StartDataPump(struct uvd *uvd);static void usbvideo_StopDataPump(struct uvd *uvd);static int usbvideo_GetFrame(struct uvd *uvd, int frameNum);static int usbvideo_NewFrame(struct uvd *uvd, int framenum);static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, struct usbvideo_frame *frame);/*******************************//* Memory management functions *//*******************************/static void *usbvideo_rvmalloc(unsigned long size){ void *mem; unsigned long adr; size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr = (unsigned long) mem; while (size > 0) { SetPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } return mem;}static void usbvideo_rvfree(void *mem, unsigned long size){ unsigned long adr; if (!mem) return; adr = (unsigned long) mem; while ((long) size > 0) { ClearPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree(mem);}static void RingQueue_Initialize(struct RingQueue *rq){ assert(rq != NULL); init_waitqueue_head(&rq->wqh);}static void RingQueue_Allocate(struct RingQueue *rq, int rqLen){ /* Make sure the requested size is a power of 2 and round up if necessary. This allows index wrapping using masks rather than modulo */ int i = 1; assert(rq != NULL); assert(rqLen > 0); while(rqLen >> i) i++; if(rqLen != 1 << (i-1)) rqLen = 1 << i; rq->length = rqLen; rq->ri = rq->wi = 0; rq->queue = usbvideo_rvmalloc(rq->length); assert(rq->queue != NULL);}static int RingQueue_IsAllocated(const struct RingQueue *rq){ if (rq == NULL) return 0; return (rq->queue != NULL) && (rq->length > 0);}static void RingQueue_Free(struct RingQueue *rq){ assert(rq != NULL); if (RingQueue_IsAllocated(rq)) { usbvideo_rvfree(rq->queue, rq->length); rq->queue = NULL; rq->length = 0; }}int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len){ int rql, toread; assert(rq != NULL); assert(dst != NULL); rql = RingQueue_GetLength(rq); if(!rql) return 0; /* Clip requested length to available data */ if(len > rql) len = rql; toread = len; if(rq->ri > rq->wi) { /* Read data from tail */ int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; memcpy(dst, rq->queue + rq->ri, read); toread -= read; dst += read; rq->ri = (rq->ri + read) & (rq->length-1); } if(toread) { /* Read data from head */ memcpy(dst, rq->queue + rq->ri, toread); rq->ri = (rq->ri + toread) & (rq->length-1); } return len;}EXPORT_SYMBOL(RingQueue_Dequeue);int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n){ int enqueued = 0; assert(rq != NULL); assert(cdata != NULL); assert(rq->length > 0); while (n > 0) { int m, q_avail; /* Calculate the largest chunk that fits the tail of the ring */ q_avail = rq->length - rq->wi; if (q_avail <= 0) { rq->wi = 0; q_avail = rq->length; } m = n; assert(q_avail > 0); if (m > q_avail) m = q_avail; memcpy(rq->queue + rq->wi, cdata, m); RING_QUEUE_ADVANCE_INDEX(rq, wi, m); cdata += m; enqueued += m; n -= m; } return enqueued;}EXPORT_SYMBOL(RingQueue_Enqueue);static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq){ assert(rq != NULL); interruptible_sleep_on(&rq->wqh);}void RingQueue_WakeUpInterruptible(struct RingQueue *rq){ assert(rq != NULL); if (waitqueue_active(&rq->wqh)) wake_up_interruptible(&rq->wqh);}EXPORT_SYMBOL(RingQueue_WakeUpInterruptible);void RingQueue_Flush(struct RingQueue *rq){ assert(rq != NULL); rq->ri = 0; rq->wi = 0;}EXPORT_SYMBOL(RingQueue_Flush);/* * usbvideo_VideosizeToString() * * This procedure converts given videosize value to readable string. * * History: * 07-Aug-2000 Created. * 19-Oct-2000 Reworked for usbvideo module. */static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs){ char tmp[40]; int n; n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); assert(n < sizeof(tmp)); if ((buf == NULL) || (bufLen < n)) err("usbvideo_VideosizeToString: buffer is too small."); else memmove(buf, tmp, n);}/* * usbvideo_OverlayChar() * * History: * 01-Feb-2000 Created. */static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, int x, int y, int ch){ static const unsigned short digits[16] = { 0xF6DE, /* 0 */ 0x2492, /* 1 */ 0xE7CE, /* 2 */ 0xE79E, /* 3 */ 0xB792, /* 4 */ 0xF39E, /* 5 */ 0xF3DE, /* 6 */ 0xF492, /* 7 */ 0xF7DE, /* 8 */ 0xF79E, /* 9 */ 0x77DA, /* a */ 0xD75C, /* b */ 0xF24E, /* c */ 0xD6DC, /* d */ 0xF34E, /* e */ 0xF348 /* f */ }; unsigned short digit; int ix, iy; if ((uvd == NULL) || (frame == NULL)) return; if (ch >= '0' && ch <= '9') ch -= '0'; else if (ch >= 'A' && ch <= 'F') ch = 10 + (ch - 'A'); else if (ch >= 'a' && ch <= 'f') ch = 10 + (ch - 'a'); else return; digit = digits[ch]; for (iy=0; iy < 5; iy++) { for (ix=0; ix < 3; ix++) { if (digit & 0x8000) { if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) {/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); } } digit = digit << 1; } }}/* * usbvideo_OverlayString() * * History: * 01-Feb-2000 Created. */static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, int x, int y, const char *str){ while (*str) { usbvideo_OverlayChar(uvd, frame, x, y, *str); str++; x += 4; /* 3 pixels character + 1 space */ }}/* * usbvideo_OverlayStats() * * Overlays important debugging information. * * History: * 01-Feb-2000 Created. */static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame){ const int y_diff = 8; char tmp[16]; int x = 10, y=10; long i, j, barLength; const int qi_x1 = 60, qi_y1 = 10; const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; /* Call the user callback, see if we may proceed after that */ if (VALID_CALLBACK(uvd, overlayHook)) { if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) return; } /* * We draw a (mostly) hollow rectangle with qi_xxx coordinates. * Left edge symbolizes the queue index 0; right edge symbolizes * the full capacity of the queue. */ barLength = qi_x2 - qi_x1 - 2; if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) {/* TODO */ long u_lo, u_hi, q_used; long m_ri, m_wi, m_lo, m_hi; /* * Determine fill zones (used areas of the queue): * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length * * if u_lo < 0 then there is no first filler. */ q_used = RingQueue_GetLength(&uvd->dp); if ((uvd->dp.ri + q_used) >= uvd->dp.length) { u_hi = uvd->dp.length; u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); } else { u_hi = (q_used + uvd->dp.ri); u_lo = -1; } /* Convert byte indices into screen units */ m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); for (j=qi_y1; j < (qi_y1 + qi_h); j++) { for (i=qi_x1; i < qi_x2; i++) { /* Draw border lines */ if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || (i == qi_x1) || (i == (qi_x2 - 1))) { RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); continue; } /* For all other points the Y coordinate does not matter */ if ((i >= m_ri) && (i <= (m_ri + 3))) { RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); } else if ((i >= m_wi) && (i <= (m_wi + 3))) { RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); } } } sprintf(tmp, "%8lx", uvd->stats.frame_num); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8lx", uvd->stats.urb_count); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8lx", uvd->stats.urb_length); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8lx", uvd->stats.data_count); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8lx", uvd->stats.header_count); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8lx", uvd->stats.iso_err_count); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8x", uvd->vpic.colour); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8x", uvd->vpic.hue); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff; sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); usbvideo_OverlayString(uvd, frame, x, y, tmp); y += y_diff;}/* * usbvideo_ReportStatistics() * * This procedure prints packet and transfer statistics. * * History: * 14-Jan-2000 Corrected default multiplier. */static void usbvideo_ReportStatistics(const struct uvd *uvd){ if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { unsigned long allPackets, badPackets, goodPackets, percent; allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; goodPackets = allPackets - badPackets; /* Calculate percentage wisely, remember integer limits */ assert(allPackets != 0); if (goodPackets < (((unsigned long)-1)/100)) percent = (100 * goodPackets) / allPackets; else percent = goodPackets / (allPackets / 100); info("Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%", allPackets, badPackets, percent); if (uvd->iso_packet_len > 0) { unsigned long allBytes, xferBytes; char multiplier = ' '; allBytes = allPackets * uvd->iso_packet_len; xferBytes = uvd->stats.data_count; assert(allBytes != 0); if (xferBytes < (((unsigned long)-1)/100)) percent = (100 * xferBytes) / allBytes; else percent = xferBytes / (allBytes / 100); /* Scale xferBytes for easy reading */ if (xferBytes > 10*1024) { xferBytes /= 1024; multiplier = 'K'; if (xferBytes > 10*1024) { xferBytes /= 1024; multiplier = 'M'; if (xferBytes > 10*1024) { xferBytes /= 1024; multiplier = 'G'; if (xferBytes > 10*1024) { xferBytes /= 1024; multiplier = 'T'; } } } } info("Transfer Statistics: Transferred=%lu%cB Usage=%lu%%", xferBytes, multiplier, percent); } }}/* * usbvideo_TestPattern() * * Procedure forms a test pattern (yellow grid on blue background). * * Parameters: * fullframe: if TRUE then entire frame is filled, otherwise the procedure * continues from the current scanline. * pmode 0: fill the frame with solid blue color (like on VCR or TV) * 1: Draw a colored grid * * History: * 01-Feb-2000 Created. */void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode){ struct usbvideo_frame *frame; int num_cell = 0; int scan_length = 0; static int num_pass = 0; if (uvd == NULL) { err("%s: uvd == NULL", __FUNCTION__); return; } if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { err("%s: uvd->curframe=%d.", __FUNCTION__, uvd->curframe); return; } /* Grab the current frame */ frame = &uvd->frame[uvd->curframe]; /* Optionally start at the beginning */ if (fullframe) { frame->curline = 0; frame->seqRead_Length = 0; }#if 0 { /* For debugging purposes only */ char tmp[20]; usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -