| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Main USB camera driver | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-03-14 05:33:01 -03:00
										 |  |  |  * Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  |  * Camera button input handling by Márton Németh | 
					
						
							|  |  |  |  * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-23 05:17:10 -03:00
										 |  |  | #define GSPCA_VERSION	"2.14.0"
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/fs.h>
 | 
					
						
							|  |  |  | #include <linux/vmalloc.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | #include <linux/string.h>
 | 
					
						
							|  |  |  | #include <linux/pagemap.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-01 10:03:42 -03:00
										 |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | #include <asm/page.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-01 10:03:42 -03:00
										 |  |  | #include <linux/uaccess.h>
 | 
					
						
							| 
									
										
										
										
											2010-03-07 06:56:56 -03:00
										 |  |  | #include <linux/ktime.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-20 08:12:02 -03:00
										 |  |  | #include <media/v4l2-ioctl.h>
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | #include <media/v4l2-ctrls.h>
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | #include <media/v4l2-fh.h>
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:20 -03:00
										 |  |  | #include <media/v4l2-event.h>
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "gspca.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-24 19:28:59 -03:00
										 |  |  | #if IS_ENABLED(CONFIG_INPUT)
 | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | #include <linux/input.h>
 | 
					
						
							|  |  |  | #include <linux/usb/input.h>
 | 
					
						
							| 
									
										
										
										
											2010-02-18 15:02:51 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | /* global values */ | 
					
						
							| 
									
										
										
										
											2009-01-17 04:46:38 -03:00
										 |  |  | #define DEF_NURBS 3		/* default number of URBs */
 | 
					
						
							|  |  |  | #if DEF_NURBS > MAX_NURBS
 | 
					
						
							|  |  |  | #error "DEF_NURBS too big"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-02 06:59:13 -03:00
										 |  |  | MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | MODULE_DESCRIPTION("GSPCA USB Camera Driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							| 
									
										
										
										
											2011-09-23 05:17:10 -03:00
										 |  |  | MODULE_VERSION(GSPCA_VERSION); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | int gspca_debug; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | EXPORT_SYMBOL(gspca_debug); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt, | 
					
						
							|  |  |  | 			__u32 pixfmt, int w, int h) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PDEBUG(debug, "%s %c%c%c%c %dx%d", | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			txt, | 
					
						
							|  |  |  | 			pixfmt & 0xff, | 
					
						
							|  |  |  | 			(pixfmt >> 8) & 0xff, | 
					
						
							|  |  |  | 			(pixfmt >> 16) & 0xff, | 
					
						
							|  |  |  | 			pixfmt >> 24, | 
					
						
							|  |  |  | 			w, h); | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PDEBUG(debug, "%s 0x%08x %dx%d", | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			txt, | 
					
						
							|  |  |  | 			pixfmt, | 
					
						
							|  |  |  | 			w, h); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-07 15:30:50 -03:00
										 |  |  | /* specific memory types - !! should be different from V4L2_MEMORY_xxx */ | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | #define GSPCA_MEMORY_NO 0	/* V4L2_MEMORY_xxx starts from 1 */
 | 
					
						
							|  |  |  | #define GSPCA_MEMORY_READ 7
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * VMA operations. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void gspca_vm_open(struct vm_area_struct *vma) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gspca_frame *frame = vma->vm_private_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	frame->vma_use_count++; | 
					
						
							|  |  |  | 	frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gspca_vm_close(struct vm_area_struct *vma) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gspca_frame *frame = vma->vm_private_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (--frame->vma_use_count <= 0) | 
					
						
							|  |  |  | 		frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-27 22:29:37 +04:00
										 |  |  | static const struct vm_operations_struct gspca_vm_ops = { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	.open		= gspca_vm_open, | 
					
						
							|  |  |  | 	.close		= gspca_vm_close, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Input and interrupt endpoint handling functions | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-01-24 19:28:59 -03:00
										 |  |  | #if IS_ENABLED(CONFIG_INPUT)
 | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | static void int_irq(struct urb *urb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = urb->status; | 
					
						
							|  |  |  | 	switch (ret) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, | 
					
						
							|  |  |  | 		    urb->transfer_buffer, urb->actual_length) < 0) { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 			PERR("Unknown packet received"); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case -ENOENT: | 
					
						
							|  |  |  | 	case -ECONNRESET: | 
					
						
							|  |  |  | 	case -ENODEV: | 
					
						
							|  |  |  | 	case -ESHUTDOWN: | 
					
						
							|  |  |  | 		/* Stop is requested either by software or hardware is gone,
 | 
					
						
							|  |  |  | 		 * keep the ret value non-zero and don't resubmit later. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PERR("URB error %i, resubmitting", urb->status); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 		urb->status = 0; | 
					
						
							|  |  |  | 		ret = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret == 0) { | 
					
						
							|  |  |  | 		ret = usb_submit_urb(urb, GFP_ATOMIC); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("Resubmit URB failed with error %i\n", ret); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int gspca_input_connect(struct gspca_dev *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct input_dev *input_dev; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->input_dev = NULL; | 
					
						
							| 
									
										
										
										
											2010-02-19 04:28:39 -03:00
										 |  |  | 	if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input)  { | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 		input_dev = input_allocate_device(); | 
					
						
							|  |  |  | 		if (!input_dev) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		usb_make_path(dev->dev, dev->phys, sizeof(dev->phys)); | 
					
						
							|  |  |  | 		strlcat(dev->phys, "/input0", sizeof(dev->phys)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		input_dev->name = dev->sd_desc->name; | 
					
						
							|  |  |  | 		input_dev->phys = dev->phys; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		usb_to_input_id(dev->dev, &input_dev->id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		input_dev->evbit[0] = BIT_MASK(EV_KEY); | 
					
						
							|  |  |  | 		input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); | 
					
						
							|  |  |  | 		input_dev->dev.parent = &dev->dev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = input_register_device(input_dev); | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("Input device registration failed with error %i\n", | 
					
						
							|  |  |  | 			       err); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 			input_dev->dev.parent = NULL; | 
					
						
							|  |  |  | 			input_free_device(input_dev); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			dev->input_dev = input_dev; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2010-02-19 04:41:40 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			  struct usb_endpoint_descriptor *ep) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int buffer_len; | 
					
						
							|  |  |  | 	int interval; | 
					
						
							|  |  |  | 	struct urb *urb; | 
					
						
							|  |  |  | 	struct usb_device *dev; | 
					
						
							|  |  |  | 	void *buffer = NULL; | 
					
						
							|  |  |  | 	int ret = -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-29 06:01:53 -03:00
										 |  |  | 	buffer_len = le16_to_cpu(ep->wMaxPacketSize); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 	interval = ep->bInterval; | 
					
						
							| 
									
										
										
										
											2010-07-14 06:26:54 -03:00
										 |  |  | 	PDEBUG(D_CONF, "found int in endpoint: 0x%x, " | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 		"buffer_len=%u, interval=%u", | 
					
						
							|  |  |  | 		ep->bEndpointAddress, buffer_len, interval); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev = gspca_dev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	urb = usb_alloc_urb(0, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!urb) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-20 21:26:12 -07:00
										 |  |  | 	buffer = usb_alloc_coherent(dev, buffer_len, | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 				GFP_KERNEL, &urb->transfer_dma); | 
					
						
							|  |  |  | 	if (!buffer) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto error_buffer; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	usb_fill_int_urb(urb, dev, | 
					
						
							|  |  |  | 		usb_rcvintpipe(dev, ep->bEndpointAddress), | 
					
						
							|  |  |  | 		buffer, buffer_len, | 
					
						
							|  |  |  | 		int_irq, (void *)gspca_dev, interval); | 
					
						
							| 
									
										
										
										
											2010-09-03 06:57:19 -03:00
										 |  |  | 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 	ret = usb_submit_urb(urb, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PERR("submit int URB failed with error %i", ret); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 		goto error_submit; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-10-26 05:16:32 -03:00
										 |  |  | 	gspca_dev->int_urb = urb; | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error_submit: | 
					
						
							| 
									
										
										
										
											2010-04-12 13:17:25 +02:00
										 |  |  | 	usb_free_coherent(dev, | 
					
						
							|  |  |  | 			  urb->transfer_buffer_length, | 
					
						
							|  |  |  | 			  urb->transfer_buffer, | 
					
						
							|  |  |  | 			  urb->transfer_dma); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | error_buffer: | 
					
						
							|  |  |  | 	usb_free_urb(urb); | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-19 04:41:40 -03:00
										 |  |  | static void gspca_input_create_urb(struct gspca_dev *gspca_dev) | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct usb_interface *intf; | 
					
						
							|  |  |  | 	struct usb_host_interface *intf_desc; | 
					
						
							|  |  |  | 	struct usb_endpoint_descriptor *ep; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gspca_dev->sd_desc->int_pkt_scan)  { | 
					
						
							|  |  |  | 		intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); | 
					
						
							|  |  |  | 		intf_desc = intf->cur_altsetting; | 
					
						
							|  |  |  | 		for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { | 
					
						
							|  |  |  | 			ep = &intf_desc->endpoint[i].desc; | 
					
						
							|  |  |  | 			if (usb_endpoint_dir_in(ep) && | 
					
						
							|  |  |  | 			    usb_endpoint_xfer_int(ep)) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-19 04:41:40 -03:00
										 |  |  | 				alloc_and_submit_int_urb(gspca_dev, ep); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct urb *urb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	urb = gspca_dev->int_urb; | 
					
						
							|  |  |  | 	if (urb) { | 
					
						
							|  |  |  | 		gspca_dev->int_urb = NULL; | 
					
						
							|  |  |  | 		usb_kill_urb(urb); | 
					
						
							| 
									
										
										
										
											2010-04-12 13:17:25 +02:00
										 |  |  | 		usb_free_coherent(gspca_dev->dev, | 
					
						
							|  |  |  | 				  urb->transfer_buffer_length, | 
					
						
							|  |  |  | 				  urb->transfer_buffer, | 
					
						
							|  |  |  | 				  urb->transfer_dma); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 		usb_free_urb(urb); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-03-30 14:32:04 -03:00
										 |  |  | #else
 | 
					
						
							|  |  |  | static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int gspca_input_connect(struct gspca_dev *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  |  * fill a video frame from an URB and resubmit | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | static void fill_frame(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			struct urb *urb) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-01-13 06:07:59 -03:00
										 |  |  | 	u8 *data;		/* address of data in the iso message */ | 
					
						
							| 
									
										
										
										
											2008-09-20 05:44:21 -03:00
										 |  |  | 	int i, len, st; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	cam_pkt_op pkt_scan; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-30 15:50:11 -03:00
										 |  |  | 	if (urb->status != 0) { | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 		if (urb->status == -ESHUTDOWN) | 
					
						
							|  |  |  | 			return;		/* disconnection */ | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:13 -03:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2009-10-24 15:02:14 -03:00
										 |  |  | 		if (gspca_dev->frozen) | 
					
						
							|  |  |  | 			return; | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:13 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PERR("urb status: %d", urb->status); | 
					
						
							| 
									
										
										
										
											2009-11-13 07:15:08 -03:00
										 |  |  | 		urb->status = 0; | 
					
						
							| 
									
										
										
										
											2009-10-23 06:56:06 -03:00
										 |  |  | 		goto resubmit; | 
					
						
							| 
									
										
										
										
											2008-06-30 15:50:11 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	pkt_scan = gspca_dev->sd_desc->pkt_scan; | 
					
						
							|  |  |  | 	for (i = 0; i < urb->number_of_packets; i++) { | 
					
						
							| 
									
										
										
										
											2010-12-27 12:00:03 -03:00
										 |  |  | 		len = urb->iso_frame_desc[i].actual_length; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* check the packet status and length */ | 
					
						
							|  |  |  | 		st = urb->iso_frame_desc[i].status; | 
					
						
							|  |  |  | 		if (st) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("ISOC data error: [%d] len=%d, status=%d\n", | 
					
						
							|  |  |  | 			       i, len, st); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			gspca_dev->last_packet_type = DISCARD_PACKET; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2010-12-25 13:22:21 -03:00
										 |  |  | 		if (len == 0) { | 
					
						
							|  |  |  | 			if (gspca_dev->empty_packet == 0) | 
					
						
							|  |  |  | 				gspca_dev->empty_packet = 1; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* let the packet be analyzed by the subdriver */ | 
					
						
							|  |  |  | 		PDEBUG(D_PACK, "packet [%d] o:%d l:%d", | 
					
						
							|  |  |  | 			i, urb->iso_frame_desc[i].offset, len); | 
					
						
							| 
									
										
										
										
											2009-01-13 06:07:59 -03:00
										 |  |  | 		data = (u8 *) urb->transfer_buffer | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 					+ urb->iso_frame_desc[i].offset; | 
					
						
							| 
									
										
										
										
											2009-11-13 09:21:03 -03:00
										 |  |  | 		pkt_scan(gspca_dev, data, len); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-23 06:56:06 -03:00
										 |  |  | resubmit: | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	/* resubmit the URB */ | 
					
						
							|  |  |  | 	st = usb_submit_urb(urb, GFP_ATOMIC); | 
					
						
							|  |  |  | 	if (st < 0) | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("usb_submit_urb() ret %d\n", st); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ISOC message interrupt from the USB device | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  |  * Analyse each packet and call the subdriver for copy to the frame buffer. | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-01-13 06:07:59 -03:00
										 |  |  | static void isoc_irq(struct urb *urb) | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-15 05:36:30 -03:00
										 |  |  | 	PDEBUG(D_PACK, "isoc irq"); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	if (!gspca_dev->streaming) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	fill_frame(gspca_dev, urb); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * bulk message interrupt from the USB device | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-01-13 06:07:59 -03:00
										 |  |  | static void bulk_irq(struct urb *urb) | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 	int st; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	PDEBUG(D_PACK, "bulk irq"); | 
					
						
							|  |  |  | 	if (!gspca_dev->streaming) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2008-11-18 04:19:52 -03:00
										 |  |  | 	switch (urb->status) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 	case -ESHUTDOWN: | 
					
						
							|  |  |  | 		return;		/* disconnection */ | 
					
						
							| 
									
										
										
										
											2008-11-18 04:19:52 -03:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2009-10-24 15:02:14 -03:00
										 |  |  | 		if (gspca_dev->frozen) | 
					
						
							|  |  |  | 			return; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PERR("urb status: %d", urb->status); | 
					
						
							| 
									
										
										
										
											2009-11-13 07:15:08 -03:00
										 |  |  | 		urb->status = 0; | 
					
						
							| 
									
										
										
										
											2009-10-23 06:56:06 -03:00
										 |  |  | 		goto resubmit; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-13 08:16:02 -03:00
										 |  |  | 	PDEBUG(D_PACK, "packet l:%d", urb->actual_length); | 
					
						
							|  |  |  | 	gspca_dev->sd_desc->pkt_scan(gspca_dev, | 
					
						
							|  |  |  | 				urb->transfer_buffer, | 
					
						
							|  |  |  | 				urb->actual_length); | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-23 06:56:06 -03:00
										 |  |  | resubmit: | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 	/* resubmit the URB */ | 
					
						
							|  |  |  | 	if (gspca_dev->cam.bulk_nurbs != 0) { | 
					
						
							|  |  |  | 		st = usb_submit_urb(urb, GFP_ATOMIC); | 
					
						
							|  |  |  | 		if (st < 0) | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("usb_submit_urb() ret %d\n", st); | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * add data to the current frame | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  |  * This function is called by the subdrivers at interrupt level. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  * To build a frame, these ones must add | 
					
						
							|  |  |  |  *	- one FIRST_PACKET | 
					
						
							|  |  |  |  *	- 0 or many INTER_PACKETs | 
					
						
							|  |  |  |  *	- one LAST_PACKET | 
					
						
							|  |  |  |  * DISCARD_PACKET invalidates the whole frame. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-11-13 09:21:03 -03:00
										 |  |  | void gspca_frame_add(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			enum gspca_packet_type packet_type, | 
					
						
							|  |  |  | 			const u8 *data, | 
					
						
							|  |  |  | 			int len) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-13 07:38:16 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	int i, j; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	PDEBUG(D_PACK, "add t:%d l:%d",	packet_type, len); | 
					
						
							| 
									
										
										
										
											2009-11-13 08:16:02 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (packet_type == FIRST_PACKET) { | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 		i = atomic_read(&gspca_dev->fr_i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* if there are no queued buffer, discard the whole frame */ | 
					
						
							|  |  |  | 		if (i == atomic_read(&gspca_dev->fr_q)) { | 
					
						
							|  |  |  | 			gspca_dev->last_packet_type = DISCARD_PACKET; | 
					
						
							| 
									
										
										
										
											2010-10-16 13:59:13 -03:00
										 |  |  | 			gspca_dev->sequence++; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 		j = gspca_dev->fr_queue[i]; | 
					
						
							|  |  |  | 		frame = &gspca_dev->frame[j]; | 
					
						
							| 
									
										
										
										
											2010-03-07 06:56:56 -03:00
										 |  |  | 		frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); | 
					
						
							| 
									
										
										
										
											2010-10-16 13:59:13 -03:00
										 |  |  | 		frame->v4l2_buf.sequence = gspca_dev->sequence++; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 		gspca_dev->image = frame->data; | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 		gspca_dev->image_len = 0; | 
					
						
							| 
									
										
										
										
											2010-07-29 02:46:02 -03:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		switch (gspca_dev->last_packet_type) { | 
					
						
							|  |  |  | 		case DISCARD_PACKET: | 
					
						
							| 
									
										
										
										
											2011-06-05 03:44:43 -03:00
										 |  |  | 			if (packet_type == LAST_PACKET) { | 
					
						
							| 
									
										
										
										
											2010-07-29 02:46:02 -03:00
										 |  |  | 				gspca_dev->last_packet_type = packet_type; | 
					
						
							| 
									
										
										
										
											2011-06-05 03:44:43 -03:00
										 |  |  | 				gspca_dev->image = NULL; | 
					
						
							|  |  |  | 				gspca_dev->image_len = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2010-07-29 02:46:02 -03:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		case LAST_PACKET: | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	/* append the packet to the frame buffer */ | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (len > 0) { | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 		if (gspca_dev->image_len + len > gspca_dev->frsz) { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 			PERR("frame overflow %d > %d", | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 				gspca_dev->image_len + len, | 
					
						
							|  |  |  | 				gspca_dev->frsz); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			packet_type = DISCARD_PACKET; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2010-07-29 02:46:02 -03:00
										 |  |  | /* !! image is NULL only when last pkt is LAST or DISCARD
 | 
					
						
							|  |  |  | 			if (gspca_dev->image == NULL) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 				pr_err("gspca_frame_add() image == NULL\n"); | 
					
						
							| 
									
										
										
										
											2010-07-29 02:46:02 -03:00
										 |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 			memcpy(gspca_dev->image + gspca_dev->image_len, | 
					
						
							|  |  |  | 				data, len); | 
					
						
							|  |  |  | 			gspca_dev->image_len += len; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gspca_dev->last_packet_type = packet_type; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	/* if last packet, invalidate packet concatenation until
 | 
					
						
							|  |  |  | 	 * next first packet, wake up the application and advance | 
					
						
							|  |  |  | 	 * in the queue */ | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (packet_type == LAST_PACKET) { | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 		i = atomic_read(&gspca_dev->fr_i); | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 		j = gspca_dev->fr_queue[i]; | 
					
						
							|  |  |  | 		frame = &gspca_dev->frame[j]; | 
					
						
							|  |  |  | 		frame->v4l2_buf.bytesused = gspca_dev->image_len; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:16:40 -03:00
										 |  |  | 		frame->v4l2_buf.flags = (frame->v4l2_buf.flags | 
					
						
							|  |  |  | 					 | V4L2_BUF_FLAG_DONE) | 
					
						
							|  |  |  | 					& ~V4L2_BUF_FLAG_QUEUED; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 		i = (i + 1) % GSPCA_MAX_FRAMES; | 
					
						
							|  |  |  | 		atomic_set(&gspca_dev->fr_i, i); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		wake_up_interruptible(&gspca_dev->wq);	/* event = new frame */ | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 		PDEBUG(D_FRAM, "frame complete len:%d", | 
					
						
							|  |  |  | 			frame->v4l2_buf.bytesused); | 
					
						
							|  |  |  | 		gspca_dev->image = NULL; | 
					
						
							|  |  |  | 		gspca_dev->image_len = 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(gspca_frame_add); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-31 04:51:36 -03:00
										 |  |  | static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, | 
					
						
							|  |  |  | 			enum v4l2_memory memory, unsigned int count) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							|  |  |  | 	unsigned int frsz; | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 	frsz = gspca_dev->pixfmt.sizeimage; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); | 
					
						
							|  |  |  | 	frsz = PAGE_ALIGN(frsz); | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	if (count >= GSPCA_MAX_FRAMES) | 
					
						
							|  |  |  | 		count = GSPCA_MAX_FRAMES - 1; | 
					
						
							| 
									
										
										
										
											2010-06-24 05:14:22 -03:00
										 |  |  | 	gspca_dev->frbuf = vmalloc_32(frsz * count); | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 	if (!gspca_dev->frbuf) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("frame alloc failed\n"); | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-12-31 04:51:36 -03:00
										 |  |  | 	gspca_dev->capt_file = file; | 
					
						
							|  |  |  | 	gspca_dev->memory = memory; | 
					
						
							|  |  |  | 	gspca_dev->frsz = frsz; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	gspca_dev->nframes = count; | 
					
						
							|  |  |  | 	for (i = 0; i < count; i++) { | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 		frame = &gspca_dev->frame[i]; | 
					
						
							|  |  |  | 		frame->v4l2_buf.index = i; | 
					
						
							|  |  |  | 		frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 
					
						
							|  |  |  | 		frame->v4l2_buf.flags = 0; | 
					
						
							|  |  |  | 		frame->v4l2_buf.field = V4L2_FIELD_NONE; | 
					
						
							|  |  |  | 		frame->v4l2_buf.length = frsz; | 
					
						
							| 
									
										
										
										
											2010-12-31 04:51:36 -03:00
										 |  |  | 		frame->v4l2_buf.memory = memory; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 		frame->v4l2_buf.sequence = 0; | 
					
						
							| 
									
										
										
										
											2010-06-27 03:08:19 -03:00
										 |  |  | 		frame->data = gspca_dev->frbuf + i * frsz; | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 		frame->v4l2_buf.m.offset = i * frsz; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	atomic_set(&gspca_dev->fr_q, 0); | 
					
						
							|  |  |  | 	atomic_set(&gspca_dev->fr_i, 0); | 
					
						
							|  |  |  | 	gspca_dev->fr_o = 0; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void frame_free(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	PDEBUG(D_STREAM, "frame free"); | 
					
						
							| 
									
										
										
										
											2008-07-05 06:12:47 -03:00
										 |  |  | 	if (gspca_dev->frbuf != NULL) { | 
					
						
							| 
									
										
										
										
											2010-06-24 05:14:22 -03:00
										 |  |  | 		vfree(gspca_dev->frbuf); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		gspca_dev->frbuf = NULL; | 
					
						
							|  |  |  | 		for (i = 0; i < gspca_dev->nframes; i++) | 
					
						
							|  |  |  | 			gspca_dev->frame[i].data = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gspca_dev->nframes = 0; | 
					
						
							| 
									
										
										
										
											2010-12-31 04:51:36 -03:00
										 |  |  | 	gspca_dev->frsz = 0; | 
					
						
							|  |  |  | 	gspca_dev->capt_file = NULL; | 
					
						
							|  |  |  | 	gspca_dev->memory = GSPCA_MEMORY_NO; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | static void destroy_urbs(struct gspca_dev *gspca_dev) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct urb *urb; | 
					
						
							|  |  |  | 	unsigned int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	PDEBUG(D_STREAM, "kill transfer"); | 
					
						
							| 
									
										
										
										
											2008-10-13 15:52:46 -03:00
										 |  |  | 	for (i = 0; i < MAX_NURBS; i++) { | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		urb = gspca_dev->urb[i]; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		if (urb == NULL) | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		gspca_dev->urb[i] = NULL; | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 		usb_kill_urb(urb); | 
					
						
							| 
									
										
										
										
											2013-03-20 11:30:46 -03:00
										 |  |  | 		usb_free_coherent(gspca_dev->dev, | 
					
						
							|  |  |  | 				  urb->transfer_buffer_length, | 
					
						
							|  |  |  | 				  urb->transfer_buffer, | 
					
						
							|  |  |  | 				  urb->transfer_dma); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		usb_free_urb(urb); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 06:11:36 -03:00
										 |  |  | static int gspca_set_alt0(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gspca_dev->alt == 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("set alt 0 err %d\n", ret); | 
					
						
							| 
									
										
										
										
											2010-04-09 06:11:36 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Note: both the queue and the usb locks should be held when calling this */ | 
					
						
							|  |  |  | static void gspca_stream_off(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	gspca_dev->streaming = 0; | 
					
						
							| 
									
										
										
										
											2012-05-07 06:44:21 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2012-05-07 06:25:30 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->stopN) | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->stopN(gspca_dev); | 
					
						
							|  |  |  | 	destroy_urbs(gspca_dev); | 
					
						
							|  |  |  | 	gspca_input_destroy_urb(gspca_dev); | 
					
						
							|  |  |  | 	gspca_set_alt0(gspca_dev); | 
					
						
							|  |  |  | 	gspca_input_create_urb(gspca_dev); | 
					
						
							| 
									
										
										
										
											2010-04-09 06:11:36 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->stop0) | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->stop0(gspca_dev); | 
					
						
							|  |  |  | 	PDEBUG(D_STREAM, "stream off OK"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2008-09-26 07:43:54 -03:00
										 |  |  |  * look for an input transfer endpoint in an alternate setting | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, | 
					
						
							| 
									
										
										
										
											2009-04-21 13:45:56 -03:00
										 |  |  | 					  int xfer) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct usb_host_endpoint *ep; | 
					
						
							|  |  |  | 	int i, attr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < alt->desc.bNumEndpoints; i++) { | 
					
						
							|  |  |  | 		ep = &alt->endpoint[i]; | 
					
						
							| 
									
										
										
										
											2008-12-31 08:13:46 -03:00
										 |  |  | 		attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; | 
					
						
							| 
									
										
										
										
											2009-04-21 13:57:31 -03:00
										 |  |  | 		if (attr == xfer | 
					
						
							| 
									
										
										
										
											2011-03-21 16:58:56 -03:00
										 |  |  | 		    && ep->desc.wMaxPacketSize != 0 | 
					
						
							|  |  |  | 		    && usb_endpoint_dir_in(&ep->desc)) | 
					
						
							| 
									
										
										
										
											2008-12-31 08:13:46 -03:00
										 |  |  | 			return ep; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | /* compute the minimum bandwidth for the current transfer */ | 
					
						
							|  |  |  | static u32 which_bandwidth(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 bandwidth; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-04 13:44:02 -03:00
										 |  |  | 	/* get the (max) image size */ | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 	bandwidth = gspca_dev->pixfmt.sizeimage; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-04 13:44:02 -03:00
										 |  |  | 	/* if the image is compressed, estimate its mean size */ | 
					
						
							| 
									
										
										
										
											2012-01-01 16:35:01 -03:00
										 |  |  | 	if (!gspca_dev->cam.needs_full_bandwidth && | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 	    bandwidth < gspca_dev->pixfmt.width * | 
					
						
							|  |  |  | 				gspca_dev->pixfmt.height) | 
					
						
							| 
									
										
										
										
											2012-01-04 13:44:02 -03:00
										 |  |  | 		bandwidth = bandwidth * 3 / 8;	/* 0.375 */ | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* estimate the frame rate */ | 
					
						
							|  |  |  | 	if (gspca_dev->sd_desc->get_streamparm) { | 
					
						
							|  |  |  | 		struct v4l2_streamparm parm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm); | 
					
						
							|  |  |  | 		bandwidth *= parm.parm.capture.timeperframe.denominator; | 
					
						
							| 
									
										
										
										
											2011-12-30 09:13:07 -03:00
										 |  |  | 		bandwidth /= parm.parm.capture.timeperframe.numerator; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2012-01-04 13:44:02 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* don't hope more than 15 fps with USB 1.1 and
 | 
					
						
							|  |  |  | 		 * image resolution >= 640x480 */ | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 		if (gspca_dev->pixfmt.width >= 640 | 
					
						
							| 
									
										
										
										
											2012-01-04 13:44:02 -03:00
										 |  |  | 		 && gspca_dev->dev->speed == USB_SPEED_FULL) | 
					
						
							|  |  |  | 			bandwidth *= 15;		/* 15 fps */ | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			bandwidth *= 30;		/* 30 fps */ | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth); | 
					
						
							|  |  |  | 	return bandwidth; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* endpoint table */ | 
					
						
							|  |  |  | #define MAX_ALT 16
 | 
					
						
							|  |  |  | struct ep_tb_s { | 
					
						
							|  |  |  | 	u32 alt; | 
					
						
							|  |  |  | 	u32 bandwidth; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  |  * build the table of the endpoints | 
					
						
							|  |  |  |  * and compute the minimum bandwidth for the image transfer | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-12-30 07:58:10 -03:00
										 |  |  | static int build_isoc_ep_tb(struct gspca_dev *gspca_dev, | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			struct usb_interface *intf, | 
					
						
							|  |  |  | 			struct ep_tb_s *ep_tb) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct usb_host_endpoint *ep; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	int i, j, nbalt, psize, found; | 
					
						
							|  |  |  | 	u32 bandwidth, last_bw; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	nbalt = intf->num_altsetting; | 
					
						
							|  |  |  | 	if (nbalt > MAX_ALT) | 
					
						
							|  |  |  | 		nbalt = MAX_ALT;	/* fixme: should warn */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* build the endpoint table */ | 
					
						
							|  |  |  | 	i = 0; | 
					
						
							|  |  |  | 	last_bw = 0; | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		ep_tb->bandwidth = 2000 * 2000 * 120; | 
					
						
							|  |  |  | 		found = 0; | 
					
						
							|  |  |  | 		for (j = 0; j < nbalt; j++) { | 
					
						
							| 
									
										
										
										
											2011-12-30 07:58:10 -03:00
										 |  |  | 			ep = alt_xfer(&intf->altsetting[j], | 
					
						
							|  |  |  | 				      USB_ENDPOINT_XFER_ISOC); | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			if (ep == NULL) | 
					
						
							|  |  |  | 				continue; | 
					
						
							| 
									
										
										
										
											2011-12-30 08:20:50 -03:00
										 |  |  | 			if (ep->desc.bInterval == 0) { | 
					
						
							|  |  |  | 				pr_err("alt %d iso endp with 0 interval\n", j); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			psize = le16_to_cpu(ep->desc.wMaxPacketSize); | 
					
						
							| 
									
										
										
										
											2011-12-30 07:58:10 -03:00
										 |  |  | 			psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); | 
					
						
							| 
									
										
										
										
											2011-12-30 08:20:50 -03:00
										 |  |  | 			bandwidth = psize * 1000; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			if (gspca_dev->dev->speed == USB_SPEED_HIGH | 
					
						
							|  |  |  | 			 || gspca_dev->dev->speed == USB_SPEED_SUPER) | 
					
						
							|  |  |  | 				bandwidth *= 8; | 
					
						
							| 
									
										
										
										
											2011-12-30 08:20:50 -03:00
										 |  |  | 			bandwidth /= 1 << (ep->desc.bInterval - 1); | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			if (bandwidth <= last_bw) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			if (bandwidth < ep_tb->bandwidth) { | 
					
						
							|  |  |  | 				ep_tb->bandwidth = bandwidth; | 
					
						
							|  |  |  | 				ep_tb->alt = j; | 
					
						
							|  |  |  | 				found = 1; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-09-29 05:57:32 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 		if (!found) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		PDEBUG(D_STREAM, "alt %d bandwidth %d", | 
					
						
							|  |  |  | 				ep_tb->alt, ep_tb->bandwidth); | 
					
						
							|  |  |  | 		last_bw = ep_tb->bandwidth; | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 		ep_tb++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-30 18:26:53 -03:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If the camera: | 
					
						
							|  |  |  | 	 * has a usb audio class interface (a built in usb mic); and | 
					
						
							|  |  |  | 	 * is a usb 1 full speed device; and | 
					
						
							|  |  |  | 	 * uses the max full speed iso bandwidth; and | 
					
						
							|  |  |  | 	 * and has more than 1 alt setting | 
					
						
							|  |  |  | 	 * then skip the highest alt setting to spare bandwidth for the mic | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (gspca_dev->audio && | 
					
						
							|  |  |  | 			gspca_dev->dev->speed == USB_SPEED_FULL && | 
					
						
							|  |  |  | 			last_bw >= 1000000 && | 
					
						
							|  |  |  | 			i > 1) { | 
					
						
							|  |  |  | 		PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt"); | 
					
						
							|  |  |  | 		i--; | 
					
						
							|  |  |  | 		ep_tb--; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	/* get the requested bandwidth and start at the highest atlsetting */ | 
					
						
							|  |  |  | 	bandwidth = which_bandwidth(gspca_dev); | 
					
						
							|  |  |  | 	ep_tb--; | 
					
						
							|  |  |  | 	while (i > 1) { | 
					
						
							|  |  |  | 		ep_tb--; | 
					
						
							|  |  |  | 		if (ep_tb->bandwidth < bandwidth) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		i--; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	return i; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  |  * create the URBs for image transfer | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int create_urbs(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			struct usb_host_endpoint *ep) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct urb *urb; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	int n, nurbs, i, psize, npkt, bsize; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* calculate the packet size and the number of packets */ | 
					
						
							|  |  |  | 	psize = le16_to_cpu(ep->desc.wMaxPacketSize); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-21 13:45:56 -03:00
										 |  |  | 	if (!gspca_dev->cam.bulk) {		/* isoc */ | 
					
						
							| 
									
										
										
										
											2008-09-29 05:57:32 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 		if (gspca_dev->pkt_size == 0) | 
					
						
							|  |  |  | 			psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			psize = gspca_dev->pkt_size; | 
					
						
							| 
									
										
										
										
											2009-04-25 13:29:01 -03:00
										 |  |  | 		npkt = gspca_dev->cam.npkt; | 
					
						
							|  |  |  | 		if (npkt == 0) | 
					
						
							|  |  |  | 			npkt = 32;		/* default value */ | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 		bsize = psize * npkt; | 
					
						
							|  |  |  | 		PDEBUG(D_STREAM, | 
					
						
							|  |  |  | 			"isoc %d pkts size %d = bsize:%d", | 
					
						
							|  |  |  | 			npkt, psize, bsize); | 
					
						
							| 
									
										
										
										
											2008-09-08 03:22:42 -03:00
										 |  |  | 		nurbs = DEF_NURBS; | 
					
						
							| 
									
										
										
										
											2008-09-29 05:57:32 -03:00
										 |  |  | 	} else {				/* bulk */ | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 		npkt = 0; | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 		bsize = gspca_dev->cam.bulk_size; | 
					
						
							| 
									
										
										
										
											2008-09-29 05:57:32 -03:00
										 |  |  | 		if (bsize == 0) | 
					
						
							|  |  |  | 			bsize = psize; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 		PDEBUG(D_STREAM, "bulk bsize:%d", bsize); | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 		if (gspca_dev->cam.bulk_nurbs != 0) | 
					
						
							|  |  |  | 			nurbs = gspca_dev->cam.bulk_nurbs; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			nurbs = 1; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	for (n = 0; n < nurbs; n++) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		urb = usb_alloc_urb(npkt, GFP_KERNEL); | 
					
						
							|  |  |  | 		if (!urb) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("usb_alloc_urb failed\n"); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-12-15 05:16:04 -03:00
										 |  |  | 		gspca_dev->urb[n] = urb; | 
					
						
							| 
									
										
										
										
											2010-04-12 13:17:25 +02:00
										 |  |  | 		urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 						bsize, | 
					
						
							|  |  |  | 						GFP_KERNEL, | 
					
						
							|  |  |  | 						&urb->transfer_dma); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		if (urb->transfer_buffer == NULL) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("usb_alloc_coherent failed\n"); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		urb->dev = gspca_dev->dev; | 
					
						
							|  |  |  | 		urb->context = gspca_dev; | 
					
						
							|  |  |  | 		urb->transfer_buffer_length = bsize; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 		if (npkt != 0) {		/* ISOC */ | 
					
						
							|  |  |  | 			urb->pipe = usb_rcvisocpipe(gspca_dev->dev, | 
					
						
							|  |  |  | 						    ep->desc.bEndpointAddress); | 
					
						
							| 
									
										
										
										
											2008-09-28 07:43:00 -03:00
										 |  |  | 			urb->transfer_flags = URB_ISO_ASAP | 
					
						
							|  |  |  | 					| URB_NO_TRANSFER_DMA_MAP; | 
					
						
							| 
									
										
										
										
											2012-01-01 13:34:58 -03:00
										 |  |  | 			urb->interval = 1 << (ep->desc.bInterval - 1); | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 			urb->complete = isoc_irq; | 
					
						
							|  |  |  | 			urb->number_of_packets = npkt; | 
					
						
							|  |  |  | 			for (i = 0; i < npkt; i++) { | 
					
						
							|  |  |  | 				urb->iso_frame_desc[i].length = psize; | 
					
						
							|  |  |  | 				urb->iso_frame_desc[i].offset = psize * i; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else {		/* bulk */ | 
					
						
							|  |  |  | 			urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, | 
					
						
							| 
									
										
										
										
											2010-10-29 07:57:03 -03:00
										 |  |  | 						ep->desc.bEndpointAddress); | 
					
						
							| 
									
										
										
										
											2008-09-28 07:43:00 -03:00
										 |  |  | 			urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; | 
					
						
							| 
									
										
										
										
											2008-09-04 07:01:50 -03:00
										 |  |  | 			urb->complete = bulk_irq; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * start the USB transfer | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int gspca_init_transfer(struct gspca_dev *gspca_dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	struct usb_interface *intf; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	struct usb_host_endpoint *ep; | 
					
						
							| 
									
										
										
										
											2009-12-15 05:16:04 -03:00
										 |  |  | 	struct urb *urb; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	struct ep_tb_s ep_tb[MAX_ALT]; | 
					
						
							|  |  |  | 	int n, ret, xfer, alt, alt_idx; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	/* reset the streaming variables */ | 
					
						
							|  |  |  | 	gspca_dev->image = NULL; | 
					
						
							|  |  |  | 	gspca_dev->image_len = 0; | 
					
						
							|  |  |  | 	gspca_dev->last_packet_type = DISCARD_PACKET; | 
					
						
							|  |  |  | 	gspca_dev->sequence = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 15:34:27 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	/* do the specific subdriver stuff before endpoint selection */ | 
					
						
							| 
									
										
										
											
												[media] gspca: Fix bulk mode cameras no longer working (regression fix)
The new iso bandwidth calculation code accidentally has broken support
for bulk mode cameras. This has broken the following drivers:
finepix, jeilinj, ovfx2, ov534, ov534_9, se401, sq905, sq905c, sq930x,
stv0680, vicam.
Thix patch fixes this. Fix tested with: se401, sq905, sq905c, stv0680 &
vicam cams.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
											
										 
											2011-12-29 16:43:12 -03:00
										 |  |  | 	intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); | 
					
						
							|  |  |  | 	gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0; | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->isoc_init) { | 
					
						
							|  |  |  | 		ret = gspca_dev->sd_desc->isoc_init(gspca_dev); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 			return ret; | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK | 
					
						
							|  |  |  | 				   : USB_ENDPOINT_XFER_ISOC; | 
					
						
							| 
									
										
										
										
											2010-10-26 05:15:46 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-30 07:58:10 -03:00
										 |  |  | 	/* if bulk or the subdriver forced an altsetting, get the endpoint */ | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 	if (gspca_dev->alt != 0) { | 
					
						
							|  |  |  | 		gspca_dev->alt--;	/* (previous version compatibility) */ | 
					
						
							|  |  |  | 		ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); | 
					
						
							|  |  |  | 		if (ep == NULL) { | 
					
						
							|  |  |  | 			pr_err("bad altsetting %d\n", gspca_dev->alt); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 			return -EIO; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		ep_tb[0].alt = gspca_dev->alt; | 
					
						
							|  |  |  | 		alt_idx = 1; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* else, compute the minimum bandwidth
 | 
					
						
							|  |  |  | 	 * and build the endpoint table */ | 
					
						
							| 
									
										
										
										
											2011-12-30 07:58:10 -03:00
										 |  |  | 		alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 		if (alt_idx <= 0) { | 
					
						
							|  |  |  | 			pr_err("no transfer endpoint found\n"); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 			return -EIO; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* set the highest alternate setting and
 | 
					
						
							|  |  |  | 	 * loop until urb submit succeeds */ | 
					
						
							|  |  |  | 	gspca_input_destroy_urb(gspca_dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gspca_dev->alt = ep_tb[--alt_idx].alt; | 
					
						
							|  |  |  | 	alt = -1; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	for (;;) { | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 		if (alt != gspca_dev->alt) { | 
					
						
							|  |  |  | 			alt = gspca_dev->alt; | 
					
						
							| 
									
										
										
										
											2011-11-30 05:54:16 -03:00
										 |  |  | 			if (intf->num_altsetting > 1) { | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 				ret = usb_set_interface(gspca_dev->dev, | 
					
						
							|  |  |  | 							gspca_dev->iface, | 
					
						
							|  |  |  | 							alt); | 
					
						
							|  |  |  | 				if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2011-08-10 07:40:47 -03:00
										 |  |  | 					if (ret == -ENOSPC) | 
					
						
							|  |  |  | 						goto retry; /*fixme: ugly*/ | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 					pr_err("set alt %d err %d\n", alt, ret); | 
					
						
							|  |  |  | 					goto out; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-12-15 05:23:04 -03:00
										 |  |  | 		if (!gspca_dev->cam.no_urb_create) { | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			PDEBUG(D_STREAM, "init transfer alt %d", alt); | 
					
						
							|  |  |  | 			ret = create_urbs(gspca_dev, | 
					
						
							|  |  |  | 				alt_xfer(&intf->altsetting[alt], xfer)); | 
					
						
							| 
									
										
										
										
											2009-12-15 05:23:04 -03:00
										 |  |  | 			if (ret < 0) { | 
					
						
							|  |  |  | 				destroy_urbs(gspca_dev); | 
					
						
							|  |  |  | 				goto out; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-12-15 05:16:04 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-22 14:51:56 -03:00
										 |  |  | 		/* clear the bulk endpoint */ | 
					
						
							| 
									
										
										
										
											2009-04-21 13:45:56 -03:00
										 |  |  | 		if (gspca_dev->cam.bulk) | 
					
						
							| 
									
										
										
										
											2008-10-22 14:51:56 -03:00
										 |  |  | 			usb_clear_halt(gspca_dev->dev, | 
					
						
							| 
									
										
										
										
											2008-12-31 08:13:46 -03:00
										 |  |  | 					gspca_dev->urb[0]->pipe); | 
					
						
							| 
									
										
										
										
											2008-10-22 14:51:56 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		/* start the cam */ | 
					
						
							| 
									
										
										
										
											2008-09-20 06:39:08 -03:00
										 |  |  | 		ret = gspca_dev->sd_desc->start(gspca_dev); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			destroy_urbs(gspca_dev); | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		gspca_dev->streaming = 1; | 
					
						
							| 
									
										
										
										
											2012-06-15 05:24:26 -03:00
										 |  |  | 		v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-14 07:53:32 -03:00
										 |  |  | 		/* some bulk transfers are started by the subdriver */ | 
					
						
							| 
									
										
										
										
											2009-04-21 13:45:56 -03:00
										 |  |  | 		if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) | 
					
						
							| 
									
										
										
										
											2008-09-08 03:22:42 -03:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		/* submit the URBs */ | 
					
						
							| 
									
										
										
										
											2009-12-15 05:16:04 -03:00
										 |  |  | 		for (n = 0; n < MAX_NURBS; n++) { | 
					
						
							|  |  |  | 			urb = gspca_dev->urb[n]; | 
					
						
							|  |  |  | 			if (urb == NULL) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			ret = usb_submit_urb(urb, GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if (ret >= 0) | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			break;			/* transfer is started */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* something when wrong
 | 
					
						
							|  |  |  | 		 * stop the webcam and free the transfer resources */ | 
					
						
							| 
									
										
										
										
											2010-04-09 06:11:36 -03:00
										 |  |  | 		gspca_stream_off(gspca_dev); | 
					
						
							| 
									
										
										
										
											2009-11-10 14:49:43 -03:00
										 |  |  | 		if (ret != -ENOSPC) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 			pr_err("usb_submit_urb alt %d err %d\n", | 
					
						
							|  |  |  | 			       gspca_dev->alt, ret); | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 			goto out; | 
					
						
							| 
									
										
										
										
											2009-11-10 14:49:43 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* the bandwidth is not wide enough
 | 
					
						
							| 
									
										
										
										
											2011-03-30 22:57:33 -03:00
										 |  |  | 		 * negotiate or try a lower alternate setting */ | 
					
						
							| 
									
										
										
										
											2011-08-10 07:40:47 -03:00
										 |  |  | retry: | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PERR("alt %d - bandwidth not wide enough, trying again", alt); | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 		msleep(20);	/* wait for kill complete */ | 
					
						
							|  |  |  | 		if (gspca_dev->sd_desc->isoc_nego) { | 
					
						
							|  |  |  | 			ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				goto out; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2011-08-10 07:38:48 -03:00
										 |  |  | 			if (alt_idx <= 0) { | 
					
						
							|  |  |  | 				pr_err("no transfer endpoint found\n"); | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 				ret = -EIO; | 
					
						
							|  |  |  | 				goto out; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2011-12-31 11:32:03 -02:00
										 |  |  | 			gspca_dev->alt = ep_tb[--alt_idx].alt; | 
					
						
							| 
									
										
										
										
											2009-06-30 07:07:01 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | out: | 
					
						
							| 
									
										
										
										
											2010-10-26 05:15:46 -03:00
										 |  |  | 	gspca_input_create_urb(gspca_dev); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | static void gspca_set_default_mode(struct gspca_dev *gspca_dev) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i = gspca_dev->cam.nmodes - 1;	/* take the highest mode */ | 
					
						
							|  |  |  | 	gspca_dev->curr_mode = i; | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 	gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i]; | 
					
						
							| 
									
										
										
										
											2010-10-02 04:12:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	/* does nothing if ctrl_handler == NULL */ | 
					
						
							|  |  |  | 	v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int wxh_to_mode(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			int width, int height) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	for (i = gspca_dev->cam.nmodes; --i > 0; ) { | 
					
						
							|  |  |  | 		if (width >= gspca_dev->cam.cam_mode[i].width | 
					
						
							|  |  |  | 		    && height >= gspca_dev->cam.cam_mode[i].height) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return i; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * search a mode with the right pixel format | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int gspca_get_mode(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			int mode, | 
					
						
							|  |  |  | 			int pixfmt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int modeU, modeD; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	modeU = modeD = mode; | 
					
						
							|  |  |  | 	while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) { | 
					
						
							|  |  |  | 		if (--modeD >= 0) { | 
					
						
							| 
									
										
										
										
											2008-07-05 11:49:20 -03:00
										 |  |  | 			if (gspca_dev->cam.cam_mode[modeD].pixelformat | 
					
						
							|  |  |  | 								== pixfmt) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 				return modeD; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (++modeU < gspca_dev->cam.nmodes) { | 
					
						
							| 
									
										
										
										
											2008-07-05 11:49:20 -03:00
										 |  |  | 			if (gspca_dev->cam.cam_mode[modeU].pixelformat | 
					
						
							|  |  |  | 								== pixfmt) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 				return modeU; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | #ifdef CONFIG_VIDEO_ADV_DEBUG
 | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | static int vidioc_g_chip_info(struct file *file, void *priv, | 
					
						
							|  |  |  | 				struct v4l2_dbg_chip_info *chip) | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 07:14:33 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->get_chip_info) | 
					
						
							|  |  |  | 		return gspca_dev->sd_desc->get_chip_info(gspca_dev, chip); | 
					
						
							|  |  |  | 	return chip->match.addr ? -EINVAL : 0; | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | static int vidioc_g_register(struct file *file, void *priv, | 
					
						
							|  |  |  | 		struct v4l2_dbg_register *reg) | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 07:14:33 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | 	return gspca_dev->sd_desc->get_register(gspca_dev, reg); | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | static int vidioc_s_register(struct file *file, void *priv, | 
					
						
							|  |  |  | 		const struct v4l2_dbg_register *reg) | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 07:14:33 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | 	return gspca_dev->sd_desc->set_register(gspca_dev, reg); | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 				struct v4l2_fmtdesc *fmtdesc) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-06-30 15:50:11 -03:00
										 |  |  | 	int i, j, index; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	__u32 fmt_tb[8]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* give an index to each format */ | 
					
						
							|  |  |  | 	index = 0; | 
					
						
							|  |  |  | 	j = 0; | 
					
						
							|  |  |  | 	for (i = gspca_dev->cam.nmodes; --i >= 0; ) { | 
					
						
							| 
									
										
										
										
											2008-07-05 11:49:20 -03:00
										 |  |  | 		fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		j = 0; | 
					
						
							|  |  |  | 		for (;;) { | 
					
						
							|  |  |  | 			if (fmt_tb[j] == fmt_tb[index]) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			j++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (j == index) { | 
					
						
							|  |  |  | 			if (fmtdesc->index == index) | 
					
						
							|  |  |  | 				break;		/* new format */ | 
					
						
							|  |  |  | 			index++; | 
					
						
							| 
									
										
										
										
											2008-11-12 23:18:21 -03:00
										 |  |  | 			if (index >= ARRAY_SIZE(fmt_tb)) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 				return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (i < 0) | 
					
						
							|  |  |  | 		return -EINVAL;		/* no more format */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fmtdesc->pixelformat = fmt_tb[index]; | 
					
						
							| 
									
										
										
										
											2011-08-10 07:32:57 -03:00
										 |  |  | 	if (gspca_dev->cam.cam_mode[i].sizeimage < | 
					
						
							|  |  |  | 			gspca_dev->cam.cam_mode[i].width * | 
					
						
							|  |  |  | 				gspca_dev->cam.cam_mode[i].height) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; | 
					
						
							|  |  |  | 	fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; | 
					
						
							|  |  |  | 	fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; | 
					
						
							|  |  |  | 	fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; | 
					
						
							|  |  |  | 	fmtdesc->description[3] = fmtdesc->pixelformat >> 24; | 
					
						
							|  |  |  | 	fmtdesc->description[4] = '\0'; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			    struct v4l2_format *fmt) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 	fmt->fmt.pix = gspca_dev->pixfmt; | 
					
						
							| 
									
										
										
										
											2012-05-23 17:17:12 -03:00
										 |  |  | 	/* some drivers use priv internally, zero it before giving it to
 | 
					
						
							|  |  |  | 	   userspace */ | 
					
						
							|  |  |  | 	fmt->fmt.pix.priv = 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			struct v4l2_format *fmt) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-07-04 17:55:18 -03:00
										 |  |  | 	int w, h, mode, mode2; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	w = fmt->fmt.pix.width; | 
					
						
							|  |  |  | 	h = fmt->fmt.pix.height; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 	PDEBUG_MODE(gspca_dev, D_CONF, "try fmt cap", | 
					
						
							|  |  |  | 		    fmt->fmt.pix.pixelformat, w, h); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	/* search the closest mode for width and height */ | 
					
						
							|  |  |  | 	mode = wxh_to_mode(gspca_dev, w, h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* OK if right palette */ | 
					
						
							| 
									
										
										
										
											2008-07-05 11:49:20 -03:00
										 |  |  | 	if (gspca_dev->cam.cam_mode[mode].pixelformat | 
					
						
							|  |  |  | 						!= fmt->fmt.pix.pixelformat) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* else, search the closest mode with the same pixel format */ | 
					
						
							|  |  |  | 		mode2 = gspca_get_mode(gspca_dev, mode, | 
					
						
							|  |  |  | 					fmt->fmt.pix.pixelformat); | 
					
						
							| 
									
										
										
										
											2008-07-05 11:49:20 -03:00
										 |  |  | 		if (mode2 >= 0) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			mode = mode2; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-05-23 17:17:12 -03:00
										 |  |  | 	fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:24 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->try_fmt) { | 
					
						
							|  |  |  | 		/* pass original resolution to subdriver try_fmt */ | 
					
						
							|  |  |  | 		fmt->fmt.pix.width = w; | 
					
						
							|  |  |  | 		fmt->fmt.pix.height = h; | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->try_fmt(gspca_dev, fmt); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-05-23 17:17:12 -03:00
										 |  |  | 	/* some drivers use priv internally, zero it before giving it to
 | 
					
						
							|  |  |  | 	   userspace */ | 
					
						
							|  |  |  | 	fmt->fmt.pix.priv = 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return mode;			/* used when s_fmt */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | static int vidioc_try_fmt_vid_cap(struct file *file, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			      void *priv, | 
					
						
							|  |  |  | 			      struct v4l2_format *fmt) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | 	ret = try_fmt_vid_cap(gspca_dev, fmt); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			    struct v4l2_format *fmt) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | 	ret = try_fmt_vid_cap(gspca_dev, fmt); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	if (gspca_dev->nframes != 0 | 
					
						
							|  |  |  | 	    && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gspca_dev->streaming) { | 
					
						
							|  |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	gspca_dev->curr_mode = ret; | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:24 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->try_fmt) | 
					
						
							|  |  |  | 		/* subdriver try_fmt can modify format parameters */ | 
					
						
							|  |  |  | 		gspca_dev->pixfmt = fmt->fmt.pix; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret]; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-25 15:04:22 -03:00
										 |  |  | static int vidioc_enum_framesizes(struct file *file, void *priv, | 
					
						
							|  |  |  | 				  struct v4l2_frmsizeenum *fsize) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2009-05-25 15:04:22 -03:00
										 |  |  | 	int i; | 
					
						
							|  |  |  | 	__u32 index = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:24 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->enum_framesizes) | 
					
						
							|  |  |  | 		return gspca_dev->sd_desc->enum_framesizes(gspca_dev, fsize); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-25 15:04:22 -03:00
										 |  |  | 	for (i = 0; i < gspca_dev->cam.nmodes; i++) { | 
					
						
							|  |  |  | 		if (fsize->pixel_format != | 
					
						
							|  |  |  | 				gspca_dev->cam.cam_mode[i].pixelformat) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (fsize->index == index) { | 
					
						
							|  |  |  | 			fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | 
					
						
							|  |  |  | 			fsize->discrete.width = | 
					
						
							|  |  |  | 				gspca_dev->cam.cam_mode[i].width; | 
					
						
							|  |  |  | 			fsize->discrete.height = | 
					
						
							|  |  |  | 				gspca_dev->cam.cam_mode[i].height; | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		index++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 06:18:46 -03:00
										 |  |  | static int vidioc_enum_frameintervals(struct file *filp, void *priv, | 
					
						
							|  |  |  | 				      struct v4l2_frmivalenum *fival) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(filp); | 
					
						
							| 
									
										
										
										
											2009-12-02 06:18:46 -03:00
										 |  |  | 	int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); | 
					
						
							|  |  |  | 	__u32 i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gspca_dev->cam.mode_framerates == NULL || | 
					
						
							|  |  |  | 			gspca_dev->cam.mode_framerates[mode].nrates == 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fival->pixel_format != | 
					
						
							|  |  |  | 			gspca_dev->cam.cam_mode[mode].pixelformat) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) { | 
					
						
							|  |  |  | 		if (fival->index == i) { | 
					
						
							|  |  |  | 			fival->type = V4L2_FRMSIZE_TYPE_DISCRETE; | 
					
						
							|  |  |  | 			fival->discrete.numerator = 1; | 
					
						
							|  |  |  | 			fival->discrete.denominator = | 
					
						
							|  |  |  | 				gspca_dev->cam.mode_framerates[mode].rates[i]; | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | static void gspca_release(struct v4l2_device *v4l2_device) | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = | 
					
						
							|  |  |  | 		container_of(v4l2_device, struct gspca_dev, v4l2_dev); | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | 	v4l2_device_unregister(&gspca_dev->v4l2_dev); | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | 	kfree(gspca_dev->usb_buf); | 
					
						
							|  |  |  | 	kfree(gspca_dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-30 06:58:20 -03:00
										 |  |  | static int dev_open(struct file *file) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2013-08-05 17:16:37 -03:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-01 13:44:49 -03:00
										 |  |  | 	PDEBUG(D_STREAM, "[%s] open", current->comm); | 
					
						
							| 
									
										
										
										
											2008-11-18 15:52:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* protect the subdriver against rmmod */ | 
					
						
							| 
									
										
										
										
											2010-12-30 20:20:09 -03:00
										 |  |  | 	if (!try_module_get(gspca_dev->module)) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-05 17:16:37 -03:00
										 |  |  | 	ret = v4l2_fh_open(file); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		module_put(gspca_dev->module); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-30 06:58:20 -03:00
										 |  |  | static int dev_close(struct file *file) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-01 13:44:49 -03:00
										 |  |  | 	PDEBUG(D_STREAM, "[%s] close", current->comm); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Needed for gspca_stream_off, always lock before queue_lock! */ | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&gspca_dev->usb_lock)) | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { | 
					
						
							|  |  |  | 		mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	/* if the file did the capture, free the streaming resources */ | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	if (gspca_dev->capt_file == file) { | 
					
						
							| 
									
										
										
										
											2012-05-07 06:44:21 -03:00
										 |  |  | 		if (gspca_dev->streaming) | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:17 -03:00
										 |  |  | 			gspca_stream_off(gspca_dev); | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		frame_free(gspca_dev); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-18 15:52:31 -03:00
										 |  |  | 	module_put(gspca_dev->module); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	PDEBUG(D_STREAM, "close done"); | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | 	return v4l2_fh_release(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_querycap(struct file *file, void  *priv, | 
					
						
							|  |  |  | 			   struct v4l2_capability *cap) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-05 14:58:46 -03:00
										 |  |  | 	strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, | 
					
						
							| 
									
										
										
										
											2010-12-28 06:55:01 -03:00
										 |  |  | 			sizeof cap->driver); | 
					
						
							| 
									
										
										
										
											2008-07-23 03:24:06 -03:00
										 |  |  | 	if (gspca_dev->dev->product != NULL) { | 
					
						
							| 
									
										
										
										
											2011-06-05 14:58:46 -03:00
										 |  |  | 		strlcpy((char *) cap->card, gspca_dev->dev->product, | 
					
						
							| 
									
										
										
										
											2008-07-23 03:24:06 -03:00
										 |  |  | 			sizeof cap->card); | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2010-12-28 06:55:01 -03:00
										 |  |  | 		snprintf((char *) cap->card, sizeof cap->card, | 
					
						
							| 
									
										
										
										
											2008-07-23 03:24:06 -03:00
										 |  |  | 			"USB Camera (%04x:%04x)", | 
					
						
							|  |  |  | 			le16_to_cpu(gspca_dev->dev->descriptor.idVendor), | 
					
						
							|  |  |  | 			le16_to_cpu(gspca_dev->dev->descriptor.idProduct)); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-12-28 06:55:01 -03:00
										 |  |  | 	usb_make_path(gspca_dev->dev, (char *) cap->bus_info, | 
					
						
							|  |  |  | 			sizeof(cap->bus_info)); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:21 -03:00
										 |  |  | 	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			  | V4L2_CAP_STREAMING | 
					
						
							|  |  |  | 			  | V4L2_CAP_READWRITE; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:21 -03:00
										 |  |  | 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_enum_input(struct file *file, void *priv, | 
					
						
							|  |  |  | 				struct v4l2_input *input) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (input->index != 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	input->type = V4L2_INPUT_TYPE_CAMERA; | 
					
						
							| 
									
										
										
										
											2009-03-29 19:17:10 -03:00
										 |  |  | 	input->status = gspca_dev->cam.input_flags; | 
					
						
							| 
									
										
										
										
											2011-06-05 14:58:46 -03:00
										 |  |  | 	strlcpy(input->name, gspca_dev->sd_desc->name, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		sizeof input->name); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	*i = 0; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_s_input(struct file *file, void *priv, unsigned int i) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (i > 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	return (0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_reqbufs(struct file *file, void *priv, | 
					
						
							|  |  |  | 			  struct v4l2_requestbuffers *rb) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2010-04-09 06:07:58 -03:00
										 |  |  | 	int i, ret = 0, streaming; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-14 06:21:16 -03:00
										 |  |  | 	i = rb->memory;			/* (avoid compilation warning) */ | 
					
						
							|  |  |  | 	switch (i) { | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	case GSPCA_MEMORY_READ:			/* (internal call) */ | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	case V4L2_MEMORY_MMAP: | 
					
						
							|  |  |  | 	case V4L2_MEMORY_USERPTR: | 
					
						
							| 
									
										
										
										
											2008-06-30 15:50:11 -03:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	if (gspca_dev->memory != GSPCA_MEMORY_NO | 
					
						
							| 
									
										
										
										
											2010-12-31 07:41:54 -03:00
										 |  |  | 	    && gspca_dev->memory != GSPCA_MEMORY_READ | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	    && gspca_dev->memory != rb->memory) { | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	/* only one file may do the capture */ | 
					
						
							|  |  |  | 	if (gspca_dev->capt_file != NULL | 
					
						
							|  |  |  | 	    && gspca_dev->capt_file != file) { | 
					
						
							|  |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if allocated, the buffers must not be mapped */ | 
					
						
							|  |  |  | 	for (i = 0; i < gspca_dev->nframes; i++) { | 
					
						
							|  |  |  | 		if (gspca_dev->frame[i].vma_use_count) { | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 			ret = -EBUSY; | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* stop streaming */ | 
					
						
							| 
									
										
										
										
											2010-04-09 06:07:58 -03:00
										 |  |  | 	streaming = gspca_dev->streaming; | 
					
						
							|  |  |  | 	if (streaming) { | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 		gspca_stream_off(gspca_dev); | 
					
						
							| 
									
										
										
										
											2011-03-14 05:33:01 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Don't restart the stream when switching from read
 | 
					
						
							|  |  |  | 		 * to mmap mode */ | 
					
						
							|  |  |  | 		if (gspca_dev->memory == GSPCA_MEMORY_READ) | 
					
						
							|  |  |  | 			streaming = 0; | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* free the previous allocated buffers, if any */ | 
					
						
							| 
									
										
										
										
											2010-12-31 04:51:36 -03:00
										 |  |  | 	if (gspca_dev->nframes != 0) | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 		frame_free(gspca_dev); | 
					
						
							|  |  |  | 	if (rb->count == 0)			/* unrequest */ | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2010-12-31 04:51:36 -03:00
										 |  |  | 	ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	if (ret == 0) { | 
					
						
							|  |  |  | 		rb->count = gspca_dev->nframes; | 
					
						
							| 
									
										
										
										
											2010-04-09 06:07:58 -03:00
										 |  |  | 		if (streaming) | 
					
						
							|  |  |  | 			ret = gspca_init_transfer(gspca_dev); | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | out: | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_querybuf(struct file *file, void *priv, | 
					
						
							|  |  |  | 			   struct v4l2_buffer *v4l2_buf) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-27 14:55:20 -03:00
										 |  |  | 	if (v4l2_buf->index >= gspca_dev->nframes) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	frame = &gspca_dev->frame[v4l2_buf->index]; | 
					
						
							|  |  |  | 	memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_streamon(struct file *file, void *priv, | 
					
						
							|  |  |  | 			   enum v4l2_buf_type buf_type) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 06:06:11 -03:00
										 |  |  | 	/* check the capture file */ | 
					
						
							|  |  |  | 	if (gspca_dev->capt_file != file) { | 
					
						
							|  |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-19 15:17:25 -03:00
										 |  |  | 	if (gspca_dev->nframes == 0 | 
					
						
							|  |  |  | 	    || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!gspca_dev->streaming) { | 
					
						
							|  |  |  | 		ret = gspca_init_transfer(gspca_dev); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-30 17:54:23 -03:00
										 |  |  | 	PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK", | 
					
						
							|  |  |  | 		    gspca_dev->pixfmt.pixelformat, | 
					
						
							|  |  |  | 		    gspca_dev->pixfmt.width, gspca_dev->pixfmt.height); | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	ret = 0; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_streamoff(struct file *file, void *priv, | 
					
						
							|  |  |  | 				enum v4l2_buf_type buf_type) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2012-05-22 11:24:05 -03:00
										 |  |  | 	int i, ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2010-12-30 13:11:21 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-30 13:11:21 -03:00
										 |  |  | 	if (!gspca_dev->streaming) { | 
					
						
							|  |  |  | 		ret = 0; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 06:06:11 -03:00
										 |  |  | 	/* check the capture file */ | 
					
						
							|  |  |  | 	if (gspca_dev->capt_file != file) { | 
					
						
							|  |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	/* stop streaming */ | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	gspca_stream_off(gspca_dev); | 
					
						
							| 
									
										
										
										
											2010-12-31 17:17:51 -03:00
										 |  |  | 	/* In case another thread is waiting in dqbuf */ | 
					
						
							|  |  |  | 	wake_up_interruptible(&gspca_dev->wq); | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	/* empty the transfer queues */ | 
					
						
							| 
									
										
										
										
											2012-05-22 11:24:05 -03:00
										 |  |  | 	for (i = 0; i < gspca_dev->nframes; i++) | 
					
						
							|  |  |  | 		gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	atomic_set(&gspca_dev->fr_q, 0); | 
					
						
							|  |  |  | 	atomic_set(&gspca_dev->fr_i, 0); | 
					
						
							|  |  |  | 	gspca_dev->fr_o = 0; | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	ret = 0; | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_g_jpegcomp(struct file *file, void *priv, | 
					
						
							|  |  |  | 			struct v4l2_jpegcompression *jpegcomp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 07:14:33 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_s_jpegcomp(struct file *file, void *priv, | 
					
						
							| 
									
										
										
										
											2012-09-17 05:05:25 -03:00
										 |  |  | 			const struct v4l2_jpegcompression *jpegcomp) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 07:14:33 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_g_parm(struct file *filp, void *priv, | 
					
						
							|  |  |  | 			struct v4l2_streamparm *parm) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(filp); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	parm->parm.capture.readbuffers = gspca_dev->nbufread; | 
					
						
							| 
									
										
										
										
											2008-12-10 06:02:42 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (gspca_dev->sd_desc->get_streamparm) { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 		gspca_dev->usb_err = 0; | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); | 
					
						
							|  |  |  | 		return gspca_dev->usb_err; | 
					
						
							| 
									
										
										
										
											2008-12-10 06:02:42 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int vidioc_s_parm(struct file *filp, void *priv, | 
					
						
							|  |  |  | 			struct v4l2_streamparm *parm) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(filp); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	int n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	n = parm->parm.capture.readbuffers; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	if (n == 0 || n >= GSPCA_MAX_FRAMES) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		parm->parm.capture.readbuffers = gspca_dev->nbufread; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		gspca_dev->nbufread = n; | 
					
						
							| 
									
										
										
										
											2008-12-10 06:02:42 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (gspca_dev->sd_desc->set_streamparm) { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 		gspca_dev->usb_err = 0; | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); | 
					
						
							|  |  |  | 		return gspca_dev->usb_err; | 
					
						
							| 
									
										
										
										
											2008-12-10 06:02:42 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int dev_mmap(struct file *file, struct vm_area_struct *vma) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-07-05 06:12:47 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	struct page *page; | 
					
						
							|  |  |  | 	unsigned long addr, start, size; | 
					
						
							|  |  |  | 	int i, ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	start = vma->vm_start; | 
					
						
							|  |  |  | 	size = vma->vm_end - vma->vm_start; | 
					
						
							|  |  |  | 	PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	if (gspca_dev->capt_file != file) { | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-05 06:12:47 -03:00
										 |  |  | 	frame = NULL; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	for (i = 0; i < gspca_dev->nframes; ++i) { | 
					
						
							|  |  |  | 		if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { | 
					
						
							|  |  |  | 			PDEBUG(D_STREAM, "mmap bad memory type"); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) | 
					
						
							|  |  |  | 						== vma->vm_pgoff) { | 
					
						
							|  |  |  | 			frame = &gspca_dev->frame[i]; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-07-05 06:12:47 -03:00
										 |  |  | 	if (frame == NULL) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		PDEBUG(D_STREAM, "mmap no frame buffer found"); | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-06-24 05:19:58 -03:00
										 |  |  | 	if (size != frame->v4l2_buf.length) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		PDEBUG(D_STREAM, "mmap bad size"); | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * - VM_IO marks the area as being a mmaped region for I/O to a | 
					
						
							|  |  |  | 	 *   device. It also prevents the region from being core dumped. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	vma->vm_flags |= VM_IO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = (unsigned long) frame->data; | 
					
						
							|  |  |  | 	while (size > 0) { | 
					
						
							|  |  |  | 		page = vmalloc_to_page((void *) addr); | 
					
						
							|  |  |  | 		ret = vm_insert_page(vma, start, page); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 			goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		start += PAGE_SIZE; | 
					
						
							|  |  |  | 		addr += PAGE_SIZE; | 
					
						
							|  |  |  | 		size -= PAGE_SIZE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-26 15:36:40 -03:00
										 |  |  | 	vma->vm_ops = &gspca_vm_ops; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	vma->vm_private_data = frame; | 
					
						
							|  |  |  | 	gspca_vm_open(vma); | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 	ret = 0; | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | out: | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, | 
					
						
							|  |  |  | 				enum v4l2_memory memory) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!gspca_dev->present) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	if (gspca_dev->capt_file != file || gspca_dev->memory != memory || | 
					
						
							|  |  |  | 			!gspca_dev->streaming) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check if a frame is ready */ | 
					
						
							|  |  |  | 	return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, | 
					
						
							|  |  |  | 			enum v4l2_memory memory) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 	ret = frame_ready_nolock(gspca_dev, file, memory); | 
					
						
							|  |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  |  * dequeue a video buffer | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  |  * If nonblock_ing is false, block until a buffer is available. | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | static int vidioc_dqbuf(struct file *file, void *priv, | 
					
						
							|  |  |  | 			struct v4l2_buffer *v4l2_buf) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							|  |  |  | 	int i, j, ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 	PDEBUG(D_FRAM, "dqbuf"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		if (ret > 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (file->f_flags & O_NONBLOCK) | 
					
						
							| 
									
										
										
										
											2008-11-19 17:16:26 -03:00
										 |  |  | 			return -EAGAIN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* wait till a frame is ready */ | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 		ret = wait_event_interruptible_timeout(gspca_dev->wq, | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 			frame_ready(gspca_dev, file, v4l2_buf->memory), | 
					
						
							| 
									
										
										
										
											2008-11-19 17:16:26 -03:00
										 |  |  | 			msecs_to_jiffies(3000)); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 		if (ret == 0) | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 			return -EIO; | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 			return -ERESTARTSYS; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-11-19 17:16:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 	i = gspca_dev->fr_o; | 
					
						
							|  |  |  | 	j = gspca_dev->fr_queue[i]; | 
					
						
							|  |  |  | 	frame = &gspca_dev->frame[j]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-30 20:00:17 -03:00
										 |  |  | 	frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; | 
					
						
							|  |  |  | 	memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); | 
					
						
							|  |  |  | 	PDEBUG(D_FRAM, "dqbuf %d", j); | 
					
						
							|  |  |  | 	ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 	if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { | 
					
						
							| 
									
										
										
										
											2008-09-03 17:11:54 -03:00
										 |  |  | 		if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 				 frame->data, | 
					
						
							|  |  |  | 				 frame->v4l2_buf.bytesused)) { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 			PERR("dqbuf cp to user failed"); | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 			ret = -EFAULT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | out: | 
					
						
							| 
									
										
										
										
											2010-12-30 19:54:33 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ret == 0 && gspca_dev->sd_desc->dq_callback) { | 
					
						
							|  |  |  | 		mutex_lock(&gspca_dev->usb_lock); | 
					
						
							|  |  |  | 		gspca_dev->usb_err = 0; | 
					
						
							|  |  |  | 		if (gspca_dev->present) | 
					
						
							|  |  |  | 			gspca_dev->sd_desc->dq_callback(gspca_dev); | 
					
						
							|  |  |  | 		mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * queue a video buffer | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Attempting to queue a buffer that has already been | 
					
						
							|  |  |  |  * queued will return -EINVAL. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int vidioc_qbuf(struct file *file, void *priv, | 
					
						
							|  |  |  | 			struct v4l2_buffer *v4l2_buf) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							|  |  |  | 	int i, index, ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	index = v4l2_buf->index; | 
					
						
							|  |  |  | 	if ((unsigned) index >= gspca_dev->nframes) { | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 		PDEBUG(D_FRAM, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			"qbuf idx %d >= %d", index, gspca_dev->nframes); | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	if (v4l2_buf->memory != gspca_dev->memory) { | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 		PDEBUG(D_FRAM, "qbuf bad memory type"); | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-23 08:09:12 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-12 09:28:04 -03:00
										 |  |  | 	frame = &gspca_dev->frame[index]; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { | 
					
						
							|  |  |  | 		PDEBUG(D_FRAM, "qbuf bad state"); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; | 
					
						
							|  |  |  | 		frame->v4l2_buf.length = v4l2_buf->length; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* put the buffer in the 'queued' queue */ | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	i = atomic_read(&gspca_dev->fr_q); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	gspca_dev->fr_queue[i] = index; | 
					
						
							| 
									
										
										
										
											2010-07-06 04:32:27 -03:00
										 |  |  | 	atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; | 
					
						
							|  |  |  | 	v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; | 
					
						
							|  |  |  | 	ret = 0; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * allocate the resources for read() | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int read_alloc(struct gspca_dev *gspca_dev, | 
					
						
							|  |  |  | 			struct file *file) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct v4l2_buffer v4l2_buf; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	int i, ret; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	PDEBUG(D_STREAM, "read alloc"); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&gspca_dev->usb_lock)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (gspca_dev->nframes == 0) { | 
					
						
							|  |  |  | 		struct v4l2_requestbuffers rb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		memset(&rb, 0, sizeof rb); | 
					
						
							|  |  |  | 		rb.count = gspca_dev->nbufread; | 
					
						
							|  |  |  | 		rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 		rb.memory = GSPCA_MEMORY_READ; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		ret = vidioc_reqbufs(file, gspca_dev, &rb); | 
					
						
							|  |  |  | 		if (ret != 0) { | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 			PDEBUG(D_STREAM, "read reqbuf err %d", ret); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 			goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		memset(&v4l2_buf, 0, sizeof v4l2_buf); | 
					
						
							|  |  |  | 		v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 		v4l2_buf.memory = GSPCA_MEMORY_READ; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		for (i = 0; i < gspca_dev->nbufread; i++) { | 
					
						
							|  |  |  | 			v4l2_buf.index = i; | 
					
						
							|  |  |  | 			ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); | 
					
						
							|  |  |  | 			if (ret != 0) { | 
					
						
							|  |  |  | 				PDEBUG(D_STREAM, "read qbuf err: %d", ret); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 				goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	/* start streaming */ | 
					
						
							|  |  |  | 	ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); | 
					
						
							|  |  |  | 	if (ret != 0) | 
					
						
							|  |  |  | 		PDEBUG(D_STREAM, "read streamon err %d", ret); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int dev_poll(struct file *file, poll_table *wait) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2012-05-07 08:19:42 -03:00
										 |  |  | 	unsigned long req_events = poll_requested_events(wait); | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	PDEBUG(D_FRAM, "poll"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-07 08:19:42 -03:00
										 |  |  | 	if (req_events & POLLPRI) | 
					
						
							|  |  |  | 		ret |= v4l2_ctrl_poll(file, wait); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-07 08:19:42 -03:00
										 |  |  | 	if (req_events & (POLLIN | POLLRDNORM)) { | 
					
						
							|  |  |  | 		/* if reqbufs is not done, the user would use read() */ | 
					
						
							|  |  |  | 		if (gspca_dev->memory == GSPCA_MEMORY_NO) { | 
					
						
							|  |  |  | 			if (read_alloc(gspca_dev, file) != 0) { | 
					
						
							|  |  |  | 				ret |= POLLERR; | 
					
						
							|  |  |  | 				goto out; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-07 08:19:42 -03:00
										 |  |  | 		poll_wait(file, &gspca_dev->wq, wait); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-07 08:19:42 -03:00
										 |  |  | 		/* check if an image has been received */ | 
					
						
							|  |  |  | 		if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { | 
					
						
							|  |  |  | 			ret |= POLLERR; | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) | 
					
						
							|  |  |  | 			ret |= POLLIN | POLLRDNORM; | 
					
						
							|  |  |  | 		mutex_unlock(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 	if (!gspca_dev->present) | 
					
						
							| 
									
										
										
										
											2012-05-07 08:19:42 -03:00
										 |  |  | 		ret |= POLLHUP; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t dev_read(struct file *file, char __user *data, | 
					
						
							|  |  |  | 		    size_t count, loff_t *ppos) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = video_drvdata(file); | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 	struct gspca_frame *frame; | 
					
						
							|  |  |  | 	struct v4l2_buffer v4l2_buf; | 
					
						
							|  |  |  | 	struct timeval timestamp; | 
					
						
							| 
									
										
										
										
											2008-07-02 09:14:56 -03:00
										 |  |  | 	int n, ret, ret2; | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-09 05:26:26 -03:00
										 |  |  | 	PDEBUG(D_FRAM, "read (%zd)", count); | 
					
						
							| 
									
										
										
										
											2010-12-31 05:05:56 -03:00
										 |  |  | 	if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 		ret = read_alloc(gspca_dev, file); | 
					
						
							|  |  |  | 		if (ret != 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	/* get a frame */ | 
					
						
							| 
									
										
										
										
											2010-03-07 06:56:56 -03:00
										 |  |  | 	timestamp = ktime_to_timeval(ktime_get()); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	timestamp.tv_sec--; | 
					
						
							| 
									
										
										
										
											2008-07-02 09:14:56 -03:00
										 |  |  | 	n = 2; | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		memset(&v4l2_buf, 0, sizeof v4l2_buf); | 
					
						
							|  |  |  | 		v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 		v4l2_buf.memory = GSPCA_MEMORY_READ; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); | 
					
						
							|  |  |  | 		if (ret != 0) { | 
					
						
							|  |  |  | 			PDEBUG(D_STREAM, "read dqbuf err %d", ret); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* if the process slept for more than 1 second,
 | 
					
						
							| 
									
										
										
										
											2008-09-26 07:43:54 -03:00
										 |  |  | 		 * get a newer frame */ | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		frame = &gspca_dev->frame[v4l2_buf.index]; | 
					
						
							| 
									
										
										
										
											2008-07-02 09:14:56 -03:00
										 |  |  | 		if (--n < 0) | 
					
						
							|  |  |  | 			break;			/* avoid infinite loop */ | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); | 
					
						
							|  |  |  | 		if (ret != 0) { | 
					
						
							|  |  |  | 			PDEBUG(D_STREAM, "read qbuf err %d", ret); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* copy the frame */ | 
					
						
							| 
									
										
										
										
											2008-07-08 06:58:15 -03:00
										 |  |  | 	if (count > frame->v4l2_buf.bytesused) | 
					
						
							|  |  |  | 		count = frame->v4l2_buf.bytesused; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	ret = copy_to_user(data, frame->data, count); | 
					
						
							|  |  |  | 	if (ret != 0) { | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		PERR("read cp to user lack %d / %zd", ret, count); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		ret = -EFAULT; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret = count; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	/* in each case, requeue the buffer */ | 
					
						
							|  |  |  | 	ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); | 
					
						
							|  |  |  | 	if (ret2 != 0) | 
					
						
							|  |  |  | 		return ret2; | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-30 06:58:20 -03:00
										 |  |  | static struct v4l2_file_operations dev_fops = { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	.owner = THIS_MODULE, | 
					
						
							|  |  |  | 	.open = dev_open, | 
					
						
							|  |  |  | 	.release = dev_close, | 
					
						
							|  |  |  | 	.read = dev_read, | 
					
						
							|  |  |  | 	.mmap = dev_mmap, | 
					
						
							| 
									
										
										
										
											2008-12-30 06:58:20 -03:00
										 |  |  | 	.unlocked_ioctl = video_ioctl2, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	.poll	= dev_poll, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-21 02:57:38 -03:00
										 |  |  | static const struct v4l2_ioctl_ops dev_ioctl_ops = { | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	.vidioc_querycap	= vidioc_querycap, | 
					
						
							|  |  |  | 	.vidioc_dqbuf		= vidioc_dqbuf, | 
					
						
							|  |  |  | 	.vidioc_qbuf		= vidioc_qbuf, | 
					
						
							| 
									
										
										
										
											2008-07-01 04:06:22 -03:00
										 |  |  | 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | 
					
						
							|  |  |  | 	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap, | 
					
						
							|  |  |  | 	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap, | 
					
						
							|  |  |  | 	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt_vid_cap, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	.vidioc_streamon	= vidioc_streamon, | 
					
						
							|  |  |  | 	.vidioc_enum_input	= vidioc_enum_input, | 
					
						
							|  |  |  | 	.vidioc_g_input		= vidioc_g_input, | 
					
						
							|  |  |  | 	.vidioc_s_input		= vidioc_s_input, | 
					
						
							|  |  |  | 	.vidioc_reqbufs		= vidioc_reqbufs, | 
					
						
							|  |  |  | 	.vidioc_querybuf	= vidioc_querybuf, | 
					
						
							|  |  |  | 	.vidioc_streamoff	= vidioc_streamoff, | 
					
						
							|  |  |  | 	.vidioc_g_jpegcomp	= vidioc_g_jpegcomp, | 
					
						
							|  |  |  | 	.vidioc_s_jpegcomp	= vidioc_s_jpegcomp, | 
					
						
							|  |  |  | 	.vidioc_g_parm		= vidioc_g_parm, | 
					
						
							|  |  |  | 	.vidioc_s_parm		= vidioc_s_parm, | 
					
						
							| 
									
										
										
										
											2009-05-25 15:04:22 -03:00
										 |  |  | 	.vidioc_enum_framesizes = vidioc_enum_framesizes, | 
					
						
							| 
									
										
										
										
											2009-12-02 06:18:46 -03:00
										 |  |  | 	.vidioc_enum_frameintervals = vidioc_enum_frameintervals, | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | #ifdef CONFIG_VIDEO_ADV_DEBUG
 | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | 	.vidioc_g_chip_info	= vidioc_g_chip_info, | 
					
						
							| 
									
										
										
										
											2009-07-19 05:29:20 -03:00
										 |  |  | 	.vidioc_g_register	= vidioc_g_register, | 
					
						
							|  |  |  | 	.vidioc_s_register	= vidioc_s_register, | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:20 -03:00
										 |  |  | 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | 
					
						
							|  |  |  | 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-14 05:33:01 -03:00
										 |  |  | static const struct video_device gspca_template = { | 
					
						
							| 
									
										
										
										
											2008-07-21 02:57:38 -03:00
										 |  |  | 	.name = "gspca main driver", | 
					
						
							|  |  |  | 	.fops = &dev_fops, | 
					
						
							|  |  |  | 	.ioctl_ops = &dev_ioctl_ops, | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	.release = video_device_release_empty, /* We use v4l2_dev.release */ | 
					
						
							| 
									
										
										
										
											2008-07-21 02:57:38 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * probe and create a new gspca device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function must be called by the sub-driver when it is | 
					
						
							|  |  |  |  * called for probing a new device. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-06-05 07:47:36 -03:00
										 |  |  | int gspca_dev_probe2(struct usb_interface *intf, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		const struct usb_device_id *id, | 
					
						
							|  |  |  | 		const struct sd_desc *sd_desc, | 
					
						
							| 
									
										
										
										
											2008-06-12 10:58:58 -03:00
										 |  |  | 		int dev_size, | 
					
						
							|  |  |  | 		struct module *module) | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct gspca_dev *gspca_dev; | 
					
						
							|  |  |  | 	struct usb_device *dev = interface_to_usbdev(intf); | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-23 05:25:28 -03:00
										 |  |  | 	pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", | 
					
						
							|  |  |  | 		sd_desc->name, id->idVendor, id->idProduct); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* create the device */ | 
					
						
							|  |  |  | 	if (dev_size < sizeof *gspca_dev) | 
					
						
							|  |  |  | 		dev_size = sizeof *gspca_dev; | 
					
						
							|  |  |  | 	gspca_dev = kzalloc(dev_size, GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2008-10-03 08:46:50 -03:00
										 |  |  | 	if (!gspca_dev) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("couldn't kzalloc gspca struct\n"); | 
					
						
							| 
									
										
										
										
											2008-10-03 08:46:50 -03:00
										 |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:19 -03:00
										 |  |  | 	gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!gspca_dev->usb_buf) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("out of memory\n"); | 
					
						
							| 
									
										
										
										
											2008-10-03 08:46:50 -03:00
										 |  |  | 		ret = -ENOMEM; | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:19 -03:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	gspca_dev->dev = dev; | 
					
						
							| 
									
										
										
										
											2010-06-05 07:47:36 -03:00
										 |  |  | 	gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; | 
					
						
							| 
									
										
										
										
											2010-07-14 06:30:18 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* check if any audio device */ | 
					
						
							| 
									
										
										
										
											2012-01-01 17:20:14 -03:00
										 |  |  | 	if (dev->actconfig->desc.bNumInterfaces != 1) { | 
					
						
							| 
									
										
										
										
											2010-07-14 06:30:18 -03:00
										 |  |  | 		int i; | 
					
						
							|  |  |  | 		struct usb_interface *intf2; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-01 17:20:14 -03:00
										 |  |  | 		for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { | 
					
						
							|  |  |  | 			intf2 = dev->actconfig->interface[i]; | 
					
						
							| 
									
										
										
										
											2010-07-14 06:30:18 -03:00
										 |  |  | 			if (intf2 != NULL | 
					
						
							|  |  |  | 			 && intf2->altsetting != NULL | 
					
						
							|  |  |  | 			 && intf2->altsetting->desc.bInterfaceClass == | 
					
						
							|  |  |  | 					 USB_CLASS_AUDIO) { | 
					
						
							|  |  |  | 				gspca_dev->audio = 1; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	gspca_dev->v4l2_dev.release = gspca_release; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | 	ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	gspca_dev->sd_desc = sd_desc; | 
					
						
							|  |  |  | 	gspca_dev->nbufread = 2; | 
					
						
							| 
									
										
										
										
											2008-10-23 07:29:51 -03:00
										 |  |  | 	gspca_dev->empty_packet = -1;	/* don't check the empty packets */ | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	gspca_dev->vdev = gspca_template; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | 	gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:18 -03:00
										 |  |  | 	video_set_drvdata(&gspca_dev->vdev, gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | 	set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	gspca_dev->module = module; | 
					
						
							|  |  |  | 	gspca_dev->present = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_init(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	gspca_dev->vdev.lock = &gspca_dev->usb_lock; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	mutex_init(&gspca_dev->queue_lock); | 
					
						
							|  |  |  | 	init_waitqueue_head(&gspca_dev->wq); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:16 -03:00
										 |  |  | 	/* configure the subdriver and initialize the USB device */ | 
					
						
							| 
									
										
										
										
											2008-11-22 04:27:34 -03:00
										 |  |  | 	ret = sd_desc->config(gspca_dev, id); | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:16 -03:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2008-11-22 04:27:34 -03:00
										 |  |  | 	ret = sd_desc->init(gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	if (sd_desc->init_controls) | 
					
						
							|  |  |  | 		ret = sd_desc->init_controls(gspca_dev); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	gspca_set_default_mode(gspca_dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-19 04:41:40 -03:00
										 |  |  | 	ret = gspca_input_connect(gspca_dev); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Don't take usb_lock for these ioctls. This improves latency if | 
					
						
							|  |  |  | 	 * usb_lock is taken for a long time, e.g. when changing a control | 
					
						
							|  |  |  | 	 * value, and a new frame is ready to be dequeued. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-05-14 11:32:48 -03:00
										 |  |  | 	v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); | 
					
						
							|  |  |  | 	v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); | 
					
						
							|  |  |  | 	v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); | 
					
						
							| 
									
										
										
										
											2012-05-23 17:17:12 -03:00
										 |  |  | #ifdef CONFIG_VIDEO_ADV_DEBUG
 | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | 	if (!gspca_dev->sd_desc->get_register) | 
					
						
							| 
									
										
										
										
											2012-05-23 17:17:12 -03:00
										 |  |  | 		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER); | 
					
						
							| 
									
										
										
										
											2013-05-29 06:59:42 -03:00
										 |  |  | 	if (!gspca_dev->sd_desc->set_register) | 
					
						
							| 
									
										
										
										
											2012-05-23 17:17:12 -03:00
										 |  |  | 		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	if (!gspca_dev->sd_desc->get_jcomp) | 
					
						
							|  |  |  | 		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP); | 
					
						
							|  |  |  | 	if (!gspca_dev->sd_desc->set_jcomp) | 
					
						
							|  |  |  | 		v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	/* init video stuff */ | 
					
						
							| 
									
										
										
										
											2008-11-19 06:37:53 -03:00
										 |  |  | 	ret = video_register_device(&gspca_dev->vdev, | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 				  VFL_TYPE_GRABBER, | 
					
						
							| 
									
										
										
										
											2009-01-13 06:07:59 -03:00
										 |  |  | 				  -1); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("video_register_device err %d\n", ret); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	usb_set_intfdata(intf, gspca_dev); | 
					
						
							| 
									
										
										
										
											2009-11-27 13:57:15 -03:00
										 |  |  | 	PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-19 04:41:40 -03:00
										 |  |  | 	gspca_input_create_urb(gspca_dev); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | out: | 
					
						
							| 
									
										
										
										
											2013-01-24 19:28:59 -03:00
										 |  |  | #if IS_ENABLED(CONFIG_INPUT)
 | 
					
						
							| 
									
										
										
										
											2010-02-19 04:41:40 -03:00
										 |  |  | 	if (gspca_dev->input_dev) | 
					
						
							|  |  |  | 		input_unregister_device(gspca_dev->input_dev); | 
					
						
							| 
									
										
										
										
											2010-02-19 05:11:09 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); | 
					
						
							| 
									
										
										
										
											2008-11-19 06:37:53 -03:00
										 |  |  | 	kfree(gspca_dev->usb_buf); | 
					
						
							|  |  |  | 	kfree(gspca_dev); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-06-05 07:47:36 -03:00
										 |  |  | EXPORT_SYMBOL(gspca_dev_probe2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* same function as the previous one, but check the interface */ | 
					
						
							|  |  |  | int gspca_dev_probe(struct usb_interface *intf, | 
					
						
							|  |  |  | 		const struct usb_device_id *id, | 
					
						
							|  |  |  | 		const struct sd_desc *sd_desc, | 
					
						
							|  |  |  | 		int dev_size, | 
					
						
							|  |  |  | 		struct module *module) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct usb_device *dev = interface_to_usbdev(intf); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* we don't handle multi-config cameras */ | 
					
						
							|  |  |  | 	if (dev->descriptor.bNumConfigurations != 1) { | 
					
						
							| 
									
										
										
										
											2011-08-21 19:56:57 -03:00
										 |  |  | 		pr_err("%04x:%04x too many config\n", | 
					
						
							|  |  |  | 		       id->idVendor, id->idProduct); | 
					
						
							| 
									
										
										
										
											2010-06-05 07:47:36 -03:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* the USB video interface must be the first one */ | 
					
						
							| 
									
										
										
										
											2012-01-01 17:20:14 -03:00
										 |  |  | 	if (dev->actconfig->desc.bNumInterfaces != 1 | 
					
						
							| 
									
										
										
										
											2010-07-14 06:26:54 -03:00
										 |  |  | 	 && intf->cur_altsetting->desc.bInterfaceNumber != 0) | 
					
						
							| 
									
										
										
										
											2010-06-05 07:47:36 -03:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return gspca_dev_probe2(intf, id, sd_desc, dev_size, module); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | EXPORT_SYMBOL(gspca_dev_probe); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * USB disconnection | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function must be called by the sub-driver | 
					
						
							|  |  |  |  * when the device disconnects, after the specific resources are freed. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void gspca_disconnect(struct usb_interface *intf) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-05-04 06:46:21 -03:00
										 |  |  | 	struct gspca_dev *gspca_dev = usb_get_intfdata(intf); | 
					
						
							| 
									
										
										
										
											2013-01-24 19:28:59 -03:00
										 |  |  | #if IS_ENABLED(CONFIG_INPUT)
 | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 	struct input_dev *input_dev; | 
					
						
							| 
									
										
										
										
											2010-02-18 15:02:51 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-27 13:57:15 -03:00
										 |  |  | 	PDEBUG(D_PROBE, "%s disconnect", | 
					
						
							|  |  |  | 		video_device_node_name(&gspca_dev->vdev)); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-04 15:33:21 -03:00
										 |  |  | 	mutex_lock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2010-12-30 13:11:21 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-22 05:35:49 -03:00
										 |  |  | 	gspca_dev->present = 0; | 
					
						
							| 
									
										
										
										
											2010-12-30 13:11:21 -03:00
										 |  |  | 	destroy_urbs(gspca_dev); | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-24 19:28:59 -03:00
										 |  |  | #if IS_ENABLED(CONFIG_INPUT)
 | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 	gspca_input_destroy_urb(gspca_dev); | 
					
						
							|  |  |  | 	input_dev = gspca_dev->input_dev; | 
					
						
							|  |  |  | 	if (input_dev) { | 
					
						
							|  |  |  | 		gspca_dev->input_dev = NULL; | 
					
						
							|  |  |  | 		input_unregister_device(input_dev); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-02-18 15:02:51 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2012-05-07 06:25:30 -03:00
										 |  |  | 	/* Free subdriver's streaming resources / stop sd workqueue(s) */ | 
					
						
							|  |  |  | 	if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->stop0(gspca_dev); | 
					
						
							|  |  |  | 	gspca_dev->streaming = 0; | 
					
						
							| 
									
										
											  
											
												[media] gspca: Don't set gspca_dev->dev to NULL before stop0
In commit a3d6e8cc0e6ddc8b3cfdeb3c979f07ed1aa528b3 gspca_dev->dev is set
to NULL on disconnect, before calling stop0. The plan was to get rid of
gspca_dev->present and instead simply check for gspca_dev->dev everywhere
where we were checking for present. This should be race free since all users
of gspca_dev->dev hold the usb_lock, or so I thought.
But I was wrong, drivers which use a work-queue + synchronous bulk transfers
to get the video data don't hold the usb_lock while doing so, their stop0
callbacks stop the workqueue, so they won't be using gspca_dev->dev anymore
after the stop0 call, but they might be dereferincing it before, so we should
not set gspca_dev->dev to NULL on disconnect before calling stop0.
This also means that the workqueue functions in these drivers cannot
use gspca_dev->dev to check if they need to stop because of disconnection,
so we will need to keep gspca_dev->present around, and set that to 0 on
disconnect, before calling stop0. Unfortunately as part of the plan to remove
gspca_dev->present, these workqueues where already moved over to checking
for gspca_dev->dev instead of gspca_dev->present as part of commit
254902b01d2acc6aced99ec17caa4c6cd890cdea, so this patch also reverts those
parts of that commit.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
											
										 
											2012-09-09 06:30:02 -03:00
										 |  |  | 	gspca_dev->dev = NULL; | 
					
						
							| 
									
										
										
										
											2012-05-07 06:25:30 -03:00
										 |  |  | 	wake_up_interruptible(&gspca_dev->wq); | 
					
						
							| 
									
										
										
										
											2010-01-28 06:39:49 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:19 -03:00
										 |  |  | 	v4l2_device_disconnect(&gspca_dev->v4l2_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	video_unregister_device(&gspca_dev->vdev); | 
					
						
							| 
									
										
										
										
											2009-02-12 08:05:45 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2008-09-28 08:12:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-30 22:57:33 -03:00
										 |  |  | 	/* (this will call gspca_release() immediately or on last close) */ | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	v4l2_device_put(&gspca_dev->v4l2_dev); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(gspca_disconnect); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | int gspca_suspend(struct usb_interface *intf, pm_message_t message) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gspca_dev *gspca_dev = usb_get_intfdata(intf); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-09 08:04:05 -03:00
										 |  |  | 	gspca_input_destroy_urb(gspca_dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | 	if (!gspca_dev->streaming) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2012-09-09 08:04:05 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	mutex_lock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | 	gspca_dev->frozen = 1;		/* avoid urb error messages */ | 
					
						
							| 
									
										
										
										
											2012-05-09 12:11:12 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:16 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->stopN) | 
					
						
							|  |  |  | 		gspca_dev->sd_desc->stopN(gspca_dev); | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | 	destroy_urbs(gspca_dev); | 
					
						
							|  |  |  | 	gspca_set_alt0(gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	if (gspca_dev->sd_desc->stop0) | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:16 -03:00
										 |  |  | 		gspca_dev->sd_desc->stop0(gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2012-09-09 08:04:05 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(gspca_suspend); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int gspca_resume(struct usb_interface *intf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gspca_dev *gspca_dev = usb_get_intfdata(intf); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:22 -03:00
										 |  |  | 	int streaming, ret = 0; | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	mutex_lock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | 	gspca_dev->frozen = 0; | 
					
						
							| 
									
										
										
										
											2012-05-09 12:11:12 -03:00
										 |  |  | 	gspca_dev->usb_err = 0; | 
					
						
							| 
									
										
										
										
											2008-09-03 17:12:16 -03:00
										 |  |  | 	gspca_dev->sd_desc->init(gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:17 -03:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Most subdrivers send all ctrl values on sd_start and thus | 
					
						
							|  |  |  | 	 * only write to the device registers on s_ctrl when streaming -> | 
					
						
							|  |  |  | 	 * Clear streaming to avoid setting all ctrls twice. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	streaming = gspca_dev->streaming; | 
					
						
							|  |  |  | 	gspca_dev->streaming = 0; | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	if (streaming) | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:22 -03:00
										 |  |  | 		ret = gspca_init_transfer(gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-09-09 08:04:05 -03:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		gspca_input_create_urb(gspca_dev); | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:27 -03:00
										 |  |  | 	mutex_unlock(&gspca_dev->usb_lock); | 
					
						
							| 
									
										
										
										
											2012-09-09 08:04:05 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-06 09:28:22 -03:00
										 |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2008-09-03 16:48:10 -03:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(gspca_resume); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-07-10 10:40:53 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | /* -- module insert / remove -- */ | 
					
						
							|  |  |  | static int __init gspca_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-09-23 05:17:10 -03:00
										 |  |  | 	pr_info("v" GSPCA_VERSION " registered\n"); | 
					
						
							| 
									
										
										
										
											2008-04-12 09:58:09 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | static void __exit gspca_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(gspca_init); | 
					
						
							|  |  |  | module_exit(gspca_exit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param_named(debug, gspca_debug, int, 0644); | 
					
						
							|  |  |  | MODULE_PARM_DESC(debug, | 
					
						
							| 
									
										
										
										
											2013-02-04 13:17:55 -03:00
										 |  |  | 		"1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo"); |