📄 stv680.c
字号:
/* * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) * * Thanks to STMicroelectronics for information on the usb commands, and * to Steve Miller at STM for his help and encouragement while I was * writing this driver. * * This driver is based heavily on the * Endpoints (formerly known as AOX) se401 USB Camera Driver * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) * * Still somewhat based on the Linux ov511 driver. * * 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 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. * * History: * ver 0.1 October, 2001. Initial attempt. * * ver 0.2 November, 2001. Fixed asbility to resize, added brightness * function, made more stable (?) * * ver 0.21 Nov, 2001. Added gamma correction and white balance, * due to Alexander Schwartz. Still trying to * improve stablility. Moved stuff into stv680.h * * ver 0.22 Nov, 2001. Added sharpen function (by Michael Sweet, * mike@easysw.com) from GIMP, also used in pencam. * Simple, fast, good integer math routine. * * ver 0.23 Dec, 2001 (gkh) * Took out sharpen function, ran code through * Lindent, and did other minor tweaks to get * things to work properly with 2.5.1 * * ver 0.24 Jan, 2002 (kjs) * Fixed the problem with webcam crashing after * two pictures. Changed the way pic is halved to * improve quality. Got rid of green line around * frame. Fix brightness reset when changing size * bug. Adjusted gamma filters slightly. * * ver 0.25 Jan, 2002 (kjs) * Fixed a bug in which the driver sometimes attempted * to set to a non-supported size. This allowed * gnomemeeting to work. * Fixed proc entry removal bug. */#include <linux/config.h>#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/pagemap.h>#include <linux/wrapper.h>#include <linux/smp_lock.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/videodev.h>#include <linux/usb.h>#include "stv680.h"static int video_nr = -1;static int swapRGB = 0; /* default for auto sleect */static int swapRGB_on = 0; /* default to allow auto select; -1=swap never, +1= swap always */static unsigned int debug = 0;#define PDEBUG(level, fmt, args...) \ do { \ if (debug >= level) \ info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args); \ } while (0)/* * Version Information */#define DRIVER_VERSION "v0.25"#define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>"#define DRIVER_DESC "STV0680 USB Camera Driver"MODULE_AUTHOR (DRIVER_AUTHOR);MODULE_DESCRIPTION (DRIVER_DESC);MODULE_LICENSE ("GPL");MODULE_PARM (debug, "i");MODULE_PARM_DESC (debug, "Debug enabled or not");MODULE_PARM (swapRGB_on, "i");MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never");MODULE_PARM (video_nr, "i");EXPORT_NO_SYMBOLS;/******************************************************************** * * Memory management * ********************************************************************//* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */static inline unsigned long kvirt_to_pa (unsigned long adr){ unsigned long kva, ret; kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret;}static void *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) { mem_map_reserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } return mem;}static void rvfree (void *mem, unsigned long size){ unsigned long adr; if (!mem) return; adr = (unsigned long) mem; while ((long) size > 0) { mem_map_unreserve(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree (mem);}/********************************************************************* * pencam read/write functions ********************************************************************/static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size){ int ret = -1; switch (set) { case 0: /* 0xc1 */ ret = usb_control_msg (stv680->udev, usb_rcvctrlpipe (stv680->udev, 0), req, (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), value, 0, buffer, size, PENCAM_TIMEOUT); break; case 1: /* 0x41 */ ret = usb_control_msg (stv680->udev, usb_sndctrlpipe (stv680->udev, 0), req, (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), value, 0, buffer, size, PENCAM_TIMEOUT); break; case 2: /* 0x80 */ ret = usb_control_msg (stv680->udev, usb_rcvctrlpipe (stv680->udev, 0), req, (USB_DIR_IN | USB_RECIP_DEVICE), value, 0, buffer, size, PENCAM_TIMEOUT); break; case 3: /* 0x40 */ ret = usb_control_msg (stv680->udev, usb_sndctrlpipe (stv680->udev, 0), req, (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), value, 0, buffer, size, PENCAM_TIMEOUT); break; } if ((ret < 0) && (req != 0x0a)) { PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret); } return ret;}static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate){ if (usb_set_configuration (dev->udev, configuration) < 0) { PDEBUG (1, "STV(e): FAILED to set configuration %i", configuration); return -1; } if (usb_set_interface (dev->udev, interface, alternate) < 0) { PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate); return -1; } return 0;}static int stv_stop_video (struct usb_stv *dev){ int i; unsigned char *buf; buf = kmalloc (40, GFP_KERNEL); if (buf == NULL) { PDEBUG (0, "STV(e): Out of (small buf) memory"); return -1; } /* this is a high priority command; it stops all lower order commands */ if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) { i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02); /* Get Last Error; 2 = busy */ PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buf[0], buf[1]); } else { PDEBUG (1, "STV(i): Camera reset to idle mode."); } if ((i = stv_set_config (dev, 1, 0, 0)) < 0) PDEBUG (1, "STV(e): Reset config during exit failed"); /* get current mode */ buf[0] = 0xf0; if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08) /* get mode */ PDEBUG (0, "STV(e): Stop_video: problem setting original mode"); if (dev->origMode != buf[0]) { memset (buf, 0, 8); buf[0] = (unsigned char) dev->origMode; if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) { PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed"); i = -1; } buf[0] = 0xf0; i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08); if ((i != 0x08) || (buf[0] != dev->origMode)) { PDEBUG (0, "STV(e): camera NOT set to original resolution."); i = -1; } else PDEBUG (0, "STV(i): Camera set to original resolution"); } /* origMode */ kfree (buf); return i;}static int stv_set_video_mode (struct usb_stv *dev){ int i, stop_video = 1; unsigned char *buf; buf = kmalloc (40, GFP_KERNEL); if (buf == NULL) { PDEBUG (0, "STV(e): Out of (small buf) memory"); return -1; } if ((i = stv_set_config (dev, 1, 0, 0)) < 0) { kfree (buf); return i; } i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12); if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) { PDEBUG (1, "STV(e): Could not get descriptor 0100."); goto error; } /* set alternate interface 1 */ if ((i = stv_set_config (dev, 1, 0, 1)) < 0) goto error; if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10) goto error; PDEBUG (1, "STV(i): Setting video mode."); /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */ if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) { stop_video = 0; goto error; } goto exit;error: kfree (buf); if (stop_video == 1) stv_stop_video (dev); return -1;exit: kfree (buf); return 0;}static int stv_init (struct usb_stv *stv680){ int i = 0; unsigned char *buffer; unsigned long int bufsize; buffer = kmalloc (40, GFP_KERNEL); if (buffer == NULL) { PDEBUG (0, "STV(e): Out of (small buf) memory"); return -1; } memset (buffer, 0, 40); udelay (100); /* set config 1, interface 0, alternate 0 */ if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) { kfree (buffer); PDEBUG (0, "STV(e): set config 1,0,0 failed"); return -1; } /* ping camera to be sure STV0680 is present */ if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02) goto error; if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) { PDEBUG (1, "STV(e): camera ping failed!!"); goto error; } /* get camera descriptor */ if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09) goto error; i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22); if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) { PDEBUG (1, "STV(e): Could not get descriptor 0200."); goto error; } if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02) goto error; if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24) goto error; if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) goto error; stv680->SupportedModes = buffer[7]; i = stv680->SupportedModes; stv680->CIF = 0; stv680->VGA = 0; stv680->QVGA = 0; if (i & 1) stv680->CIF = 1; if (i & 2) stv680->VGA = 1; if (i & 8) stv680->QVGA = 1; if (stv680->SupportedModes == 0) { PDEBUG (0, "STV(e): There are NO supported STV680 modes!!"); i = -1; goto error; } else { if (stv680->CIF) PDEBUG (0, "STV(i): CIF is supported"); if (stv680->QVGA) PDEBUG (0, "STV(i): QVGA is supported"); } /* FW rev, ASIC rev, sensor ID */ PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]); PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]); PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4)); /* set alternate interface 1 */ if ((i = stv_set_config (stv680, 1, 0, 1)) < 0) goto error; if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) goto error; if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08) goto error; i = buffer[3]; PDEBUG (0, "STV(i): Camera has %i pictures.", i); /* get current mode */ if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08) goto error; stv680->origMode = buffer[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ /* This will attemp CIF mode, if supported. If not, set to QVGA */ memset (buffer, 0, 8); if (stv680->CIF) buffer[0] = 0x00; else if (stv680->QVGA) buffer[0] = 0x03; if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) { PDEBUG (0, "STV(i): Set_Camera_Mode failed"); i = -1; goto error; } buffer[0] = 0xf0; stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08); if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) { PDEBUG (0, "STV(e): Error setting camera video mode!"); i = -1; goto error; } else { if (buffer[0] == 0) { stv680->VideoMode = 0x0000; PDEBUG (0, "STV(i): Video Mode set to CIF"); } if (buffer[0] == 0x03) { stv680->VideoMode = 0x0300; PDEBUG (0, "STV(i): Video Mode set to QVGA"); } } if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10) goto error; bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]); stv680->cwidth = (buffer[4] << 8) | (buffer[5]); /* ->camera = 322, 356, 644 */ stv680->cheight = (buffer[6] << 8) | (buffer[7]); /* ->camera = 242, 292, 484 */ stv680->origGain = buffer[12]; goto exit;error: i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02); /* Get Last Error */ PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buffer[0], buffer[1]); kfree (buffer); return -1;exit: kfree (buffer); /* video = 320x240, 352x288 */ if (stv680->CIF == 1) { stv680->maxwidth = 352; stv680->maxheight = 288; stv680->vwidth = 352; stv680->vheight = 288; } if (stv680->QVGA == 1) { stv680->maxwidth = 320; stv680->maxheight = 240; stv680->vwidth = 320; stv680->vheight = 240; } stv680->rawbufsize = bufsize; /* must be ./. by 8 */ stv680->maxframesize = bufsize * 3; /* RGB size */ PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight); PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize); /* some default values */ stv680->bulk_in_endpointAddr = 0x82; stv680->dropped = 0; stv680->error = 0; stv680->framecount = 0; stv680->readcount = 0; stv680->streaming = 0; /* bright, white, colour, hue, contrast are set by software, not in stv0680 */ stv680->brightness = 32767; stv680->chgbright = 0; stv680->whiteness = 0; /* only for greyscale */ stv680->colour = 32767; stv680->contrast = 32767; stv680->hue = 32767; stv680->palette = STV_VIDEO_PALETTE; stv680->depth = 24; /* rgb24 bits */ swapRGB = 0; if ((swapRGB_on == 0) && (swapRGB == 0)) PDEBUG (1, "STV(i): swapRGB is (auto) OFF"); else if ((swapRGB_on == 1) && (swapRGB == 1)) PDEBUG (1, "STV(i): swapRGB is (auto) ON"); else if (swapRGB_on == 1) PDEBUG (1, "STV(i): swapRGB is (forced) ON"); else if (swapRGB_on == -1) PDEBUG (1, "STV(i): swapRGB is (forced) OFF"); if (stv_set_video_mode (stv680) < 0) { PDEBUG (0, "STV(e): Could not set video mode in stv_init"); return -1; } return 0;}/***************** last of pencam routines *******************//******************************************************************** * /proc interface *******************************************************************/#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)static struct proc_dir_entry *stv680_proc_entry = NULL;extern struct proc_dir_entry *video_proc_entry;#define YES_NO(x) ((x) ? "yes" : "no")#define ON_OFF(x) ((x) ? "(auto) on" : "(auto) off")static int stv680_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data){ char *out = page; int len; struct usb_stv *stv680 = data; /* Stay under PAGE_SIZE or else bla bla bla.... */ out += sprintf (out, "driver_version : %s\n", DRIVER_VERSION); out += sprintf (out, "model : %s\n", stv680->camera_name); out += sprintf (out, "in use : %s\n", YES_NO (stv680->user)); out += sprintf (out, "streaming : %s\n", YES_NO (stv680->streaming)); out += sprintf (out, "num_frames : %d\n", STV680_NUMFRAMES); out += sprintf (out, "Current size : %ix%i\n", stv680->vwidth, stv680->vheight); if (swapRGB_on == 0) out += sprintf (out, "swapRGB : %s\n", ON_OFF (swapRGB)); else if (swapRGB_on == 1) out += sprintf (out, "swapRGB : (forced) on\n"); else if (swapRGB_on == -1) out += sprintf (out, "swapRGB : (forced) off\n"); out += sprintf (out, "Palette : %i", stv680->palette); out += sprintf (out, "\n"); out += sprintf (out, "Frames total : %d\n", stv680->readcount); out += sprintf (out, "Frames read : %d\n", stv680->framecount); out += sprintf (out, "Packets dropped : %d\n", stv680->dropped); out += sprintf (out, "Decoding Errors : %d\n", stv680->error);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -