📄 sqcam.c
字号:
/* * USB SQcam WebCam driver based on the ViCam driver written by Joe Burks, * Christopher L Cheney, Pavel Machek, John Tyner, Monroe Williams. * * Thanks to Theodore Kilgore for his help. * * 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. * * This source code is based heavily on the CPiA webcam driver which was * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt * * Portions of this code were also copied from usbvideo.c * * The bayer decoding functions are copied from libgphoto2 * */#include <linux/kernel.h>#include <linux/wrapper.h>#include <linux/module.h>#include <linux/init.h>#include <linux/videodev.h>#include <linux/usb.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include "usbvideo.h"//#define SQCAM_DEBUG#ifndef MODULE_LICENSE#define MODULE_LICENSE(a)#endif#ifndef bool#define bool int#endif#ifdef SQCAM_DEBUG#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args)#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args)#else#define DBG(fmn,args...) do {} while(0)#endif/* Version Information */#define DRIVER_VERSION "v1.0"#define DRIVER_AUTHOR "Marcell Lengyel, miketkf@mailbox.hu"#define DRIVER_DESC "SQcam WebCam Driver"/* Define these values to match your device */#define USB_SQCAM_VENDOR_ID 0x2770#define USB_SQCAM_PRODUCT_ID 0x9120#define SQCAM_BYTES_PER_PIXEL 3#define SQCAM_MAX_READ_SIZE 0x8000#define SQCAM_MAX_RAW_SIZE 320*240+64#define SQCAM_MAX_FRAME_SIZE (SQCAM_BYTES_PER_PIXEL*320*240)#define SQCAM_FRAMES 2struct sqcam_camera { u8 *raw_image; // raw data captured from the camera u8 *framebuf; // processed data in RGB24 format struct video_device vdev; // v4l video device struct usb_device *udev; // usb device struct semaphore busy_lock; // guard against SMP multithreading bool is_initialized; bool is_removed; bool is_opened; u8 bulkEndpoint; u32 framebuf_size; // # of valid bytes in framebuf u32 framebuf_read_start; // position in frame buf that a read is happening at.};static void *sqcam_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);static void sqcam_disconnect(struct usb_device *dev, void *ptr);static void read_frame(struct sqcam_camera *cam, int framenum);static void sqcam_purge(struct sqcam_camera *cam);static int gp_bayer_decode (unsigned char *input, int w, int h, unsigned char *output, int tile);static intsend_control_msg(int set, struct usb_device *udev, u8 request, u16 value, u16 index, unsigned char *cp, u16 size){ int status; // for reasons not yet known to me, you can't send USB control messages // with data in the module (if you are compiled as a module). Whatever // the reason, copying it to memory allocated as kernel memory then // doing the usb control message fixes the problem. unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); memcpy(transfer_buffer, cp, size); status = usb_control_msg(udev, set ? usb_sndctrlpipe(udev, 0) : usb_rcvctrlpipe(udev, 0), request, (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, transfer_buffer, size, HZ); kfree(transfer_buffer); DBG("ctrl msg:: value: %0x, index: %0x\n", value, index); if (status < 0) { printk(KERN_INFO "Failed sending control message, error %d.\n", status); } return status;}static intinitialize_camera(struct sqcam_camera *cam){ struct usb_device *udev = cam->udev; int status; unsigned char ch[5]; if ((status = send_control_msg(1, udev, 0x0c, 0x06, 0xf0, NULL, 0)) < 0) return status; if ((status = send_control_msg(0, udev, 0x0c, 0x07, 0, &ch[0], 1)) < 0) return status; if ((status = send_control_msg(0, udev, 0x0c, 0x07, 0, &ch[0], 4)) < 0) return status; ch[0] = 0x50; if ((status = send_control_msg(1, udev, 0x0c, 0x06, 0xa0, &ch[0], 1)) < 0) return status; if ((status = send_control_msg(0, udev, 0x0c, 0x07, 0, &ch[0], 1)) < 0) return status; return 0;}static intreset_camera(struct sqcam_camera *cam){ struct usb_device *udev = cam->udev; int status; unsigned char ch; ch = 0xff; if ((status = send_control_msg(1, udev, 0x0c, 0xc0, 0x00, &ch, 1)) < 0) return status; if ((status = send_control_msg(1, udev, 0x0c, 0x06, 0xa0, NULL, 0)) < 0) return status; if ((status = send_control_msg(0, udev, 0x0c, 0x07, 0, &ch, 1)) < 0) return status; return 0;}static intsqcam_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg){ struct sqcam_camera *cam = dev->priv; int retval = 0; if (!cam) return -ENODEV; /* make this _really_ smp-safe */ if (down_interruptible(&cam->busy_lock)) return -EINTR; switch (ioctlnr) { /* query capabilites */ case VIDIOCGCAP: { struct video_capability b; DBG("VIDIOCGCAP\n"); strcpy(b.name, "SQcam-based Camera"); b.type = VID_TYPE_CAPTURE; b.channels = 1; b.audios = 0; b.maxwidth = 320; /* VIDEOSIZE_CIF */ b.maxheight = 240; b.minwidth = 320; /* VIDEOSIZE_160_120 */ b.minheight = 240; if (copy_to_user(arg, &b, sizeof (b))) retval = -EFAULT; break; } /* get/set video source - we are a camera and nothing else */ case VIDIOCGCHAN: { struct video_channel v; DBG("VIDIOCGCHAN\n"); if (copy_from_user(&v, arg, sizeof (v))) { retval = -EFAULT; break; } if (v.channel != 0) { retval = -EINVAL; break; } v.channel = 0; strcpy(v.name, "Camera"); v.tuners = 0; v.flags = 0; v.type = VIDEO_TYPE_CAMERA; v.norm = 0; if (copy_to_user(arg, &v, sizeof (v))) retval = -EFAULT; break; } case VIDIOCSCHAN: { int v; if (copy_from_user(&v, arg, sizeof (v))) retval = -EFAULT; DBG("VIDIOCSCHAN %d\n", v); if (retval == 0 && v != 0) retval = -EINVAL; break; } /* image properties */ case VIDIOCGPICT: { struct video_picture vp; DBG("VIDIOCGPICT\n"); memset(&vp, 0, sizeof (struct video_picture)); vp.depth = 24; vp.palette = VIDEO_PALETTE_RGB24; if (copy_to_user (arg, &vp, sizeof (struct video_picture))) retval = -EFAULT; break; } case VIDIOCSPICT: { struct video_picture vp; if(copy_from_user(&vp, (struct video_picture *) arg, sizeof(struct video_picture))) retval = -EFAULT; else { DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, vp.palette); if (vp.depth != 24 || vp.palette != VIDEO_PALETTE_RGB24) retval = -EINVAL; } break; } /* get/set capture window */ case VIDIOCGWIN: { struct video_window vw; vw.x = 0; vw.y = 0; vw.width = 320; vw.height = 240; vw.chromakey = 0; vw.flags = 0; vw.clips = NULL; vw.clipcount = 0; DBG("VIDIOCGWIN\n"); if (copy_to_user ((void *) arg, (void *) &vw, sizeof (vw))) retval = -EFAULT; // I'm not sure what the deal with a capture window is, it is very poorly described // in the doc. So I won't support it now. break; } case VIDIOCSWIN: { struct video_window *vw = (struct video_window *) arg; DBG("VIDIOCSWIN %d x %d\n", vw->width, vw->height); if ( vw->width != 320 || vw->height != 240 ) retval = -EFAULT; break; } /* mmap interface */ case VIDIOCGMBUF: { struct video_mbuf vm; int i; DBG("VIDIOCGMBUF\n"); memset(&vm, 0, sizeof (vm)); vm.size = SQCAM_MAX_FRAME_SIZE * SQCAM_FRAMES; vm.frames = SQCAM_FRAMES; for (i = 0; i < SQCAM_FRAMES; i++) vm.offsets[i] = SQCAM_MAX_FRAME_SIZE * i; if (copy_to_user ((void *) arg, (void *) &vm, sizeof (vm))) retval = -EFAULT; break; } case VIDIOCMCAPTURE: { struct video_mmap vm; // int video_size; if (copy_from_user ((void *) &vm, (void *) arg, sizeof (vm))) { retval = -EFAULT; break; } DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); if ( vm.frame >= SQCAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) retval = -EINVAL; // in theory right here we'd start the image capturing // (fill in a bulk urb and submit it asynchronously) // // Instead we're going to do a total hack job for now and // retrieve the frame in VIDIOCSYNC break; } case VIDIOCSYNC: { int frame; if (copy_from_user((void *) &frame, arg, sizeof (int))) { retval = -EFAULT; break; } DBG("VIDIOCSYNC: %d\n", frame); read_frame(cam, frame); break; } /* pointless to implement overlay with this camera */ case VIDIOCCAPTURE: case VIDIOCGFBUF: case VIDIOCSFBUF: case VIDIOCKEY: retval = -EINVAL; break; /* tuner interface - we have none */ case VIDIOCGTUNER: case VIDIOCSTUNER: case VIDIOCGFREQ: case VIDIOCSFREQ: retval = -EINVAL; break; /* audio interface - we have none */ case VIDIOCGAUDIO: case VIDIOCSAUDIO: retval = -EINVAL; break; default: retval = -ENOIOCTLCMD; break; } up(&cam->busy_lock); return retval;}static intsqcam_open(struct video_device *dev, int flags){ struct sqcam_camera *cam = (struct sqcam_camera *) dev->priv; int intr; unsigned char siz; int n;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -