⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ov511.c

📁 0v511摄像头linux最新驱动源码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * 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 + -