📄 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>#define __NO_VERSION__ /* Temporary: usbvideo is not a module yet */#include <linux/module.h>#include <linux/mm.h>#include <linux/smp_lock.h>#include <linux/vmalloc.h>#include <linux/wrapper.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_PARM(video_nr, "i");/* * Local prototypes. */#if USES_PROC_FSstatic void usbvideo_procfs_level1_create(usbvideo_t *ut);static void usbvideo_procfs_level1_destroy(usbvideo_t *ut);static void usbvideo_procfs_level2_create(uvd_t *uvd);static void usbvideo_procfs_level2_destroy(uvd_t *uvd);static int usbvideo_default_procfs_read_proc( char *page, char **start, off_t off, int count, int *eof, void *data);static int usbvideo_default_procfs_write_proc( struct file *file, const char *buffer, unsigned long count, void *data);#endif/*******************************//* Memory management functions *//*******************************/#define MDEBUG(x) do { } while(0) /* Debug memory management *//* Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. */unsigned long usbvideo_uvirt_to_kva(pgd_t *pgd, unsigned long adr){ unsigned long ret = 0UL; pmd_t *pmd; pte_t *ptep, pte; if (!pgd_none(*pgd)) { pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); pte = *ptep; if (pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE-1)); } } } MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); return ret;}/* * Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. */unsigned long usbvideo_kvirt_to_pa(unsigned long adr){ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = usbvideo_uvirt_to_kva(pgd_offset_k(va), va); ret = __pa(kva); MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); return ret;}void *usbvideo_rvmalloc(unsigned long size){ void *mem; unsigned long adr, page; /* Round it off to PAGE_SIZE */ size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); 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) { page = usbvideo_kvirt_to_pa(adr); mem_map_reserve(virt_to_page(__va(page))); adr += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } return mem;}void usbvideo_rvfree(void *mem, unsigned long size){ unsigned long adr, page; if (!mem) return; size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); adr=(unsigned long) mem; while (size > 0) { page = usbvideo_kvirt_to_pa(adr); mem_map_unreserve(virt_to_page(__va(page))); adr += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } vfree(mem);}void RingQueue_Initialize(RingQueue_t *rq){ assert(rq != NULL); init_waitqueue_head(&rq->wqh);}void RingQueue_Allocate(RingQueue_t *rq, int rqLen){ assert(rq != NULL); assert(rqLen > 0); rq->length = rqLen; rq->queue = usbvideo_rvmalloc(rq->length); assert(rq->queue != NULL);}int RingQueue_IsAllocated(const RingQueue_t *rq){ if (rq == NULL) return 0; return (rq->queue != NULL) && (rq->length > 0);}void RingQueue_Free(RingQueue_t *rq){ assert(rq != NULL); if (RingQueue_IsAllocated(rq)) { usbvideo_rvfree(rq->queue, rq->length); rq->queue = NULL; rq->length = 0; }}int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len){ int i; assert(rq != NULL); assert(dst != NULL); for (i=0; i < len; i++) { dst[i] = rq->queue[rq->ri]; RING_QUEUE_DEQUEUE_BYTES(rq,1); } return len;}int RingQueue_Enqueue(RingQueue_t *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; memmove(rq->queue + rq->wi, cdata, m); RING_QUEUE_ADVANCE_INDEX(rq, wi, m); cdata += m; enqueued += m; n -= m; } return enqueued;}int RingQueue_GetLength(const RingQueue_t *rq){ int ri, wi; assert(rq != NULL); ri = rq->ri; wi = rq->wi; if (ri == wi) return 0; else if (ri < wi) return wi - ri; else return wi + (rq->length - ri);}void RingQueue_InterruptibleSleepOn(RingQueue_t *rq){ assert(rq != NULL); interruptible_sleep_on(&rq->wqh);}void RingQueue_WakeUpInterruptible(RingQueue_t *rq){ assert(rq != NULL); if (waitqueue_active(&rq->wqh)) wake_up_interruptible(&rq->wqh);}/* * usbvideo_VideosizeToString() * * This procedure converts given videosize value to readable string. * * History: * 07-Aug-2000 Created. * 19-Oct-2000 Reworked for usbvideo module. */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. */void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *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. */void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *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. */void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *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; } 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. */void usbvideo_ReportStatistics(const uvd_t *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -