📄 zc0301_core.c
字号:
/*************************************************************************** * Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip * * * * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * Informations about the chip internals needed to enable the I2C protocol * * have been taken from the documentation of the ZC030x Video4Linux1 * * driver written by Andrew Birkett <andy@nobugs.org> * * * * 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. * ***************************************************************************/#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/param.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/fs.h>#include <linux/delay.h>#include <linux/compiler.h>#include <linux/ioctl.h>#include <linux/poll.h>#include <linux/stat.h>#include <linux/mm.h>#include <linux/vmalloc.h>#include <linux/page-flags.h>#include <asm/byteorder.h>#include <asm/page.h>#include <asm/uaccess.h>#include "zc0301.h"/*****************************************************************************/#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301[P] " \ "Image Processor and Control Chip"#define ZC0301_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia"#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"#define ZC0301_MODULE_LICENSE "GPL"#define ZC0301_MODULE_VERSION "1:1.10"#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 10)/*****************************************************************************/MODULE_DEVICE_TABLE(usb, zc0301_id_table);MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL);MODULE_DESCRIPTION(ZC0301_MODULE_NAME);MODULE_VERSION(ZC0301_MODULE_VERSION);MODULE_LICENSE(ZC0301_MODULE_LICENSE);static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1};module_param_array(video_nr, short, NULL, 0444);MODULE_PARM_DESC(video_nr, "\n<-1|n[,...]> Specify V4L2 minor mode number." "\n -1 = use next available (default)" "\n n = use minor number n (integer >= 0)" "\nYou can specify up to " __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." "\nFor example:" "\nvideo_nr=-1,2,-1 would assign minor number 2 to" "\nthe second registered camera and use auto for the first" "\none and for every other camera." "\n");static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = ZC0301_FORCE_MUNMAP};module_param_array(force_munmap, bool, NULL, 0444);MODULE_PARM_DESC(force_munmap, "\n<0|1[,...]> Force the application to unmap previously" "\nmapped buffer memory before calling any VIDIOC_S_CROP or" "\nVIDIOC_S_FMT ioctl's. Not all the applications support" "\nthis feature. This parameter is specific for each" "\ndetected camera." "\n 0 = do not force memory unmapping" "\n 1 = force memory unmapping (save memory)" "\nDefault value is "__MODULE_STRING(ZC0301_FORCE_MUNMAP)"." "\n");static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = ZC0301_FRAME_TIMEOUT};module_param_array(frame_timeout, uint, NULL, 0644);MODULE_PARM_DESC(frame_timeout, "\n<n[,...]> Timeout for a video frame in seconds." "\nThis parameter is specific for each detected camera." "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." "\n");#ifdef ZC0301_DEBUGstatic unsigned short debug = ZC0301_DEBUG_LEVEL;module_param(debug, ushort, 0644);MODULE_PARM_DESC(debug, "\n<n> Debugging information level, from 0 to 3:" "\n0 = none (use carefully)" "\n1 = critical errors" "\n2 = significant informations" "\n3 = more verbose messages" "\nLevel 3 is useful for testing only, when only " "one device is used." "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." "\n");#endif/*****************************************************************************/static u32zc0301_request_buffers(struct zc0301_device* cam, u32 count, enum zc0301_io_method io){ struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? (p->width * p->height * p->priv) / 8 : (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; if (count > ZC0301_MAX_FRAMES) count = ZC0301_MAX_FRAMES; cam->nbuffers = count; while (cam->nbuffers > 0) { if ((buff = vmalloc_32_user(cam->nbuffers * PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } for (i = 0; i < cam->nbuffers; i++) { cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); cam->frame[i].buf.index = i; cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); cam->frame[i].buf.length = imagesize; cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam->frame[i].buf.sequence = 0; cam->frame[i].buf.field = V4L2_FIELD_NONE; cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; cam->frame[i].buf.flags = 0; } return cam->nbuffers;}static void zc0301_release_buffers(struct zc0301_device* cam){ if (cam->nbuffers) { vfree(cam->frame[0].bufmem); cam->nbuffers = 0; } cam->frame_current = NULL;}static void zc0301_empty_framequeues(struct zc0301_device* cam){ u32 i; INIT_LIST_HEAD(&cam->inqueue); INIT_LIST_HEAD(&cam->outqueue); for (i = 0; i < ZC0301_MAX_FRAMES; i++) { cam->frame[i].state = F_UNUSED; cam->frame[i].buf.bytesused = 0; }}static void zc0301_requeue_outqueue(struct zc0301_device* cam){ struct zc0301_frame_t *i; list_for_each_entry(i, &cam->outqueue, frame) { i->state = F_QUEUED; list_add(&i->frame, &cam->inqueue); } INIT_LIST_HEAD(&cam->outqueue);}static void zc0301_queue_unusedframes(struct zc0301_device* cam){ unsigned long lock_flags; u32 i; for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].state == F_UNUSED) { cam->frame[i].state = F_QUEUED; spin_lock_irqsave(&cam->queue_lock, lock_flags); list_add_tail(&cam->frame[i].frame, &cam->inqueue); spin_unlock_irqrestore(&cam->queue_lock, lock_flags); }}/*****************************************************************************/int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value){ struct usb_device* udev = cam->usbdev; int res; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); if (res < 0) { DBG(3, "Failed to write a register (index 0x%04X, " "value 0x%02X, error %d)",index, value, res); return -1; } return 0;}int zc0301_read_reg(struct zc0301_device* cam, u16 index){ struct usb_device* udev = cam->usbdev; u8* buff = cam->control_buffer; int res; res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); if (res < 0) DBG(3, "Failed to read a register (index 0x%04X, error %d)", index, res); PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); return (res >= 0) ? (int)(*buff) : -1;}int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length){ int err = 0, res, r0, r1; err += zc0301_write_reg(cam, 0x0092, address); err += zc0301_write_reg(cam, 0x0090, 0x02); msleep(1); res = zc0301_read_reg(cam, 0x0091); if (res < 0) err += res; r0 = zc0301_read_reg(cam, 0x0095); if (r0 < 0) err += r0; r1 = zc0301_read_reg(cam, 0x0096); if (r1 < 0) err += r1; res = (length <= 1) ? r0 : r0 | (r1 << 8); if (err) DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", address, res); PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); return err ? -1 : res;}int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value){ int err = 0, res; err += zc0301_write_reg(cam, 0x0092, address); err += zc0301_write_reg(cam, 0x0093, value & 0xff); err += zc0301_write_reg(cam, 0x0094, value >> 8); err += zc0301_write_reg(cam, 0x0090, 0x01); msleep(1); res = zc0301_read_reg(cam, 0x0091); if (res < 0) err += res; if (err) DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", address, value); PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); return err ? -1 : 0;}/*****************************************************************************/#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs)#elsestatic void zc0301_urb_complete(struct urb *urb)#endif{ struct zc0301_device* cam = urb->context; struct zc0301_frame_t** f; size_t imagesize; u8 i; int err = 0; if (urb->status == -ENOENT) return; f = &cam->frame_current; if (cam->stream == STREAM_INTERRUPT) { cam->stream = STREAM_OFF; if ((*f)) (*f)->state = F_QUEUED; DBG(3, "Stream interrupted"); wake_up(&cam->wait_stream); } if (cam->state & DEV_DISCONNECTED) return; if (cam->state & DEV_MISCONFIGURED) { wake_up_interruptible(&cam->wait_frame); return; } if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) goto resubmit_urb; if (!(*f)) (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, frame); imagesize = (cam->sensor.pix_format.width * cam->sensor.pix_format.height * cam->sensor.pix_format.priv) / 8; for (i = 0; i < urb->number_of_packets; i++) { unsigned int len, status; void *pos; u16* soi; u8 sof; len = urb->iso_frame_desc[i].actual_length; status = urb->iso_frame_desc[i].status; pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; if (status) { DBG(3, "Error in isochronous frame"); (*f)->state = F_ERROR; continue; } sof = (*(soi = pos) == 0xd8ff); PDBGG("Isochrnous frame: length %u, #%u i,", len, i); if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)start_of_frame: if (sof) { (*f)->state = F_GRABBING; (*f)->buf.bytesused = 0; do_gettimeofday(&(*f)->buf.timestamp); DBG(3, "SOF detected: new video frame"); } if ((*f)->state == F_GRABBING) { if (sof && (*f)->buf.bytesused) goto end_of_frame; if ((*f)->buf.bytesused + len > imagesize) { DBG(3, "Video frame size exceeded"); (*f)->state = F_ERROR; continue; } memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); (*f)->buf.bytesused += len; if ((*f)->buf.bytesused == imagesize) { u32 b;end_of_frame: b = (*f)->buf.bytesused; (*f)->state = F_DONE; (*f)->buf.sequence= ++cam->frame_count; spin_lock(&cam->queue_lock); list_move_tail(&(*f)->frame, &cam->outqueue); if (!list_empty(&cam->inqueue)) (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, frame); else (*f) = NULL; spin_unlock(&cam->queue_lock); DBG(3, "Video frame captured: : %lu bytes", (unsigned long)(b)); if (!(*f)) goto resubmit_urb; if (sof) goto start_of_frame; } } }resubmit_urb: urb->dev = cam->usbdev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0 && err != -EPERM) { cam->state |= DEV_MISCONFIGURED; DBG(1, "usb_submit_urb() failed"); } wake_up_interruptible(&cam->wait_frame);}static int zc0301_start_transfer(struct zc0301_device* cam){ struct usb_device *udev = cam->usbdev; struct usb_host_interface* altsetting = usb_altnum_to_altsetting( usb_ifnum_to_if(udev, 0), ZC0301_ALTERNATE_SETTING); const unsigned int psz = le16_to_cpu(altsetting-> endpoint[0].desc.wMaxPacketSize); struct urb* urb; s8 i, j; int err = 0; for (i = 0; i < ZC0301_URBS; i++) { cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, GFP_KERNEL); if (!cam->transfer_buffer[i]) { err = -ENOMEM; DBG(1, "Not enough memory"); goto free_buffers; } } for (i = 0; i < ZC0301_URBS; i++) { urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); cam->urb[i] = urb; if (!urb) { err = -ENOMEM; DBG(1, "usb_alloc_urb() failed"); goto free_urbs; } urb->dev = udev; urb->context = cam; urb->pipe = usb_rcvisocpipe(udev, 1); urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = ZC0301_ISO_PACKETS; urb->complete = zc0301_urb_complete; urb->transfer_buffer = cam->transfer_buffer[i]; urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; urb->interval = 1; for (j = 0; j < ZC0301_ISO_PACKETS; j++) { urb->iso_frame_desc[j].offset = psz * j; urb->iso_frame_desc[j].length = psz; } } err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); if (err) { DBG(1, "usb_set_interface() failed"); goto free_urbs; } cam->frame_current = NULL; for (i = 0; i < ZC0301_URBS; i++) { err = usb_submit_urb(cam->urb[i], GFP_KERNEL); if (err) { for (j = i-1; j >= 0; j--) usb_kill_urb(cam->urb[j]); DBG(1, "usb_submit_urb() failed, error %d", err); goto free_urbs; } } return 0;free_urbs: for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) usb_free_urb(cam->urb[i]);free_buffers: for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) kfree(cam->transfer_buffer[i]); return err;}static int zc0301_stop_transfer(struct zc0301_device* cam){ struct usb_device *udev = cam->usbdev; s8 i; int err = 0; if (cam->state & DEV_DISCONNECTED) return 0; for (i = ZC0301_URBS-1; i >= 0; i--) { usb_kill_urb(cam->urb[i]); usb_free_urb(cam->urb[i]); kfree(cam->transfer_buffer[i]); } err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ if (err) DBG(3, "usb_set_interface() failed");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -