📄 zc0301_driver.c
字号:
/* This file in the main file of this driver. * * This driver is suite for zc0301 control IC and pb0330 sensor IC. * * The device uses USB1.1 to transfer datas, so speed is not so fast. * * I bought my camera at 2005.01.xx, it cost me 110 RMB. * * It's created by WE.XCC at 2008.05.03
*
* Author's E-mail:84318391@163.com */#include <linux/kernel.h> /*printk and other*/#include <linux/module.h> /*module_init(exit)*/#include <linux/types.h> /*u8 u16 u32 etc*/#include <linux/usb.h> /*should be contained if the device is an usb device*/#include <linux/fs.h> /*struct file_operations*/#include "zc0301_struct.h"#include "zc0301_debug.h"#include "zc0301_descriptors.h"#include "zc0301_pb0330.h"#include "zc0301_urb.h"#include "zc0301_v4l2.h"static const struct usb_device_id zc0301_id_table[] = { { USB_DEVICE(0x0ac8, 0x301b) }, /* PB-0330/HV7131 */ { }};int zc0301_init_pb0330_and_0301(struct zc0301_device *camm){ struct zc0301_device *cam = NULL; int err = 0; if (camm == NULL) { debug_error("zc0301_init_pb0330_and_0301:camm is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); cam = camm; err = zc0301_pb0330_init(cam); if (err) { debug_error("zc0301_init_pb0330_and_0301:pb0330 init failed\n"); return err; } debug_info("zc0301_init_pb0330_and_0301:pb0330 init successed\n"); err = zc0301_set_compression(cam); if (err) { debug_error("zc0301_init_pb0330_and_0301:set compression failed\n"); return err; } debug_info("zc0301_init_pb0330_and_0301:set compression successed\n"); init_waitqueue_head(&cam->wait_frame); spin_lock_init(&cam->queue_lock); mutex_init(&cam->fop_mutex); cam->d_state |= INITED; cam->users = 0; return 0;}int zc0301_mmap(struct file *filp, struct vm_area_struct *vma){ struct zc0301_device *cam = NULL; unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos = NULL; u32 i = 0; if ((filp == NULL) || (vma == NULL)) { debug_error("zc0301_mmap:filp or vma is null\n"); return -EIO; } //debug_param("%s\n", __FUNCTION__); cam = filp->private_data; if (mutex_lock_interruptible(&cam->fop_mutex)) /*I don't know why to use interruptible?*/ return -ERESTARTSYS; if ((cam->d_state & DISCONNECTED) || (cam->d_state & MISCONFIGED)) { debug_error("zc0301_mmap:state is wrong\n"); mutex_unlock(&cam->fop_mutex); return cam->d_state & DISCONNECTED ? -ENODEV : -EIO; } if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_READ) || size != 307200) { debug_error("zc0301_mmap:flag or size is wrong, failed\n"); mutex_unlock(&cam->fop_mutex); return -EINVAL; } for (i = 0; i < cam->nbuffers; i++) { if ((cam->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) break; } if (i >= cam->nbuffers) { debug_error("zc0301_mmap:i >= nbuffers, failed\n"); mutex_unlock(&cam->fop_mutex); return -EINVAL; } vma->vm_flags |= VM_IO; vma->vm_flags |= VM_RESERVED; pos = cam->frame[i].bufmem; while (size > 0) { if (vm_insert_page(vma, start, vmalloc_to_page(pos))) /*I don't know here :(*/ { debug_error("zc0301_mmap:vm_insert_page failed\n"); return -EAGAIN; } pos += PAGE_SIZE; start += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_private_data = &cam->frame[i]; mutex_unlock(&cam->fop_mutex); return 0;}int zc0301_open(struct inode *inode, struct file *filp){ struct zc0301_device *cam = NULL; int err = 0; u8 i = 0; if (filp == NULL) { debug_error("zc0301_open:filp is null\n"); return -EINVAL; } //debug_param("%s\n", __FUNCTION__); debug_param("process %i(%s) wanna open this driver\n", current->pid, current->comm); cam = video_get_drvdata(video_devdata(filp)); mutex_lock(&cam->dev_mutex); if (cam->users) { debug_info("the device is busy now.plz visit it later\n"); if (filp->f_flags & O_NONBLOCK) { debug_error("zc0301_open:operation is nonblocked\n"); mutex_unlock(&cam->dev_mutex); return -EWOULDBLOCK; } /*if open-operation is blocked, I will add blocked handles here*/ /*But open-operation is non_blocked usually*/ } /*if this is the first time to open device, driver should make sure whether the device has been configed*/ if (cam->d_state & MISCONFIGED) { err = zc0301_init_pb0330_and_0301(cam); if (err) { debug_error("zc0301_open:init pb0330 and 0301 failed\n"); mutex_unlock(&cam->dev_mutex); return -EFAULT; } cam->d_state = INITED; } filp->private_data = cam; INIT_LIST_HEAD(&cam->inqueue); INIT_LIST_HEAD(&cam->outqueue); for (i = 0; i < ZC0301_MAX_FRAMES; i++) { cam->frame[i].f_state = F_UNUSED; cam->frame[i].buf.bytesused = 0; } cam->users++; cam->nbuffers = 0; cam->s_state = STREAM_OFF; err = zc0301_urb_start(cam); if (err) { debug_error("zc0301_open:urb start transfer failed\n"); mutex_unlock(&cam->dev_mutex); return err; } mutex_unlock(&cam->dev_mutex); //debug_info("zc0301_open:open successed\n"); return 0;}int zc0301_release(struct inode *inode, struct file *filp){ struct zc0301_device *cam = NULL; int err = 0; if (filp == NULL) { debug_error("zc0301_release:filp is null\n"); return -EINVAL; } //debug_param("%s\n", __FUNCTION__); cam = filp->private_data; mutex_lock(&cam->dev_mutex); err = zc0301_urb_stop(cam); if (err) { debug_error("zc0301_release:urb stop transfer failed\n"); mutex_unlock(&cam->dev_mutex); return err; } /*if driver has alloced some vma, vfree them first*/ if (cam->nbuffers) { vfree(cam->frame[0].bufmem); cam->nbuffers = 0; } cam->users--; mutex_unlock(&cam->dev_mutex); //debug_info("zc0301_release:close successed\n"); return 0;}int zc0301_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ struct zc0301_device *cam = NULL; int err = 0; if (filp == NULL) { debug_error("zc0301_ioctl:filp is null\n"); return -EINVAL; } //debug_param("%s\n", __FUNCTION__); cam = filp->private_data; if (mutex_lock_interruptible(&cam->fop_mutex)) /*I don't know why to use interruptible here*/ return -ERESTARTSYS; /*it works well if I choose mutex_lock*/ if ((cam->d_state & DISCONNECTED) || (cam->d_state & MISCONFIGED)) { debug_error("zc0301_ioctl:dev state is wrong\n"); //wake_up_interruptible_all(&cam->wait_frame); mutex_unlock(&cam->fop_mutex); return cam->d_state & DISCONNECTED ? -ENODEV : -EIO; } err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); mutex_unlock(&cam->fop_mutex); return err;}static const struct file_operations zc0301_fops = { .owner = THIS_MODULE, .open = zc0301_open, .release = zc0301_release, .mmap = zc0301_mmap, .ioctl = zc0301_ioctl, //.compat_ioctl = v4l_compat_ioctl32,};static struct video_device zc0301_video = { .name = "zc0301 PC camera", .owner = THIS_MODULE, .type = VID_TYPE_CAPTURE, .fops = &zc0301_fops, .minor = -1, .release = video_device_release,};static int zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id){ struct zc0301_device *cam = NULL; struct usb_device *udev = NULL; int err = 0; u8 flag = 0; if ((intf == NULL) || (id == NULL)) { debug_error("zc0301_usb_probe:intf or id is null\n"); return -EIO; } debug_param("There are %u alternate settings for this" "interface, so you must make sure that your altsetting" "number should between 0 to %u\n" , intf->num_altsetting, intf->num_altsetting - 1); udev = interface_to_usbdev(intf); if (udev == NULL) { debug_error("zc0301_usb_probe:udev is null\n"); return -EINVAL; } debug_param("This Device has %d configurations\n", (udev->descriptor).bNumConfigurations); if ((udev->descriptor).bNumConfigurations > 1) { debug_error("We Don't Handle MultiConfig Device.....\n"); return -EFAULT; } switch (udev->speed) { case USB_SPEED_UNKNOWN: debug_info("UNKNOWN SPEED.\n"); break; case USB_SPEED_LOW: debug_info("LOW SPEED DEVICE.\n"); break; case USB_SPEED_FULL: debug_info("FULL SPEED DEVICE.\n"); break; case USB_SPEED_HIGH: debug_info("HIGH SPEED DEVICE.\n"); break; case USB_SPEED_VARIABLE: debug_info("USING FOR WIRELESS DEVICE.\n"); break; } debug_info("START PROBE\n"); do { if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) { debug_error("zc0301_usb_probe:cam alloc failed\n"); flag = 1; break; } if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { debug_error("zc0301_usb_probe:control_buffer alloc failed\n"); flag = 1; break; } if (!(cam->v4ldev = video_device_alloc())) { debug_error("zc0301_usb_probe:v4ldev alloc failed\n"); flag = 1; break; } debug_info("resources alloc successed\n"); cam->usbdev = udev; mutex_init(&cam->dev_mutex); mutex_lock(&cam->dev_mutex); err = zc0301_pb0330_probe(cam); if (err) { debug_error("zc0301_usb_probe:pb0330 probe failed\n"); flag = 1; mutex_unlock(&cam->dev_mutex); break; } debug_info("sensor IC:pb0330 probe successed\n"); err = zc0301_init_pb0330_and_0301(cam); if (err) { debug_error("zc0301_usb_probe:pb0330 and 0301 failed\n"); cam->d_state = MISCONFIGED; } debug_info("pb0330 && 0301 init successed\n"); memcpy(cam->v4ldev, &zc0301_video, sizeof(struct video_device)); video_set_drvdata(cam->v4ldev, cam); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, 0); if (err) { debug_error("zc0301_usb_probe:register video device failed\n"); mutex_unlock(&cam->dev_mutex); flag = 1; break; } debug_info("register video device successed\n"); usb_set_intfdata(intf, cam); usb_get_dev(cam->usbdev); mutex_unlock(&cam->dev_mutex); debug_info("PROBE SUCCESSED!\n"); /*print descriptors*/ zc0301_get_device_descriptor(cam); zc0301_get_config_descriptor(cam); zc0301_get_interface_descriptor(cam); zc0301_get_endpoint_descriptor(cam); zc0301_get_string_descriptor(cam); } while(0); if (flag) { debug_error("PROBE FAILED!!!!!\n"); if (cam) { if (cam->control_buffer) kfree(cam->control_buffer); if (cam->v4ldev) video_device_release(cam->v4ldev); } kfree(cam); } return err;}static void zc0301_usb_disconnect(struct usb_interface* intf){ struct zc0301_device *cam = NULL; //u8 i = 0; if (intf == NULL) { debug_error("zc0301_usb_disconnect:intf is null\n"); return; } //debug_param("%s\n", __FUNCTION__); cam = usb_get_intfdata(intf); if (cam == NULL) { debug_error("zc0301_usb_disconnect:cam is null\n"); return; } debug_info("DISCONNECT START\n"); mutex_lock(&cam->dev_mutex); if (cam->users) { zc0301_urb_stop(cam); cam->d_state |= DISCONNECTED; wake_up_interruptible(&cam->wait_frame); } else { video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); kfree(cam->control_buffer); cam->d_state |= DISCONNECTED; } usb_put_dev(cam->usbdev); mutex_unlock(&cam->dev_mutex); if (cam->users == 0) kfree(cam); debug_info("DISCONNECT SUCCESSED!\n");}static struct usb_driver zc0301_module_driver = { .name = "webcam", .id_table = zc0301_id_table, .probe = zc0301_usb_probe, .disconnect = zc0301_usb_disconnect,};static int __init zc0301_module_insmod(void){ s8 err = 0; debug_info("MODULE INSMOD START\n"); err = usb_register(&zc0301_module_driver); if (err) { debug_error("register an usb device failed...\n"); return err; } return 0;}static void __exit zc0301_module_rmmod(void){ debug_info("MODULE RMMOD START\n"); usb_deregister(&zc0301_module_driver);}module_init(zc0301_module_insmod);module_exit(zc0301_module_rmmod);MODULE_LICENSE("GPL");MODULE_AUTHOR("WE.XCC");MODULE_DESCRIPTION("This is the first driver I programs using for my ZC0301 PC Camera" "ZC0301[P] process && control IC and PB0330 sensor IC");MODULE_DEVICE_TABLE(usb, zc0301_id_table);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -