📄 vicam.c
字号:
/* -*- linux-c -*- * USB ViCAM driver * * Copyright (c) 2001 Christopher L Cheney (ccheney@cheney.cx) * Copyright (c) 2001 Pavel Machek (pavel@suse.cz) sponsored by SuSE * * 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 of * the License, or (at your option) any later version. * * This driver is for the Vista Imaging ViCAM and 3Com HomeConnect USB * * Thanks to Greg Kroah-Hartman for the USB Skeleton driver * * TODO: * - find out the ids for the Vista Imaging ViCAM * * History: * * 2001_07_07 - 0.1 - christopher: first version * 2001_08_28 - 0.2 - pavel: messed it up, but for some fun, try while true; do dd if=/dev/video of=/dev/fb0 bs=$[0x1e480] count=1 2> /dev/null; done yep, moving pictures. * 2001_08_29 - 0.3 - pavel: played a little bit more. Experimental mmap support. For some fun, get gqcam-0.9, compile it and run. Better than dd ;-). * 2001_08_29 - 0.4 - pavel: added shutter speed control (not much functional) kill update_params if it does not seem to work for you. * 2001_08_30 - 0.5 - pavel: fixed stupid bug with update_params & vicam_bulk * * FIXME: It crashes on rmmod with camera plugged. */#define DEBUG 1#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/list.h>#include <linux/smp_lock.h>#include <linux/devfs_fs_kernel.h>#include <linux/usb.h>#include <asm/io.h>#include <linux/wrapper.h>#include <linux/vmalloc.h>#include <linux/videodev.h>#include "vicam.h"#include "vicamurbs.h"/* Version Information */#define DRIVER_VERSION "v0"#define DRIVER_AUTHOR "Christopher L Cheney <ccheney@cheney.cx>, Pavel Machek <pavel@suse.cz>"#define DRIVER_DESC "USB ViCAM Driver"/* Define these values to match your device */#define USB_VICAM_VENDOR_ID 0x04C1#define USB_VICAM_PRODUCT_ID 0x009D/* table of devices that work with this driver */static struct usb_device_id vicam_table [] = { { USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, vicam_table);static int video_nr = -1; /* next avail video device */static struct usb_driver vicam_driver;static char *buf, *buf2;static int change_pending = 0; static int vicam_parameters(struct usb_vicam *vicam);/****************************************************************************** * * Memory management functions * * Taken from bttv-drivers.c 2.4.7-pre3 * ******************************************************************************//* [DaveM] I've recoded most of this so that: * 1) It's easier to tell what is happening * 2) It's more portable, especially for translating things * out of vmalloc mapped areas in the kernel. * 3) Less unnecessary translations happen. * * The code used to assume that the kernel vmalloc mappings * existed in the page tables of every process, this is simply * not guarenteed. We now use pgd_offset_k which is the * defined way to get at the kernel page tables. *//* Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. */static inline unsigned long 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)); } } } return ret;}static inline unsigned long uvirt_to_bus(unsigned long adr){ unsigned long kva, ret; kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); ret = virt_to_bus((void *)kva); return ret;}static inline unsigned long kvirt_to_bus(unsigned long adr){ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = virt_to_bus((void *)kva); 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. */static inline unsigned long kvirt_to_pa(unsigned long adr){ unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = __pa(kva); return ret;}static void * rvmalloc(signed long size){ void * mem; unsigned long adr, page; mem=vmalloc_32(size); if (mem) { memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr=(unsigned long) mem; while (size > 0) { page = kvirt_to_pa(adr); mem_map_reserve(virt_to_page(__va(page))); adr+=PAGE_SIZE; size-=PAGE_SIZE; } } return mem;}static void rvfree(void * mem, signed long size){ unsigned long adr, page; if (mem) { adr=(unsigned long) mem; while (size > 0) { page = kvirt_to_pa(adr); mem_map_unreserve(virt_to_page(__va(page))); adr+=PAGE_SIZE; size-=PAGE_SIZE; } vfree(mem); }}/****************************************************************************** * * Foo Bar * ******************************************************************************//** * usb_vicam_debug_data */static inline void usb_vicam_debug_data (const char *function, int size, const unsigned char *data){ int i; if (!debug) return; printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); for (i = 0; i < size; ++i) { printk ("%.2x ", data[i]); } printk ("\n");}/***************************************************************************** * * Send command to vicam * *****************************************************************************/static int vicam_sndctrl(int set, struct usb_vicam *vicam, unsigned short req, unsigned short value, unsigned char *cp, int size){ int ret; unsigned char *transfer_buffer = kmalloc (size, GFP_KERNEL); /* Needs to return data I think, works for sending though */ memcpy(transfer_buffer, cp, size); ret = usb_control_msg ( vicam->udev, set ? usb_sndctrlpipe(vicam->udev, 0) : usb_rcvctrlpipe(vicam->udev, 0), req, (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, 0, transfer_buffer, size, HZ); kfree(transfer_buffer); if (ret) printk("vicam: error: %d\n", ret); mdelay(100); return ret;}/***************************************************************************** * * Video4Linux Helpers * *****************************************************************************/static int vicam_get_capability(struct usb_vicam *vicam, struct video_capability *b){ dbg("vicam_get_capability"); strcpy(b->name, vicam->camera_name); b->type = VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME; b->channels = 1; b->audios = 0; b->maxwidth = vicam->width[vicam->sizes-1]; b->maxheight = vicam->height[vicam->sizes-1]; b->minwidth = vicam->width[0]; b->minheight = vicam->height[0]; return 0;} static int vicam_get_channel(struct usb_vicam *vicam, struct video_channel *v){ dbg("vicam_get_channel"); if (v->channel != 0) return -EINVAL; v->flags = 0; v->tuners = 0; v->type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Camera"); return 0;} static int vicam_set_channel(struct usb_vicam *vicam, struct video_channel *v){ dbg("vicam_set_channel"); if (v->channel != 0) return -EINVAL; return 0;} static int vicam_get_mmapbuffer(struct usb_vicam *vicam, struct video_mbuf *vm){ int i; dbg("vicam_get_mmapbuffer"); memset(vm, 0, sizeof(vm)); vm->size = VICAM_NUMFRAMES * vicam->maxframesize; vm->frames = VICAM_NUMFRAMES; for (i=0; i<VICAM_NUMFRAMES; i++) vm->offsets[i] = vicam->maxframesize * i; return 0;}static int vicam_get_picture(struct usb_vicam *vicam, struct video_picture *p){ dbg("vicam_get_picture"); /* This is probably where that weird 0x56 call goes */ p->brightness = vicam->win.brightness; p->hue = vicam->win.hue; p->colour = vicam->win.colour; p->contrast = vicam->win.contrast; p->whiteness = vicam->win.whiteness; p->depth = vicam->win.depth; p->palette = vicam->win.palette; return 0;}static void synchronize(struct usb_vicam *vicam){ change_pending = 1; interruptible_sleep_on(&vicam->wait); vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); mdelay(10); vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); mdelay(10);}static void params_changed(struct usb_vicam *vicam){#if 1 synchronize(vicam); mdelay(10); vicam_parameters(vicam); printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb));#endif}static int vicam_set_picture(struct usb_vicam *vicam, struct video_picture *p){ int changed = 0; info("vicam_set_picture (%d)", p->brightness);#define SET(x) \ if (vicam->win.x != p->x) \ vicam->win.x = p->x, changed = 1; SET(brightness); SET(hue); SET(colour); SET(contrast); SET(whiteness); SET(depth); SET(palette); if (changed) params_changed(vicam); return 0; /* Investigate what should be done maybe 0x56 type call */ if (p->depth != 8) return 1; if (p->palette != VIDEO_PALETTE_GREY) return 1; return 0;}/* FIXME - vicam_sync_frame - important */static int vicam_sync_frame(struct usb_vicam *vicam, int frame){ dbg("vicam_sync_frame"); if(frame <0 || frame >= VICAM_NUMFRAMES) return -EINVAL; /* Probably need to handle various cases *//* ret=vicam_newframe(vicam, frame); vicam->frame[frame].grabstate=FRAME_UNUSED;*/ return 0;} static int vicam_get_window(struct usb_vicam *vicam, struct video_window *vw){ dbg("vicam_get_window"); vw->x = 0; vw->y = 0; vw->chromakey = 0; vw->flags = 0; vw->clipcount = 0; vw->width = vicam->win.width; vw->height = vicam->win.height; return 0;}static int vicam_set_window(struct usb_vicam *vicam, struct video_window *vw){ info("vicam_set_window"); if (vw->flags) return -EINVAL; if (vw->clipcount) return -EINVAL; if (vicam->win.width == vw->width && vicam->win.height == vw->height) return 0; /* Pick largest mode that is smaller than specified res */ /* If specified res is too small reject */ /* Add urb send to device... */ vicam->win.width = vw->width; vicam->win.height = vw->height; params_changed(vicam); return 0;}/* FIXME - vicam_mmap_capture - important */static int vicam_mmap_capture(struct usb_vicam *vicam, struct video_mmap *vm){ dbg("vicam_mmap_capture"); /* usbvideo.c looks good for using here */ /* if (vm->frame >= VICAM_NUMFRAMES) return -EINVAL; if (vicam->frame[vm->frame].grabstate != FRAME_UNUSED) return -EBUSY; vicam->frame[vm->frame].grabstate=FRAME_READY; */ /* No need to vicam_set_window here according to Alan */ /* if (!vicam->streaming) vicam_start_stream(vicam); */ /* set frame as ready */ return 0;}/***************************************************************************** * * Video4Linux * *****************************************************************************/static int vicam_v4l_open(struct video_device *vdev, int flags){ struct usb_vicam *vicam = (struct usb_vicam *)vdev; int err = 0; dbg("vicam_v4l_open"); MOD_INC_USE_COUNT; down(&vicam->sem); if (vicam->open_count) /* Maybe not needed? */ err = -EBUSY; else { vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES); if (!vicam->fbuf) err=-ENOMEM; else { vicam->open_count = 1; }#ifdef BLINKING vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); info ("led on"); vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);#endif } up(&vicam->sem); if (err) MOD_DEC_USE_COUNT; return err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -