📄 ov511.c
字号:
* 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;
}
/* Allocate logically contiguous mmap-compatible reserved pages */
static void *
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 = 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;
}
static void
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 = 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);
}
#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 5) */
/**********************************************************************
* /proc interface
* Based on the CPiA driver version 0.7.4 -claudio
**********************************************************************/
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
static struct proc_dir_entry *ov511_proc_entry = NULL;
extern struct proc_dir_entry *video_proc_entry;
static struct file_operations ov511_control_fops = {
.ioctl = ov51x_control_ioctl,
};
#define YES_NO(x) ((x) ? "yes" : "no")
/* /proc/video/ov511/<minor#>/info */
static int
ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
char *out = page;
int i, len;
struct usb_ov511 *ov = data;
struct video_picture p;
unsigned char exp;
if (!ov || !ov->dev)
return -ENODEV;
sensor_get_picture(ov, &p);
sensor_get_exposure(ov, &exp);
/* IMPORTANT: This output MUST be kept under PAGE_SIZE
* or we need to get more sophisticated. */
out += sprintf(out, "driver_version : %s\n", DRIVER_VERSION);
out += sprintf(out, "custom_id : %d\n", ov->customid);
out += sprintf(out, "model : %s\n", ov->desc);
out += sprintf(out, "streaming : %s\n", YES_NO(ov->streaming));
out += sprintf(out, "grabbing : %s\n", YES_NO(ov->grabbing));
out += sprintf(out, "compress : %s\n", YES_NO(ov->compress));
out += sprintf(out, "subcapture : %s\n", YES_NO(ov->sub_flag));
out += sprintf(out, "sub_size : %d %d %d %d\n",
ov->subx, ov->suby, ov->subw, ov->subh);
#ifdef OV511_ALLOW_CONVERSION
out += sprintf(out, "data_format : %s\n",
force_rgb ? "RGB" : "BGR");
#endif
out += sprintf(out, "brightness : %d\n", p.brightness >> 8);
out += sprintf(out, "colour : %d\n", p.colour >> 8);
out += sprintf(out, "contrast : %d\n", p.contrast >> 8);
out += sprintf(out, "hue : %d\n", p.hue >> 8);
out += sprintf(out, "exposure : %d\n", exp);
out += sprintf(out, "num_frames : %d\n", OV511_NUMFRAMES);
for (i = 0; i < OV511_NUMFRAMES; i++) {
out += sprintf(out, "frame : %d\n", i);
out += sprintf(out, " depth : %d\n",
ov->frame[i].depth);
out += sprintf(out, " size : %d %d\n",
ov->frame[i].width, ov->frame[i].height);
out += sprintf(out, " format : %s\n",
symbolic(v4l1_plist, ov->frame[i].format));
out += sprintf(out, " data_buffer : 0x%p\n",
ov->frame[i].data);
}
out += sprintf(out, "snap_enabled : %s\n", YES_NO(ov->snap_enabled));
out += sprintf(out, "bridge : %s\n",
symbolic(brglist, ov->bridge));
out += sprintf(out, "sensor : %s\n",
symbolic(senlist, ov->sensor));
out += sprintf(out, "packet_size : %d\n", ov->packet_size);
out += sprintf(out, "framebuffer : 0x%p\n", ov->fbuf);
out += sprintf(out, "packet_numbering: %d\n", ov->packet_numbering);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
out += sprintf(out, "topology : %s\n", ov->usb_path);
#else
out += sprintf(out, "usb_bus : %d\n", ov->dev->bus->busnum);
out += sprintf(out, "usb_device : %d\n", ov->dev->devnum);
#endif
len = out - page;
len -= off;
if (len < count) {
*eof = 1;
if (len <= 0)
return 0;
} else
len = count;
*start = page + off;
return len;
}
/* /proc/video/ov511/<minor#>/button
*
* When the camera's button is pressed, the output of this will change from a
* 0 to a 1 (ASCII). It will retain this value until it is read, after which
* it will reset to zero.
*
* SECURITY NOTE: Since reading this file can change the state of the snapshot
* status, it is important for applications that open it to keep it locked
* against access by other processes, using flock() or a similar mechanism. No
* locking is provided by this driver.
*/
static int
ov511_read_proc_button(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
char *out = page;
int len, status;
struct usb_ov511 *ov = data;
if (!ov || !ov->dev)
return -ENODEV;
status = ov51x_check_snapshot(ov);
out += sprintf(out, "%d", status);
if (status)
ov51x_clear_snapshot(ov);
len = out - page;
len -= off;
if (len < count) {
*eof = 1;
if (len <= 0)
return 0;
} else {
len = count;
}
*start = page + off;
return len;
}
static void
create_proc_ov511_cam(struct usb_ov511 *ov)
{
char dirname[10];
if (!ov511_proc_entry || !ov)
return;
/* Create per-device directory */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 10)
snprintf(dirname, 10, "%d", ov->vdev.minor);
#else
sprintf(dirname, "%d", ov->vdev.minor);
#endif
PDEBUG(4, "creating /proc/video/ov511/%s/", dirname);
ov->proc_devdir = create_proc_entry(dirname, S_IFDIR, ov511_proc_entry);
if (!ov->proc_devdir)
return;
ov->proc_devdir->owner = THIS_MODULE;
/* Create "info" entry (human readable device information) */
PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname);
ov->proc_info = create_proc_read_entry("info", S_IFREG|S_IRUGO|S_IWUSR,
ov->proc_devdir, ov511_read_proc_info, ov);
if (!ov->proc_info)
return;
ov->proc_info->owner = THIS_MODULE;
/* Don't create it if old snapshot mode on (would cause race cond.) */
if (!snapshot) {
/* Create "button" entry (snapshot button status) */
PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname);
ov->proc_button = create_proc_read_entry("button",
S_IFREG|S_IRUGO|S_IWUSR, ov->proc_devdir,
ov511_read_proc_button, ov);
if (!ov->proc_button)
return;
ov->proc_button->owner = THIS_MODULE;
}
/* Create "control" entry (ioctl() interface) */
PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname);
lock_kernel();
ov->proc_control = create_proc_entry("control", S_IFREG|S_IRUGO|S_IWUSR,
ov->proc_devdir);
if (!ov->proc_control) {
unlock_kernel();
return;
}
ov->proc_control->owner = THIS_MODULE;
ov->proc_control->data = ov;
ov->proc_control->proc_fops = &ov511_control_fops;
unlock_kernel();
}
static void
destroy_proc_ov511_cam(struct usb_ov511 *ov)
{
char dirname[10];
if (!ov || !ov->proc_devdir)
return;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 10)
snprintf(dirname, 10, "%d", ov->vdev.minor);
#else
sprintf(dirname, "%d", ov->vdev.minor);
#endif
/* Destroy "control" entry */
if (ov->proc_control) {
PDEBUG(4, "destroying /proc/video/ov511/%s/control", dirname);
remove_proc_entry("control", ov->proc_devdir);
ov->proc_control = NULL;
}
/* Destroy "button" entry */
if (ov->proc_button) {
PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname);
remove_proc_entry("button", ov->proc_devdir);
ov->proc_button = NULL;
}
/* Destroy "info" entry */
if (ov->proc_info) {
PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname);
remove_proc_entry("info", ov->proc_devdir);
ov->proc_info = NULL;
}
/* Destroy per-device directory */
PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname);
remove_proc_entry(dirname, ov511_proc_entry);
ov->proc_devdir = NULL;
}
static void
proc_ov511_create(void)
{
/* No current standard here. Alan prefers /proc/video/ as it keeps
* /proc "less cluttered than /proc/randomcardifoundintheshed/"
* -claudio
*/
if (video_proc_entry == NULL) {
err("Error: /proc/video/ does not exist");
return;
}
ov511_proc_entry = create_proc_entry("ov511", S_IFDIR,
video_proc_entry);
if (ov511_proc_entry)
ov511_proc_entry->owner = THIS_MODULE;
else
err("Unable to create /proc/video/ov511");
}
static void
proc_ov511_destroy(void)
{
PDEBUG(3, "removing /proc/video/ov511");
if (ov511_proc_entry == NULL)
return;
remove_proc_entry("ov511", video_proc_entry);
}
#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
/**********************************************************************
*
* Register I/O
*
**********************************************************************/
/* Write an OV51x register */
static int
reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
{
int rc;
PDEBUG(5, "0x%02X:0x%02X", reg, value);
down(&ov->cbuf_lock);
ov->cbuf[0] = value;
rc = usb_control_msg(ov->dev,
usb_sndctrlpipe(ov->dev, 0),
(ov->bclass == BCL_OV518)?1:2 /* REG_IO */,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, &ov->cbuf[0], 1, HZ);
up(&ov->cbuf_lock);
if (rc < 0)
err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc));
return rc;
}
/* Read from an OV51x register */
/* returns: negative is error, pos or zero is data */
static int
reg_r(struct usb_ov511 *ov, unsigned char reg)
{
int rc;
down(&ov->cbuf_lock);
rc = usb_control_msg(ov->dev,
usb_rcvctrlpipe(ov->dev, 0),
(ov->bclass == BCL_OV518)?1:3 /* REG_IO */,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, &ov->cbuf[0], 1, HZ);
if (rc < 0) {
err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc));
} else {
rc = ov->cbuf[0];
PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]);
}
up(&ov->cbuf_lock);
return rc;
}
/*
* Writes bits at positions specified by mask to an OV51x reg. Bits that are in
* the same position as 1's in "mask" are cleared and set to "value". Bits
* that are in the same position as 0's in "mask" are preserved, regardless
* of their respective state in "value".
*/
static int
reg_w_mask(struct usb_ov511 *ov,
unsigned char reg,
unsigned char value,
unsigned char mask)
{
int ret;
unsigned char oldval, newval;
ret = reg_r(ov, reg);
if (ret < 0)
return ret;
oldval = (unsigned char) ret;
oldval &= (~mask); /* Clear the masked bits */
value &= mask; /* Enforce mask on value */
newval = oldval | value; /* Set the desired bits */
return (reg_w(ov, reg, newval));
}
/*
* Writes multiple (n) byte value to a single register. Only valid with certain
* registers (0x30 and 0xc4 - 0xce).
*/
static int
ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n)
{
int rc;
PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n);
down(&ov->cbuf_lock);
*((u32 *)ov->cbuf) = __cpu_to_le32(val);
rc = usb_control_msg(ov->dev,
usb_sndctrlpipe(ov->dev, 0),
1 /* REG_IO */,
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, ov->cbuf, n, HZ);
up(&ov->cbuf_lock);
if (rc < 0)
err("reg write multiple: error %d: %s", rc,
symbolic(urb_errlist, rc));
return rc;
}
static int
ov511_upload_quan_tables(struct usb_ov511 *ov)
{
unsigned char *pYTable = yQuanTable511;
unsigned char *pUVTable = uvQuanTable511;
unsigned char val0, val1;
int i, rc, reg = R511_COMP_LUT_BEGIN;
PDEBUG(4, "Uploading quantization tables");
for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) {
if (ENABLE_Y_QUANTABLE) {
val0 = *pYTable++;
val1 = *pYTable++;
val0 &= 0x0f;
val1 &= 0x0f;
val0 |= val1 << 4;
rc = reg_w(ov, reg, val0);
if (rc < 0)
return rc;
}
if (ENABLE_UV_QUANTABLE) {
val0 = *pUVTable++;
val1 = *pUVTable++;
val0 &= 0x0f;
val1 &= 0x0f;
val0 |= val1 << 4;
rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0);
if (rc < 0)
return rc;
}
reg++;
}
return 0;
}
/* OV518 quantization tables are 8x4 (instead of 8x8) */
static int
ov518_upload_quan_tables(struct usb_ov511 *ov)
{
unsigned char *pYTable = yQuanTable518;
unsigned char *pUVTable = uvQuanTable518;
unsigned char val0, val1;
int i, rc, reg = R511_COMP_LUT_BEGIN;
PDEBUG(4, "Uploading quantization tables");
for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) {
if (ENABLE_Y_QUANTABLE) {
val0 = *pYTable++;
val1 = *pYTable++;
val0 &= 0x0f;
val1 &= 0x0f;
val0 |= val1 << 4;
rc = reg_w(ov, reg, val0);
if (rc < 0)
return rc;
}
if (ENABLE_UV_QUANTABLE) {
val0 = *pUVTable++;
val1 = *pUVTable++;
val0 &= 0x0f;
val1 &= 0x0f;
val0 |= val1 << 4;
rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0);
if (rc < 0)
return rc;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -