📄 em28xx-video.c
字号:
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, rc); return rc;}static const struct file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, .release = em28xx_v4l2_close, .read = em28xx_v4l2_read, .poll = em28xx_v4l2_poll, .mmap = em28xx_v4l2_mmap, .ioctl = video_ioctl2, .llseek = no_llseek, .compat_ioctl = v4l_compat_ioctl32,};static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,#if 0 .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,#endif .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency,#ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register,#endif#ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf,#endif};static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, .release = video_device_release, .ioctl_ops = &video_ioctl_ops, .minor = -1, .tvnorms = V4L2_STD_ALL, .current_norm = V4L2_STD_PAL,};static const struct file_operations radio_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, .release = em28xx_v4l2_close, .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek,};static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = radio_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, .vidioc_s_audio = radio_s_audio, .vidioc_s_input = radio_s_input, .vidioc_queryctrl = radio_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency,#ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register,#endif};static struct video_device em28xx_radio_template = { .name = "em28xx-radio", .fops = &radio_fops, .ioctl_ops = &radio_ioctl_ops, .minor = -1,};/******************************** usb interface ******************************/static LIST_HEAD(em28xx_extension_devlist);static DEFINE_MUTEX(em28xx_extension_devlist_lock);int em28xx_register_extension(struct em28xx_ops *ops){ struct em28xx *dev = NULL; mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); list_for_each_entry(dev, &em28xx_devlist, devlist) { if (dev) ops->init(dev); } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); mutex_unlock(&em28xx_extension_devlist_lock); return 0;}EXPORT_SYMBOL(em28xx_register_extension);void em28xx_unregister_extension(struct em28xx_ops *ops){ struct em28xx *dev = NULL; list_for_each_entry(dev, &em28xx_devlist, devlist) { if (dev) ops->fini(dev); } mutex_lock(&em28xx_extension_devlist_lock); printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); list_del(&ops->next); mutex_unlock(&em28xx_extension_devlist_lock);}EXPORT_SYMBOL(em28xx_unregister_extension);static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, const char *type_name){ struct video_device *vfd; vfd = video_device_alloc(); if (NULL == vfd) return NULL; *vfd = *template; vfd->minor = -1; vfd->parent = &dev->udev->dev; vfd->release = video_device_release; vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); return vfd;}/* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device */static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor){ struct em28xx_ops *ops = NULL; struct em28xx *dev = *devhandle; int retval = -ENOMEM; int errCode; unsigned int maxh, maxw; dev->udev = udev; mutex_init(&dev->lock); spin_lock_init(&dev->slock); init_waitqueue_head(&dev->open); init_waitqueue_head(&dev->wait_frame); init_waitqueue_head(&dev->wait_stream); dev->em28xx_write_regs = em28xx_write_regs; dev->em28xx_read_reg = em28xx_read_reg; dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; dev->em28xx_write_regs_req = em28xx_write_regs_req; dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->is_em2800 = em28xx_boards[dev->model].is_em2800; em28xx_pre_card_setup(dev); errCode = em28xx_config(dev); if (errCode) { em28xx_errdev("error configuring device\n"); em28xx_devused &= ~(1<<dev->devno); kfree(dev); return -ENOMEM; } /* register i2c bus */ errCode = em28xx_i2c_register(dev); if (errCode < 0) { em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", __func__, errCode); return errCode; } /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); /* Configure audio */ errCode = em28xx_audio_analog_set(dev); if (errCode < 0) { em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n", __func__, errCode); return errCode; } /* configure the device */ em28xx_config_i2c(dev); /* set default norm */ dev->norm = em28xx_video_template.current_norm; maxw = norm_maxw(dev); maxh = norm_maxh(dev); /* set default image size */ dev->width = maxw; dev->height = maxh; dev->interlaced = EM28XX_INTERLACED_DEFAULT; dev->hscale = 0; dev->vscale = 0; dev->ctl_input = 2; errCode = em28xx_config(dev); if (errCode < 0) { em28xx_errdev("%s: em28xx_config - errCode [%d]!\n", __func__, errCode); return errCode; } list_add_tail(&dev->devlist, &em28xx_devlist); /* allocate and fill video video_device struct */ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); if (NULL == dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); goto fail_unreg; } /* register v4l2 video video_device */ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); if (retval) { em28xx_errdev("unable to register video device (error=%i).\n", retval); goto fail_unreg; } /* Allocate and fill vbi video_device struct */ dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); /* register v4l2 vbi video_device */ if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]) < 0) { em28xx_errdev("unable to register vbi device\n"); retval = -ENODEV; goto fail_unreg; } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio"); if (NULL == dev->radio_dev) { em28xx_errdev("cannot allocate video_device.\n"); goto fail_unreg; } retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (retval < 0) { em28xx_errdev("can't register radio device\n"); goto fail_unreg; } em28xx_info("Registered radio device as /dev/radio%d\n", dev->radio_dev->num); } /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued);#if 0 video_set_drvdata(dev->vbi_dev, dev);#endif if (dev->has_msp34xx) { /* Send a reset to other chips via gpio */ errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); if (errCode < 0) { em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n", __func__, errCode); return errCode; } msleep(3); errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); if (errCode < 0) { em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n", __func__, errCode); return errCode; } msleep(3); } video_mux(dev, 0); em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", dev->vdev->num, dev->vbi_dev->num); mutex_lock(&em28xx_extension_devlist_lock); if (!list_empty(&em28xx_extension_devlist)) { list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->id) ops->init(dev); } } mutex_unlock(&em28xx_extension_devlist_lock); return 0;fail_unreg: em28xx_release_resources(dev); mutex_unlock(&dev->lock); kfree(dev); return retval;}#if defined(CONFIG_MODULES) && defined(MODULE)#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)static void request_module_async(void *ptr){ struct em28xx *dev = (struct em28xx *)ptr;#elsestatic void request_module_async(struct work_struct *work){ struct em28xx *dev = container_of(work, struct em28xx, request_module_wk);#endif if (dev->has_audio_class) request_module("snd-usb-audio"); else request_module("em28xx-alsa"); if (dev->has_dvb) request_module("em28xx-dvb");}static void request_modules(struct em28xx *dev){#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) INIT_WORK(&dev->request_module_wk, request_module_async, (void *)dev);#else INIT_WORK(&dev->request_module_wk, request_module_async);#endif schedule_work(&dev->request_module_wk);}#else#define request_modules(dev)#endif /* CONFIG_MODULES *//* * em28xx_usb_probe() * checks for supported devices */static int em28xx_usb_probe(struct usb_interface *interface, const struct usb_device_id *id){ const struct usb_endpoint_descriptor *endpoint; struct usb_device *udev; struct usb_interface *uif; struct em28xx *dev = NULL; int retval = -ENODEV; int i, nr, ifnum; udev = usb_get_dev(interface_to_usbdev(interface)); ifnum = interface->altsetting[0].desc.bInterfaceNumber; /* Check to see next free device and mark as used */ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); em28xx_devused |= 1<<nr; /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n", udev->descriptor.idVendor, udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); em28xx_devused &= ~(1<<nr); return -ENODEV; } em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n", udev->descriptor.idVendor, udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); endpoint = &interface->cur_altsetting->endpoint[1].desc; /* check if the device has the iso in endpoint at the correct place */ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); em28xx_devused &= ~(1<<nr); return -ENODEV; } if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); em28xx_devused &= ~(1<<nr); return -ENODEV; } if (nr >= EM28XX_MAXBOARDS) { printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", EM28XX_MAXBOARDS); em28xx_devused &= ~(1<<nr); return -ENOMEM; } /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); em28xx_devused &= ~(1<<nr); return -ENOMEM; } snprintf(dev->name, 29, "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; /* Checks if audio is provided by some interface */ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { uif = udev->config->interface[i]; if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { dev->has_audio_class = 1; break; } } printk(KERN_INFO DRIVER_NAME " %s usb audio class\n", dev->has_audio_class ? "Has" : "Doesn't have"); /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; dev->num_alt = uif->num_altsetting; em28xx_info("Alternate settings: %i\n", dev->num_alt);/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */ dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); if (dev->alt_max_pkt_size == NULL) { em28xx_errdev("out of memory!\n"); em28xx_devused &= ~(1<<nr); kfree(dev); return -ENOMEM; } for (i = 0; i < dev->num_alt ; i++) { u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); em28xx_info("Alternate setting %i, max size= %i\n", i, dev->alt_max_pkt_size[i]); } if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) dev->model = card[nr]; /* allocate device struct */ retval = em28xx_init_dev(&dev, udev, nr); if (retval) return retval; em28xx_info("Found %s\n", em28xx_boards[dev->model].name); /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); request_modules(dev); return 0;}/* * em28xx_usb_disconnect() * called when the device gets diconencted * video device will be unregistered on v4l2_close in case it is still open */static void em28xx_usb_disconnect(struct usb_interface *interface){ struct em28xx *dev; struct em28xx_ops *ops = NULL; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); if (!dev) return; em28xx_info("disconnecting %s\n", dev->vdev->name); /* wait until all current v4l2 io is finished then deallocate resources */ mutex_lock(&dev->lock); wake_up_interruptible_all(&dev->open); if (dev->users) { em28xx_warn ("device /dev/video%d is open! Deregistration and memory " "deallocation are deferred on close.\n", dev->vdev->num); dev->state |= DEV_MISCONFIGURED; em28xx_uninit_isoc(dev); dev->state |= DEV_DISCONNECTED; wake_up_interruptible(&dev->wait_frame); wake_up_interruptible(&dev->wait_stream); } else { dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); } mutex_unlock(&dev->lock); mutex_lock(&em28xx_extension_devlist_lock); if (!list_empty(&em28xx_extension_devlist)) { list_for_each_entry(ops, &em28xx_extension_devlist, next) { ops->fini(dev); } } mutex_unlock(&em28xx_extension_devlist_lock); if (!dev->users) { kfree(dev->alt_max_pkt_size); kfree(dev); }}static struct usb_driver em28xx_usb_driver = { .name = "em28xx", .probe = em28xx_usb_probe, .disconnect = em28xx_usb_disconnect, .id_table = em28xx_id_table,};static int __init em28xx_module_init(void){ int result; printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n", (EM28XX_VERSION_CODE >> 16) & 0xff, (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);#ifdef SNAPSHOT printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n", SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);#endif /* register this driver with the USB subsystem */ result = usb_register(&em28xx_usb_driver); if (result) em28xx_err(DRIVER_NAME " usb_register failed. Error number %d.\n", result); return result;}static void __exit em28xx_module_exit(void){ /* deregister this driver with the USB subsystem */ usb_deregister(&em28xx_usb_driver);}module_init(em28xx_module_init);module_exit(em28xx_module_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -