[RFC] gspca pac7302: add support for camera button

Message ID 4AFFC00F.6060704@freemail.hu (mailing list archive)
State Superseded, archived
Headers

Commit Message

Németh Márton Nov. 15, 2009, 8:47 a.m. UTC
  From: Márton Németh <nm127@freemail.hu>

Add support for snapshot button found on Labtec Webcam 2200.

Signed-off-by: Márton Németh <nm127@freemail.hu>
---
Hi,

this is the first trial to add support for the snapshot button. This
code is working only before the streaming is started. When the streaming
is started the alternate number of the interface 0 is changed and the
interrupt URB is no longer usable. I guess the interrupt URB is to
be reconstructed every time when the alternate number is changed.

When I disconnect the device I get the following error:

uhci_hcd 0000:00:1d.1: dma_pool_free buffer-32, f58ba168/358ba168 (bad dma)

I guess something is wrong in this patch with the cleanup routine.

Regards,

	Márton Németh

---
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  

Comments

Hans de Goede Nov. 15, 2009, 10:23 a.m. UTC | #1
Hi,

Thanks for working on this! I think it would be great if we could
get support for camera buttons in general into gspca.

I've not looked closely at your code yet, have you looked at
the camera button code in the gspca sn9c20x.c driver? Also I would really
like to see as much of the button handling code as possible go into
the gspca core. AFAIK many many camera's use an usb interrupt ep for this, so
I would like to see the setting up and cleanup of this interrupt ep be in
the core (as said before see the sn9c20x driver for another driver which
does such things).

Regards,

Hans


On 11/15/2009 09:47 AM, Németh Márton wrote:
> From: Márton Németh<nm127@freemail.hu>
>
> Add support for snapshot button found on Labtec Webcam 2200.
>
> Signed-off-by: Márton Németh<nm127@freemail.hu>
> ---
> Hi,
>
> this is the first trial to add support for the snapshot button. This
> code is working only before the streaming is started. When the streaming
> is started the alternate number of the interface 0 is changed and the
> interrupt URB is no longer usable. I guess the interrupt URB is to
> be reconstructed every time when the alternate number is changed.
>
> When I disconnect the device I get the following error:
>
> uhci_hcd 0000:00:1d.1: dma_pool_free buffer-32, f58ba168/358ba168 (bad dma)
>
> I guess something is wrong in this patch with the cleanup routine.
>
> Regards,
>
> 	Márton Németh
>
> ---
> diff -r 09c1284de47d linux/drivers/media/video/gspca/gspca.h
> --- a/linux/drivers/media/video/gspca/gspca.h	Sat Nov 14 08:58:12 2009 +0100
> +++ b/linux/drivers/media/video/gspca/gspca.h	Sun Nov 15 10:40:54 2009 +0100
> @@ -138,6 +138,7 @@
>   	struct module *module;		/* subdriver handling the device */
>   	struct usb_device *dev;
>   	struct file *capt_file;		/* file doing video capture */
> +	struct input_dev *input_dev;
>
>   	struct cam cam;				/* device information */
>   	const struct sd_desc *sd_desc;		/* subdriver description */
> @@ -147,6 +148,7 @@
>   #define USB_BUF_SZ 64
>   	__u8 *usb_buf;				/* buffer for USB exchanges */
>   	struct urb *urb[MAX_NURBS];
> +	struct urb *int_urb;
>
>   	__u8 *frbuf;				/* buffer for nframes */
>   	struct gspca_frame frame[GSPCA_MAX_FRAMES];
> diff -r 09c1284de47d linux/drivers/media/video/gspca/pac7302.c
> --- a/linux/drivers/media/video/gspca/pac7302.c	Sat Nov 14 08:58:12 2009 +0100
> +++ b/linux/drivers/media/video/gspca/pac7302.c	Sun Nov 15 10:40:54 2009 +0100
> @@ -68,6 +68,7 @@
>
>   #define MODULE_NAME "pac7302"
>
> +#include<linux/input.h>
>   #include<media/v4l2-chip-ident.h>
>   #include "gspca.h"
>
> @@ -1220,6 +1221,50 @@
>   }
>   #endif
>
> +#if LINUX_VERSION_CODE<  KERNEL_VERSION(2, 6, 19)
> +static void int_irq(struct urb *urb, struct pt_regs *regs)
> +#else
> +static void int_irq(struct urb *urb)
> +#endif
> +{
> +	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
> +	int ret;
> +	int i;
> +	__u8 data0, data1;
> +
> +	printk(KERN_DEBUG "int_irq()\n");
> +	printk(KERN_DEBUG "urb->status: %i\n", urb->status);
> +	if (urb->status == 0) {
> +		printk(KERN_DEBUG "urb->actual_length: %u\n", urb->actual_length);
> +		for (i = 0; i<  urb->actual_length; i++) {
> +			printk(KERN_DEBUG "urb->transfer_buffer[%i]=0x%x\n",
> +				i, ((__u8*)urb->transfer_buffer)[i]);
> +		}
> +		if (urb->actual_length == 2) {
> +			data0 = ((__u8*)urb->transfer_buffer)[0];
> +			data1 = ((__u8*)urb->transfer_buffer)[1];
> +			if ((data0 == 0x00&&  data1 == 0x11) ||
> +			    (data0 == 0x22&&  data1 == 0x33) ||
> +			    (data0 == 0x44&&  data1 == 0x55) ||
> +			    (data0 == 0x66&&  data1 == 0x77) ||
> +			    (data0 == 0x88&&  data1 == 0x99) ||
> +			    (data0 == 0xaa&&  data1 == 0xbb) ||
> +			    (data0 == 0xcc&&  data1 == 0xdd) ||
> +			    (data0 == 0xee&&  data1 == 0xff)) {
> +				input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
> +				input_sync(gspca_dev->input_dev);
> +				input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
> +				input_sync(gspca_dev->input_dev);
> +			} else
> +				printk(KERN_DEBUG "Unknown packet received\n");
> +		}
> +		ret = usb_submit_urb(urb, GFP_ATOMIC);
> +		printk(KERN_DEBUG "resubmit urb: %i\n", ret);
> +	}
> +
> +}
> +
> +
>   /* sub-driver description for pac7302 */
>   static struct sd_desc sd_desc = {
>   	.name = MODULE_NAME,
> @@ -1254,19 +1299,132 @@
>   };
>   MODULE_DEVICE_TABLE(usb, device_table);
>
> +static int init_camera_input(struct gspca_dev *gspca_dev, const struct usb_device_id *id)
> +{
> +	struct input_dev *input_dev;
> +	int err;
> +
> +	printk(KERN_DEBUG "allocating input device\n");
> +	input_dev = input_allocate_device();
> +	if (!input_dev)
> +		return -ENOMEM;
> +
> +	//input_dev->name = "Camera capture button";
> +	//input_dev->phys = "camera";
> +	input_dev->id.bustype = BUS_USB;
> +	input_dev->id.vendor = id->idVendor;
> +	input_dev->id.product = id->idProduct;
> +	input_dev->id.version = id->bcdDevice_hi;
> +	//input_dev->id.version = id->bcdDevice_lo;
> +
> +	input_dev->evbit[0] = BIT_MASK(EV_KEY);
> +	input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
> +	//input_dev->dev.parent = ;
> +
> +	printk(KERN_DEBUG "registering input device\n");
> +	err = input_register_device(input_dev);
> +	if (err) {
> +		input_dev->dev.parent = NULL;
> +		input_free_device(input_dev);
> +	} else {
> +		gspca_dev->input_dev = input_dev;
> +	}
> +
> +	return err;
> +}
> +
>   /* -- device connect -- */
>   static int sd_probe(struct usb_interface *intf,
>   			const struct usb_device_id *id)
>   {
> -	return gspca_dev_probe(intf, id,&sd_desc, sizeof(struct sd),
> +	int ret;
> +	struct usb_host_interface *intf_desc;
> +	struct usb_endpoint_descriptor *ep;
> +	int i;
> +
> +	struct urb *urb;
> +	void* buffer = NULL;
> +	unsigned int buffer_len;
> +	int interval;
> +	struct gspca_dev *gspca_dev;
> +	struct usb_device *dev;
> +
> +	ret = gspca_dev_probe(intf, id,&sd_desc, sizeof(struct sd),
>   				THIS_MODULE);
> +	if (0<= ret) {
> +		intf_desc = intf->cur_altsetting;
> +		for (i = 0; i<  intf_desc->desc.bNumEndpoints; i++) {
> +			ep =&intf_desc->endpoint[i].desc;
> +			if ((ep->bEndpointAddress&  USB_DIR_IN)&&
> +			    ((ep->bmAttributes&  USB_ENDPOINT_XFERTYPE_MASK)
> +				== USB_ENDPOINT_XFER_INT)) {
> +
> +				buffer_len = ep->wMaxPacketSize;
> +				interval = ep->bInterval;
> +				printk(KERN_DEBUG "found int in endpoint: 0x%x\n", ep->bEndpointAddress);
> +				printk(KERN_DEBUG " - buffer_len = %u\n", buffer_len);
> +				printk(KERN_DEBUG " - interval = %u\n", interval);
> +
> +				gspca_dev = usb_get_intfdata(intf);
> +				dev = gspca_dev->dev;
> +				gspca_dev->int_urb = NULL;
> +				gspca_dev->input_dev = NULL;
> +
> +				buffer = kmalloc(ep->wMaxPacketSize, GFP_KERNEL);
> +				if (buffer)
> +					urb = usb_alloc_urb(0, GFP_KERNEL);
> +				else {
> +					kfree(buffer);
> +					urb = NULL;
> +				}
> +				if (buffer&&  urb) {
> +					usb_fill_int_urb(urb, dev,
> +						usb_rcvintpipe(dev, ep->bEndpointAddress),
> +						buffer, buffer_len,
> +						int_irq, (void*)gspca_dev, interval);
> +					ret = init_camera_input(gspca_dev, id);
> +					if (0<= ret) {
> +						gspca_dev->int_urb = urb;
> +						ret = usb_submit_urb(urb, GFP_KERNEL);
> +						printk(KERN_DEBUG "usb_submit_urb() returns %i\n", ret);
> +					}
> +				}
> +			}
> +
> +		}
> +	}
> +	return ret;
> +}
> +
> +static void sd_disconnect(struct usb_interface *intf)
> +{
> +	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
> +	struct urb *urb;
> +	struct input_dev *input_dev;
> +
> +	urb = gspca_dev->int_urb;
> +	if (urb != NULL) {
> +		gspca_dev->int_urb = NULL;
> +		usb_kill_urb(urb);
> +		usb_buffer_free(gspca_dev->dev,
> +				urb->transfer_buffer_length,
> +				urb->transfer_buffer,
> +				urb->transfer_dma);
> +		usb_free_urb(urb);
> +	}
> +	input_dev = gspca_dev->input_dev;
> +	if (input_dev) {
> +		gspca_dev->input_dev = NULL;
> +		input_unregister_device(input_dev);
> +	}
> +	gspca_disconnect(intf);
>   }
>
>   static struct usb_driver sd_driver = {
>   	.name = MODULE_NAME,
>   	.id_table = device_table,
>   	.probe = sd_probe,
> -	.disconnect = gspca_disconnect,
> +	.disconnect = sd_disconnect,
>   #ifdef CONFIG_PM
>   	.suspend = gspca_suspend,
>   	.resume = gspca_resume,
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Németh Márton Nov. 16, 2009, 6:58 a.m. UTC | #2
Hi,
Hans de Goede wrote:
> Hi,
> 
> Thanks for working on this! I think it would be great if we could
> get support for camera buttons in general into gspca.
> 
> I've not looked closely at your code yet, have you looked at
> the camera button code in the gspca sn9c20x.c driver? Also I would really

As you proposed I had a look on sn9c20x. It seems that sn9c20x uses register read
via USB control message. The pac7302 uses interrupt endpoint. So it looks like
quite different to me. Currently I see the common point in the connection
to input subsystem only.

> like to see as much of the button handling code as possible go into
> the gspca core. AFAIK many many camera's use an usb interrupt ep for this, so
> I would like to see the setting up and cleanup of this interrupt ep be in
> the core (as said before see the sn9c20x driver for another driver which
> does such things).

Unfortunately I do not know how the USB descriptors of other webcams look like.
I have access to two webcams which are handled by gspca:

 1. Labtec Webcam 2200 (USB ID 093a:2626):

	Interface Number: 0
		Name: (none)
		Alternate Number: 0
		Class: ff(vend.)
		Sub Class: ff
		Protocol: ff
		Number of Endpoints: 6

			[...]

			Endpoint Address: 83
			Direction: in
			Attribute: 3
			Type: Int.
			Max Packet Size: 2
			Interval: 50ms

    This endpoint handles the capture button.

 2. Trust 610 LCD PowerC@m Zoom (USB ID 06d6:0031)

	Interface Number: 1
		Name: (none)
		Alternate Number: 0
		Class: ff(vend.)
		Sub Class: 00
		Protocol: 00
		Number of Endpoints: 3

			[...]

			Endpoint Address: 84
			Direction: in
			Attribute: 3
			Type: Int.
			Max Packet Size: 1
			Interval: 1ms

    I don't know what this interrupt endpoint is useful for.

Comparing these two endpoints shows the common and different points:
Common: interface class, endpoint direction, endpoint type.
Different: interface number, sub class, protocol, endpoint address, max
           packet size, interval.

Maybe the second example is not a good one because I don't know whether
the interrupt endpoint is used for buttons or not.

Do you have access to webcams equipped with button? Could you please
send the device descriptor (lsusb -v) about these devices in order
the common points can be identified for interrupt endpoints?

Regards,

	Márton Németh

> On 11/15/2009 09:47 AM, Németh Márton wrote:
>> From: Márton Németh<nm127@freemail.hu>
>>
>> Add support for snapshot button found on Labtec Webcam 2200.
>>
>> Signed-off-by: Márton Németh<nm127@freemail.hu>
>> ---
>> Hi,
>>
>> this is the first trial to add support for the snapshot button. This
>> code is working only before the streaming is started. When the streaming
>> is started the alternate number of the interface 0 is changed and the
>> interrupt URB is no longer usable. I guess the interrupt URB is to
>> be reconstructed every time when the alternate number is changed.
>>
>> When I disconnect the device I get the following error:
>>
>> uhci_hcd 0000:00:1d.1: dma_pool_free buffer-32, f58ba168/358ba168 (bad dma)
>>
>> I guess something is wrong in this patch with the cleanup routine.
>>
>> Regards,
>>
>> 	Márton Németh
>>
>> ---
>> diff -r 09c1284de47d linux/drivers/media/video/gspca/gspca.h
>> --- a/linux/drivers/media/video/gspca/gspca.h	Sat Nov 14 08:58:12 2009 +0100
>> +++ b/linux/drivers/media/video/gspca/gspca.h	Sun Nov 15 10:40:54 2009 +0100
>> @@ -138,6 +138,7 @@
>>   	struct module *module;		/* subdriver handling the device */
>>   	struct usb_device *dev;
>>   	struct file *capt_file;		/* file doing video capture */
>> +	struct input_dev *input_dev;
>>
>>   	struct cam cam;				/* device information */
>>   	const struct sd_desc *sd_desc;		/* subdriver description */
>> @@ -147,6 +148,7 @@
>>   #define USB_BUF_SZ 64
>>   	__u8 *usb_buf;				/* buffer for USB exchanges */
>>   	struct urb *urb[MAX_NURBS];
>> +	struct urb *int_urb;
>>
>>   	__u8 *frbuf;				/* buffer for nframes */
>>   	struct gspca_frame frame[GSPCA_MAX_FRAMES];
>> diff -r 09c1284de47d linux/drivers/media/video/gspca/pac7302.c
>> --- a/linux/drivers/media/video/gspca/pac7302.c	Sat Nov 14 08:58:12 2009 +0100
>> +++ b/linux/drivers/media/video/gspca/pac7302.c	Sun Nov 15 10:40:54 2009 +0100
>> @@ -68,6 +68,7 @@
>>
>>   #define MODULE_NAME "pac7302"
>>
>> +#include<linux/input.h>
>>   #include<media/v4l2-chip-ident.h>
>>   #include "gspca.h"
>>
>> @@ -1220,6 +1221,50 @@
>>   }
>>   #endif
>>
>> +#if LINUX_VERSION_CODE<  KERNEL_VERSION(2, 6, 19)
>> +static void int_irq(struct urb *urb, struct pt_regs *regs)
>> +#else
>> +static void int_irq(struct urb *urb)
>> +#endif
>> +{
>> +	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
>> +	int ret;
>> +	int i;
>> +	__u8 data0, data1;
>> +
>> +	printk(KERN_DEBUG "int_irq()\n");
>> +	printk(KERN_DEBUG "urb->status: %i\n", urb->status);
>> +	if (urb->status == 0) {
>> +		printk(KERN_DEBUG "urb->actual_length: %u\n", urb->actual_length);
>> +		for (i = 0; i<  urb->actual_length; i++) {
>> +			printk(KERN_DEBUG "urb->transfer_buffer[%i]=0x%x\n",
>> +				i, ((__u8*)urb->transfer_buffer)[i]);
>> +		}
>> +		if (urb->actual_length == 2) {
>> +			data0 = ((__u8*)urb->transfer_buffer)[0];
>> +			data1 = ((__u8*)urb->transfer_buffer)[1];
>> +			if ((data0 == 0x00&&  data1 == 0x11) ||
>> +			    (data0 == 0x22&&  data1 == 0x33) ||
>> +			    (data0 == 0x44&&  data1 == 0x55) ||
>> +			    (data0 == 0x66&&  data1 == 0x77) ||
>> +			    (data0 == 0x88&&  data1 == 0x99) ||
>> +			    (data0 == 0xaa&&  data1 == 0xbb) ||
>> +			    (data0 == 0xcc&&  data1 == 0xdd) ||
>> +			    (data0 == 0xee&&  data1 == 0xff)) {
>> +				input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
>> +				input_sync(gspca_dev->input_dev);
>> +				input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
>> +				input_sync(gspca_dev->input_dev);
>> +			} else
>> +				printk(KERN_DEBUG "Unknown packet received\n");
>> +		}
>> +		ret = usb_submit_urb(urb, GFP_ATOMIC);
>> +		printk(KERN_DEBUG "resubmit urb: %i\n", ret);
>> +	}
>> +
>> +}
>> +
>> +
>>   /* sub-driver description for pac7302 */
>>   static struct sd_desc sd_desc = {
>>   	.name = MODULE_NAME,
>> @@ -1254,19 +1299,132 @@
>>   };
>>   MODULE_DEVICE_TABLE(usb, device_table);
>>
>> +static int init_camera_input(struct gspca_dev *gspca_dev, const struct usb_device_id *id)
>> +{
>> +	struct input_dev *input_dev;
>> +	int err;
>> +
>> +	printk(KERN_DEBUG "allocating input device\n");
>> +	input_dev = input_allocate_device();
>> +	if (!input_dev)
>> +		return -ENOMEM;
>> +
>> +	//input_dev->name = "Camera capture button";
>> +	//input_dev->phys = "camera";
>> +	input_dev->id.bustype = BUS_USB;
>> +	input_dev->id.vendor = id->idVendor;
>> +	input_dev->id.product = id->idProduct;
>> +	input_dev->id.version = id->bcdDevice_hi;
>> +	//input_dev->id.version = id->bcdDevice_lo;
>> +
>> +	input_dev->evbit[0] = BIT_MASK(EV_KEY);
>> +	input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
>> +	//input_dev->dev.parent = ;
>> +
>> +	printk(KERN_DEBUG "registering input device\n");
>> +	err = input_register_device(input_dev);
>> +	if (err) {
>> +		input_dev->dev.parent = NULL;
>> +		input_free_device(input_dev);
>> +	} else {
>> +		gspca_dev->input_dev = input_dev;
>> +	}
>> +
>> +	return err;
>> +}
>> +
>>   /* -- device connect -- */
>>   static int sd_probe(struct usb_interface *intf,
>>   			const struct usb_device_id *id)
>>   {
>> -	return gspca_dev_probe(intf, id,&sd_desc, sizeof(struct sd),
>> +	int ret;
>> +	struct usb_host_interface *intf_desc;
>> +	struct usb_endpoint_descriptor *ep;
>> +	int i;
>> +
>> +	struct urb *urb;
>> +	void* buffer = NULL;
>> +	unsigned int buffer_len;
>> +	int interval;
>> +	struct gspca_dev *gspca_dev;
>> +	struct usb_device *dev;
>> +
>> +	ret = gspca_dev_probe(intf, id,&sd_desc, sizeof(struct sd),
>>   				THIS_MODULE);
>> +	if (0<= ret) {
>> +		intf_desc = intf->cur_altsetting;
>> +		for (i = 0; i<  intf_desc->desc.bNumEndpoints; i++) {
>> +			ep =&intf_desc->endpoint[i].desc;
>> +			if ((ep->bEndpointAddress&  USB_DIR_IN)&&
>> +			    ((ep->bmAttributes&  USB_ENDPOINT_XFERTYPE_MASK)
>> +				== USB_ENDPOINT_XFER_INT)) {
>> +
>> +				buffer_len = ep->wMaxPacketSize;
>> +				interval = ep->bInterval;
>> +				printk(KERN_DEBUG "found int in endpoint: 0x%x\n", ep->bEndpointAddress);
>> +				printk(KERN_DEBUG " - buffer_len = %u\n", buffer_len);
>> +				printk(KERN_DEBUG " - interval = %u\n", interval);
>> +
>> +				gspca_dev = usb_get_intfdata(intf);
>> +				dev = gspca_dev->dev;
>> +				gspca_dev->int_urb = NULL;
>> +				gspca_dev->input_dev = NULL;
>> +
>> +				buffer = kmalloc(ep->wMaxPacketSize, GFP_KERNEL);
>> +				if (buffer)
>> +					urb = usb_alloc_urb(0, GFP_KERNEL);
>> +				else {
>> +					kfree(buffer);
>> +					urb = NULL;
>> +				}
>> +				if (buffer&&  urb) {
>> +					usb_fill_int_urb(urb, dev,
>> +						usb_rcvintpipe(dev, ep->bEndpointAddress),
>> +						buffer, buffer_len,
>> +						int_irq, (void*)gspca_dev, interval);
>> +					ret = init_camera_input(gspca_dev, id);
>> +					if (0<= ret) {
>> +						gspca_dev->int_urb = urb;
>> +						ret = usb_submit_urb(urb, GFP_KERNEL);
>> +						printk(KERN_DEBUG "usb_submit_urb() returns %i\n", ret);
>> +					}
>> +				}
>> +			}
>> +
>> +		}
>> +	}
>> +	return ret;
>> +}
>> +
>> +static void sd_disconnect(struct usb_interface *intf)
>> +{
>> +	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
>> +	struct urb *urb;
>> +	struct input_dev *input_dev;
>> +
>> +	urb = gspca_dev->int_urb;
>> +	if (urb != NULL) {
>> +		gspca_dev->int_urb = NULL;
>> +		usb_kill_urb(urb);
>> +		usb_buffer_free(gspca_dev->dev,
>> +				urb->transfer_buffer_length,
>> +				urb->transfer_buffer,
>> +				urb->transfer_dma);
>> +		usb_free_urb(urb);
>> +	}
>> +	input_dev = gspca_dev->input_dev;
>> +	if (input_dev) {
>> +		gspca_dev->input_dev = NULL;
>> +		input_unregister_device(input_dev);
>> +	}
>> +	gspca_disconnect(intf);
>>   }
>>
>>   static struct usb_driver sd_driver = {
>>   	.name = MODULE_NAME,
>>   	.id_table = device_table,
>>   	.probe = sd_probe,
>> -	.disconnect = gspca_disconnect,
>> +	.disconnect = sd_disconnect,
>>   #ifdef CONFIG_PM
>>   	.suspend = gspca_suspend,
>>   	.resume = gspca_resume,
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Hans de Goede Nov. 16, 2009, 8:43 a.m. UTC | #3
Hi,

On 11/16/2009 07:58 AM, Németh Márton wrote:
> Hi,
> Hans de Goede wrote:
>> Hi,
>>
>> Thanks for working on this! I think it would be great if we could
>> get support for camera buttons in general into gspca.
>>
>> I've not looked closely at your code yet, have you looked at
>> the camera button code in the gspca sn9c20x.c driver? Also I would really
>
> As you proposed I had a look on sn9c20x. It seems that sn9c20x uses register read
> via USB control message. The pac7302 uses interrupt endpoint. So it looks like
> quite different to me. Currently I see the common point in the connection
> to input subsystem only.
>

Ah you are right, oops, most camera's use an interrupt end point so I assumed
sn9c20x would be the same, my bad.

>> like to see as much of the button handling code as possible go into
>> the gspca core. AFAIK many many camera's use an usb interrupt ep for this, so
>> I would like to see the setting up and cleanup of this interrupt ep be in
>> the core (as said before see the sn9c20x driver for another driver which
>> does such things).
>
> Unfortunately I do not know how the USB descriptors of other webcams look like.
> I have access to two webcams which are handled by gspca:
>

No problem, just put all the input code in pac7302.c for now, we will abstract it
later when we add support for the button on other camera's too.

<snip>

> Comparing these two endpoints shows the common and different points:
> Common: interface class, endpoint direction, endpoint type.
> Different: interface number, sub class, protocol, endpoint address, max
>             packet size, interval.
>
> Maybe the second example is not a good one because I don't know whether
> the interrupt endpoint is used for buttons or not.
>
> Do you have access to webcams equipped with button? Could you please
> send the device descriptor (lsusb -v) about these devices in order
> the common points can be identified for interrupt endpoints?
>

As the author/maintainer of quite a few drivers and libv4l author I have
build up quite a test camera collection, I'll send you the lsusb -v output
of a few in a private mail. But as said before, for now I think you can just put
the input code inside pac7302.c, then later on we can try to abstract it.

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  

Patch

diff -r 09c1284de47d linux/drivers/media/video/gspca/gspca.h
--- a/linux/drivers/media/video/gspca/gspca.h	Sat Nov 14 08:58:12 2009 +0100
+++ b/linux/drivers/media/video/gspca/gspca.h	Sun Nov 15 10:40:54 2009 +0100
@@ -138,6 +138,7 @@ 
 	struct module *module;		/* subdriver handling the device */
 	struct usb_device *dev;
 	struct file *capt_file;		/* file doing video capture */
+	struct input_dev *input_dev;

 	struct cam cam;				/* device information */
 	const struct sd_desc *sd_desc;		/* subdriver description */
@@ -147,6 +148,7 @@ 
 #define USB_BUF_SZ 64
 	__u8 *usb_buf;				/* buffer for USB exchanges */
 	struct urb *urb[MAX_NURBS];
+	struct urb *int_urb;

 	__u8 *frbuf;				/* buffer for nframes */
 	struct gspca_frame frame[GSPCA_MAX_FRAMES];
diff -r 09c1284de47d linux/drivers/media/video/gspca/pac7302.c
--- a/linux/drivers/media/video/gspca/pac7302.c	Sat Nov 14 08:58:12 2009 +0100
+++ b/linux/drivers/media/video/gspca/pac7302.c	Sun Nov 15 10:40:54 2009 +0100
@@ -68,6 +68,7 @@ 

 #define MODULE_NAME "pac7302"

+#include <linux/input.h>
 #include <media/v4l2-chip-ident.h>
 #include "gspca.h"

@@ -1220,6 +1221,50 @@ 
 }
 #endif

+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void int_irq(struct urb *urb, struct pt_regs *regs)
+#else
+static void int_irq(struct urb *urb)
+#endif
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+	int ret;
+	int i;
+	__u8 data0, data1;
+
+	printk(KERN_DEBUG "int_irq()\n");
+	printk(KERN_DEBUG "urb->status: %i\n", urb->status);
+	if (urb->status == 0) {
+		printk(KERN_DEBUG "urb->actual_length: %u\n", urb->actual_length);
+		for (i = 0; i < urb->actual_length; i++) {
+			printk(KERN_DEBUG "urb->transfer_buffer[%i]=0x%x\n",
+				i, ((__u8*)urb->transfer_buffer)[i]);
+		}
+		if (urb->actual_length == 2) {
+			data0 = ((__u8*)urb->transfer_buffer)[0];
+			data1 = ((__u8*)urb->transfer_buffer)[1];
+			if ((data0 == 0x00 && data1 == 0x11) ||
+			    (data0 == 0x22 && data1 == 0x33) ||
+			    (data0 == 0x44 && data1 == 0x55) ||
+			    (data0 == 0x66 && data1 == 0x77) ||
+			    (data0 == 0x88 && data1 == 0x99) ||
+			    (data0 == 0xaa && data1 == 0xbb) ||
+			    (data0 == 0xcc && data1 == 0xdd) ||
+			    (data0 == 0xee && data1 == 0xff)) {
+				input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+				input_sync(gspca_dev->input_dev);
+				input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+				input_sync(gspca_dev->input_dev);
+			} else
+				printk(KERN_DEBUG "Unknown packet received\n");
+		}
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		printk(KERN_DEBUG "resubmit urb: %i\n", ret);
+	}
+
+}
+
+
 /* sub-driver description for pac7302 */
 static struct sd_desc sd_desc = {
 	.name = MODULE_NAME,
@@ -1254,19 +1299,132 @@ 
 };
 MODULE_DEVICE_TABLE(usb, device_table);

+static int init_camera_input(struct gspca_dev *gspca_dev, const struct usb_device_id *id)
+{
+	struct input_dev *input_dev;
+	int err;
+
+	printk(KERN_DEBUG "allocating input device\n");
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	//input_dev->name = "Camera capture button";
+	//input_dev->phys = "camera";
+	input_dev->id.bustype = BUS_USB;
+	input_dev->id.vendor = id->idVendor;
+	input_dev->id.product = id->idProduct;
+	input_dev->id.version = id->bcdDevice_hi;
+	//input_dev->id.version = id->bcdDevice_lo;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+	//input_dev->dev.parent = ;
+
+	printk(KERN_DEBUG "registering input device\n");
+	err = input_register_device(input_dev);
+	if (err) {
+		input_dev->dev.parent = NULL;
+		input_free_device(input_dev);
+	} else {
+		gspca_dev->input_dev = input_dev;
+	}
+
+	return err;
+}
+
 /* -- device connect -- */
 static int sd_probe(struct usb_interface *intf,
 			const struct usb_device_id *id)
 {
-	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+	int ret;
+	struct usb_host_interface *intf_desc;
+	struct usb_endpoint_descriptor *ep;
+	int i;
+
+	struct urb *urb;
+	void* buffer = NULL;
+	unsigned int buffer_len;
+	int interval;
+	struct gspca_dev *gspca_dev;
+	struct usb_device *dev;
+
+	ret = gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
 				THIS_MODULE);
+	if (0 <= ret) {
+		intf_desc = intf->cur_altsetting;
+		for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+			ep = &intf_desc->endpoint[i].desc;
+			if ((ep->bEndpointAddress & USB_DIR_IN) &&
+			    ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				== USB_ENDPOINT_XFER_INT)) {
+
+				buffer_len = ep->wMaxPacketSize;
+				interval = ep->bInterval;
+				printk(KERN_DEBUG "found int in endpoint: 0x%x\n", ep->bEndpointAddress);
+				printk(KERN_DEBUG " - buffer_len = %u\n", buffer_len);
+				printk(KERN_DEBUG " - interval = %u\n", interval);
+
+				gspca_dev = usb_get_intfdata(intf);
+				dev = gspca_dev->dev;
+				gspca_dev->int_urb = NULL;
+				gspca_dev->input_dev = NULL;
+
+				buffer = kmalloc(ep->wMaxPacketSize, GFP_KERNEL);
+				if (buffer)
+					urb = usb_alloc_urb(0, GFP_KERNEL);
+				else {
+					kfree(buffer);
+					urb = NULL;
+				}
+				if (buffer && urb) {
+					usb_fill_int_urb(urb, dev,
+						usb_rcvintpipe(dev, ep->bEndpointAddress),
+						buffer, buffer_len,
+						int_irq, (void*)gspca_dev, interval);
+					ret = init_camera_input(gspca_dev, id);
+					if (0 <= ret) {
+						gspca_dev->int_urb = urb;
+						ret = usb_submit_urb(urb, GFP_KERNEL);
+						printk(KERN_DEBUG "usb_submit_urb() returns %i\n", ret);
+					}
+				}
+			}
+
+		}
+	}
+	return ret;
+}
+
+static void sd_disconnect(struct usb_interface *intf)
+{
+	struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+	struct urb *urb;
+	struct input_dev *input_dev;
+
+	urb = gspca_dev->int_urb;
+	if (urb != NULL) {
+		gspca_dev->int_urb = NULL;
+		usb_kill_urb(urb);
+		usb_buffer_free(gspca_dev->dev,
+				urb->transfer_buffer_length,
+				urb->transfer_buffer,
+				urb->transfer_dma);
+		usb_free_urb(urb);
+	}
+	input_dev = gspca_dev->input_dev;
+	if (input_dev) {
+		gspca_dev->input_dev = NULL;
+		input_unregister_device(input_dev);
+	}
+	gspca_disconnect(intf);
 }

 static struct usb_driver sd_driver = {
 	.name = MODULE_NAME,
 	.id_table = device_table,
 	.probe = sd_probe,
-	.disconnect = gspca_disconnect,
+	.disconnect = sd_disconnect,
 #ifdef CONFIG_PM
 	.suspend = gspca_suspend,
 	.resume = gspca_resume,