linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: Vicam/3com homeconnect usb camera driver
@ 2002-10-06  2:58 John Tyner
  2002-10-06 10:11 ` Oliver Neukum
  0 siblings, 1 reply; 18+ messages in thread
From: John Tyner @ 2002-10-06  2:58 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 660 bytes --]

This time with the patch actually attached!

> > If you send me a patch for 2.5, I'd be glad to add it to the tree.
>
> As promised, here is a patch against 2.5.40.
>
> The 2.5 V4L interface looks like it will provide a way for multiple users
to
> use the same camera simultaneously, but that's a fairly drastic change,
and
> I'd like to start with something that is pretty well tested [at least by
> me]. So, this patch is a sraight forward-port of the 2.4 version posted a
> few days ago.
>
> > Right now, I'm not accepting USB drivers that don't show up in 2.5
> > first.
>
> If desired, I'll create a patch of the 2.4 version as well.
>
> Thanks,
> John
>

[-- Attachment #2: vicam-2.5.40.patch.gz --]
[-- Type: application/x-gzip, Size: 16808 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06  2:58 Vicam/3com homeconnect usb camera driver John Tyner
@ 2002-10-06 10:11 ` Oliver Neukum
  2002-10-06 17:35   ` John Tyner
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Neukum @ 2002-10-06 10:11 UTC (permalink / raw)
  To: John Tyner, Greg KH; +Cc: linux-kernel

On Sunday 06 October 2002 04:58, John Tyner wrote:
> This time with the patch actually attached!

In vicam_v4l_open:

Why is only the first control message checked for errors?

vicam_usb_probe:

__devinit ???

vicam_usb_disconnect:

__devexit ???
And you should probably kill the tasklet before you unregister the video 
device.

	Regards
		Oliver

PS: Is that just me, or did diff produce particularly unreadable output this 
time?

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 10:11 ` Oliver Neukum
@ 2002-10-06 17:35   ` John Tyner
  2002-10-06 22:01     ` Oliver Neukum
  0 siblings, 1 reply; 18+ messages in thread
From: John Tyner @ 2002-10-06 17:35 UTC (permalink / raw)
  To: Oliver Neukum, Greg KH; +Cc: linux-kernel

> In vicam_v4l_open:
>
> Why is only the first control message checked for errors?

The second one turns on the LED. I didn't check it because I figured the LED
actually turning on was not a big deal. Though, I suppose that if an error
occurred, that could be indicative of some other problem.

> vicam_usb_probe:
>
> __devinit ???
>
> vicam_usb_disconnect:
>
> __devexit ???

I'm not sure I see the problem here. __devinit is only defined when HOTPLUG
is not defined, which seems right to me. If there is no HOTPLUG then we can
throw away the code as soon as init is completed. ...similar argument for
__devexit. Correct me if I'm wrong.

> And you should probably kill the tasklet before you unregister the video
> device.

Makes sense.

> PS: Is that just me, or did diff produce particularly unreadable output
> this time?

Probably. The existing driver always crashed on me. I started fresh with
this one partly as a learning experience. Therefore, the diff probably isn't
very readable since it is completely removing the old driver and putting
this one in its place.

I'll re-diff after the open and __devinit/__devexit discussion gets cleared
up.

Thanks,
John


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 17:35   ` John Tyner
@ 2002-10-06 22:01     ` Oliver Neukum
  2002-10-06 23:25       ` John Tyner
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Neukum @ 2002-10-06 22:01 UTC (permalink / raw)
  To: John Tyner, Greg KH; +Cc: linux-kernel

On Sunday 06 October 2002 19:35, John Tyner wrote:
> > In vicam_v4l_open:
> >
> > Why is only the first control message checked for errors?
>
> The second one turns on the LED. I didn't check it because I figured the
> LED actually turning on was not a big deal. Though, I suppose that if an
> error occurred, that could be indicative of some other problem.
>

Exactly.

> > vicam_usb_probe:
> >
> > __devinit ???
> >
> > vicam_usb_disconnect:
> >
> > __devexit ???
>
> I'm not sure I see the problem here. __devinit is only defined when HOTPLUG
> is not defined, which seems right to me. If there is no HOTPLUG then we can
> throw away the code as soon as init is completed. ...similar argument for
> __devexit. Correct me if I'm wrong.

But usbcore will happily call probe if  HOTPLUG is not defined and AFAICT
usb will compile without HOTPLUG.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 22:01     ` Oliver Neukum
@ 2002-10-06 23:25       ` John Tyner
  2002-10-07  4:55         ` Oliver Neukum
  2002-10-07  9:43         ` Oliver Neukum
  0 siblings, 2 replies; 18+ messages in thread
From: John Tyner @ 2002-10-06 23:25 UTC (permalink / raw)
  To: Oliver Neukum, Greg KH; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 760 bytes --]

> > The second one turns on the LED. I didn't check it because I figured the
> > LED actually turning on was not a big deal. Though, I suppose that if an
> > error occurred, that could be indicative of some other problem.
>
> Exactly.

Fixed.

> But usbcore will happily call probe if  HOTPLUG is not defined and AFAICT
> usb will compile without HOTPLUG.

I thought HOTPLUG affected the user's ability to add and remove USB devices
while the kernel is running. It would seem that I misunderstood (or, in less
formal terms, was wrong :)).

Attached is a patch against 2.5.40 with __dev* uses removed and the error
checking in the open routine fixed.

Let me know if the ordering of the video_unregister_device and tasklet_kill
is still an issue.

Thanks,
John

[-- Attachment #2: vicam-2.5.40.patch.gz --]
[-- Type: application/x-gzip, Size: 16778 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 23:25       ` John Tyner
@ 2002-10-07  4:55         ` Oliver Neukum
  2002-10-07  9:43         ` Oliver Neukum
  1 sibling, 0 replies; 18+ messages in thread
From: Oliver Neukum @ 2002-10-07  4:55 UTC (permalink / raw)
  To: John Tyner, Greg KH; +Cc: linux-kernel, linux-usb-devel


> Attached is a patch against 2.5.40 with __dev* uses removed and the error
> checking in the open routine fixed.
>
> Let me know if the ordering of the video_unregister_device and tasklet_kill
> is still an issue.

It isn't. But the disconnect is still wrong. You fail to unlink the current 
urb. This has to be done before you kill the tasklet. And you have to use a 
flag and a spinlock to guard against a race with the completion handler.
There's a recent discussion on this in the usb archives. And you need to
defer freeing the memory if the device is open.
Have a look at how pwc does it. It should be correct in that regard.

And while you at it, could you rename the tasklet from ...bh... to ...tl... ?
It's no longer a bottom half.

	Regards
		Oliver
 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 23:25       ` John Tyner
  2002-10-07  4:55         ` Oliver Neukum
@ 2002-10-07  9:43         ` Oliver Neukum
  2002-10-07 21:51           ` John Tyner
       [not found]           ` <001901c26e8d$3f62f850$0a00a8c0@refresco>
  1 sibling, 2 replies; 18+ messages in thread
From: Oliver Neukum @ 2002-10-07  9:43 UTC (permalink / raw)
  To: John Tyner, Greg KH; +Cc: linux-kernel, linux-usb-devel


> Attached is a patch against 2.5.40 with __dev* uses removed and the error
> checking in the open routine fixed.
>
> Let me know if the ordering of the video_unregister_device and tasklet_kill
> is still an issue.

Well, here we go again, there are other issues as well.
There's a race between open() and disconnect(). The best way to deal
with it is to introduce a common semaphore for all devices the driver handles
and to take it as the first thing open() and disconnect() do and release it 
as the last thing. In addition every device needs a flag to show that it has
been opened and a flag to show that it has been unplugged. Alternatively
you could introduce device states, being "present and unused", "present and 
used" and "unplugged but used". As this needs to be checked in release(), it 
needs to take the semaphore as well.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-07  9:43         ` Oliver Neukum
@ 2002-10-07 21:51           ` John Tyner
       [not found]           ` <001901c26e8d$3f62f850$0a00a8c0@refresco>
  1 sibling, 0 replies; 18+ messages in thread
From: John Tyner @ 2002-10-07 21:51 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Greg KH, linux-kernel, linux-usb-devel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 448 bytes --]

On Mon, 7 Oct 2002, Oliver Neukum wrote:
> Well, here we go again, there are other issues as well.
> There's a race between open() and disconnect(). The best way to deal

[snip]

I believe this addresses (and hopefully corrects) this issues raised about
the race between disconnect and the device being open. It should also
correct the unlinking of the urb problem you brought up.

I even removed the ...bh... from the tasklet name.

Thanks,
John


[-- Attachment #2: Type: APPLICATION/octet-stream, Size: 16814 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [linux-usb-devel] Re: Vicam/3com homeconnect usb camera driver
       [not found]           ` <001901c26e8d$3f62f850$0a00a8c0@refresco>
@ 2002-10-08 16:30             ` Oliver Neukum
  0 siblings, 0 replies; 18+ messages in thread
From: Oliver Neukum @ 2002-10-08 16:30 UTC (permalink / raw)
  To: John Tyner, Greg KH; +Cc: linux-usb-devel, linux-kernel

On Tuesday 08 October 2002 07:40, John Tyner wrote:
> This is a resend of the patch I sent earlier. I worked on the one earlier
> while away from home and was unable to test it. This one has been fixed,
> tested, and should be correct. Sorry for the confusion.
>
> Patch is against 2.5.41.
>
> Thanks,
> John

Hi,

+	if ( waitqueue_active( &vicam_v4l_priv->no_users ) ) {
+		wake_up( &vicam_v4l_priv->no_users );

never ever do this. Always do the wake_up unconditionally.

ioctl() - VIDIOSYNC: this may hang if you unplug at the wrong moment,
as there's nobody to up the semaphore in that case.
You should do the up in the disconnect handler and check for disconnection
in the ioctl so you can return -ENODEV in that case.

in disconnect:

+	/* make sure no one will submit another urb */
+	clear_bit( 0, &vicam_v4l_priv->vicam_present );

But it does not guard against control messages in flight.
You need a semaphore to do that.

+	usb_unlink_urb( vicam_v4l_priv->urb );
+	down( &vicam_v4l_priv->busy_mutex );

This can hang for two reasons,
- you might not be the only user of that semaphore
- usb_unlink_urb may fail due to there being no queued urb,
  eg. if the completion handler is running - you need to check the return     
  value

+	if ( vdev->users ) {
+		sleep_on( &vicam_v4l_priv->no_users );
+	}

_Very_ bad idea.
First, never use sleep_on. Use the appropriate macro.
Second, you have blocked khubd without an upper time limit.
That you _must_ _not_ in any case do.

+	kfree( vicam_v4l_priv );
+	kfree( vicam_usb_priv );

Potentionally deadly if the device is open, you need to defer it to release 
in that case

Sorry for all that trouble.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 21:06 ` Pavel Machek
@ 2002-10-07 18:43   ` John Tyner
  0 siblings, 0 replies; 18+ messages in thread
From: John Tyner @ 2002-10-07 18:43 UTC (permalink / raw)
  To: Pavel Machek; +Cc: video4linux-list, linux-kernel, greg, kraxel

> How is this different from 3comhc.c from sourceforge?

Lots of little fixes, speed and memory allocation improvements and
generally more understandable (in my opinion). The one I've written
actually implements an asynchronous send/receive and decode so
the camera works a bit faster.

I had actually been developing this driver in parallel with the
sourceforge guys before I found out about them. I combined their work with
mine to finish the driver.

See these two threads. The first provides a little more background and the
second is the thread that you replied to.

http://marc.theaimsgroup.com/?t=103344771400001&r=1&w=2
http://marc.theaimsgroup.com/?t=103370436400001&r=1&w=2

Thanks,
John


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-06 21:32 John Tyner
@ 2002-10-06 21:44 ` Oliver Neukum
  0 siblings, 0 replies; 18+ messages in thread
From: Oliver Neukum @ 2002-10-06 21:44 UTC (permalink / raw)
  To: John Tyner, Greg KH; +Cc: linux-kernel

On Sunday 06 October 2002 23:32, John Tyner wrote:
> > And you should probably kill the tasklet before you unregister the video
> > device.
>
> The more I think about this, the more I think that killing the tasklet
> after unregistering the device is the correct way.
>
> From what I can tell, there are two ways that the disconnect function can
> be called: a physical disconnect or a module removal.

And as a result of an ioctl() through usbfs.

> In the case of a physical disconnect, the ordering probably doesn't matter
> because the tasklet won't be scheduled again because urb's would fail to
> complete successfully.
>
> The case of module removal becomes a bit more complicated (for reasons
> concerning module unload races that are being discussed by people far
> smarter than I). But in any event, I think that it makes more sense to
> unregister the open/close/etc. interface so that there is less chance of
> trying to send another urb (thus causing another schedule of the tasklet)
> before actually killing the tasklet.
>
> This also brings up the (somewhat) rhetorical question I posed in the
> driver's disconnect function. What happens when a disconnect occurs while
> the device is open?

I can't tell right now. I'll sync up after returning home and look into the 
matter.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
@ 2002-10-06 21:32 John Tyner
  2002-10-06 21:44 ` Oliver Neukum
  0 siblings, 1 reply; 18+ messages in thread
From: John Tyner @ 2002-10-06 21:32 UTC (permalink / raw)
  To: Oliver Neukum, Greg KH; +Cc: linux-kernel

> And you should probably kill the tasklet before you unregister the video
> device.

The more I think about this, the more I think that killing the tasklet after
unregistering the device is the correct way.

>From what I can tell, there are two ways that the disconnect function can be
called: a physical disconnect or a module removal.

In the case of a physical disconnect, the ordering probably doesn't matter
because the tasklet won't be scheduled again because urb's would fail to
complete successfully.

The case of module removal becomes a bit more complicated (for reasons
concerning module unload races that are being discussed by people far
smarter than I). But in any event, I think that it makes more sense to
unregister the open/close/etc. interface so that there is less chance of
trying to send another urb (thus causing another schedule of the tasklet)
before actually killing the tasklet.

This also brings up the (somewhat) rhetorical question I posed in the
driver's disconnect function. What happens when a disconnect occurs while
the device is open?

Thanks,
John


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-04  3:59 John Tyner
  2002-10-04  4:50 ` Greg KH
@ 2002-10-06 21:06 ` Pavel Machek
  2002-10-07 18:43   ` John Tyner
  1 sibling, 1 reply; 18+ messages in thread
From: Pavel Machek @ 2002-10-06 21:06 UTC (permalink / raw)
  To: John Tyner; +Cc: video4linux-list, linux-kernel, greg, kraxel

Hi!

> Attached is a driver for the 3Com HomeConnect USB Camera. The code is
> based somewhat on the vicam driver in the kernel (which crashes my
> computer hard) and also relies heavily on the information obtained by
> the guys doing the reverse engineering and kernel driver work at
> homeconnectusb.sourceforge.net. The fact that this driver works speaks
> very highly of them.
> 
> The driver gets the "It Works for Me" approval, but can definitely use
> some wider testing and review. Please test, review, and comment.
> 
> The code is for for 2.4.19, and I'll port it forward to 2.5 if it
> seems like it would be useful.

How is this different from 3comhc.c from sourceforge?

Anyway, this is *good stuff* (tm), as old vicam.c is not too
functional (to say at least). Basically anything is better than old
vicam.c
								Pavel
-- 
I'm pavel@ucw.cz. "In my country we have almost anarchy and I don't care."
Panos Katsaloulis describing me w.r.t. patents at discuss@linmodems.org

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-04  4:50 ` Greg KH
  2002-10-04  5:05   ` John Tyner
@ 2002-10-06  2:57   ` John Tyner
  1 sibling, 0 replies; 18+ messages in thread
From: John Tyner @ 2002-10-06  2:57 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel

> If you send me a patch for 2.5, I'd be glad to add it to the tree.

As promised, here is a patch against 2.5.40.

The 2.5 V4L interface looks like it will provide a way for multiple users to
use the same camera simultaneously, but that's a fairly drastic change, and
I'd like to start with something that is pretty well tested [at least by
me]. So, this patch is a sraight forward-port of the 2.4 version posted a
few days ago.

> Right now, I'm not accepting USB drivers that don't show up in 2.5
> first.

If desired, I'll create a patch of the 2.4 version as well.

Thanks,
John


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-04  5:05   ` John Tyner
@ 2002-10-04  5:22     ` Greg KH
  0 siblings, 0 replies; 18+ messages in thread
From: Greg KH @ 2002-10-04  5:22 UTC (permalink / raw)
  To: John Tyner; +Cc: video4linux-list, linux-kernel, kraxel

On Thu, Oct 03, 2002 at 10:05:05PM -0700, John Tyner wrote:
> > If you send me a patch for 2.5, I'd be glad to add it to the tree.
> > Right now, I'm not accepting USB drivers that don't show up in 2.5
> > first.
> 
> I'll get to work.

Great!

> > Other than that, the code looks nice.  Did you look at how the usb video
> > drivers do their memory management in 2.4.20-pre like I mentioned
> > before?
> 
> I did. The ov511 as well as the bttv and cpia drivers still use the
> rvmalloc/rvfree methods. They are minor'ly updated from the version I was
> using, and I've already incorporated those changes to the driver submitted
> in my previous post.

Ah, good, I just wanted to make sure, as I know there were some changes
there.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-04  4:50 ` Greg KH
@ 2002-10-04  5:05   ` John Tyner
  2002-10-04  5:22     ` Greg KH
  2002-10-06  2:57   ` John Tyner
  1 sibling, 1 reply; 18+ messages in thread
From: John Tyner @ 2002-10-04  5:05 UTC (permalink / raw)
  To: Greg KH; +Cc: video4linux-list, linux-kernel, kraxel

> If you send me a patch for 2.5, I'd be glad to add it to the tree.
> Right now, I'm not accepting USB drivers that don't show up in 2.5
> first.

I'll get to work.

> Other than that, the code looks nice.  Did you look at how the usb video
> drivers do their memory management in 2.4.20-pre like I mentioned
> before?

I did. The ov511 as well as the bttv and cpia drivers still use the
rvmalloc/rvfree methods. They are minor'ly updated from the version I was
using, and I've already incorporated those changes to the driver submitted
in my previous post.

Thanks,
John


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Vicam/3com homeconnect usb camera driver
  2002-10-04  3:59 John Tyner
@ 2002-10-04  4:50 ` Greg KH
  2002-10-04  5:05   ` John Tyner
  2002-10-06  2:57   ` John Tyner
  2002-10-06 21:06 ` Pavel Machek
  1 sibling, 2 replies; 18+ messages in thread
From: Greg KH @ 2002-10-04  4:50 UTC (permalink / raw)
  To: John Tyner; +Cc: video4linux-list, linux-kernel, kraxel

On Thu, Oct 03, 2002 at 08:59:30PM -0700, John Tyner wrote:
> 
> The code is for for 2.4.19, and I'll port it forward to 2.5 if it
> seems like it would be useful.
> 
> Apologies:
> 	1. The files are not a patch but should build outside of the tree.

If you send me a patch for 2.5, I'd be glad to add it to the tree.
Right now, I'm not accepting USB drivers that don't show up in 2.5
first.

Other than that, the code looks nice.  Did you look at how the usb video
drivers do their memory management in 2.4.20-pre like I mentioned
before?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Vicam/3com homeconnect usb camera driver
@ 2002-10-04  3:59 John Tyner
  2002-10-04  4:50 ` Greg KH
  2002-10-06 21:06 ` Pavel Machek
  0 siblings, 2 replies; 18+ messages in thread
From: John Tyner @ 2002-10-04  3:59 UTC (permalink / raw)
  To: video4linux-list, linux-kernel; +Cc: greg, kraxel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 888 bytes --]

Attached is a driver for the 3Com HomeConnect USB Camera. The code is
based somewhat on the vicam driver in the kernel (which crashes my
computer hard) and also relies heavily on the information obtained by
the guys doing the reverse engineering and kernel driver work at
homeconnectusb.sourceforge.net. The fact that this driver works speaks
very highly of them.

The driver gets the "It Works for Me" approval, but can definitely use
some wider testing and review. Please test, review, and comment.

The code is for for 2.4.19, and I'll port it forward to 2.5 if it
seems like it would be useful.

Apologies:
	1. The files are not a patch but should build outside of the tree.
	2. The file is woefully underdocumented
	3. There aren't any printk's in the driver.

I'm not subscribed to the list but rather lurk on the archives, so please
try to include me in any replies.

Thanks,
John

[-- Attachment #2: Type: TEXT/PLAIN, Size: 16372 bytes --]

/* -*- linux-c -*-
 * vicam.c - ViCAM/3Com HomeConnect USB Camera Driver
 *
 * Copyright (c) 2002 John Tyner <jtyner@cs.ucr.edu>
 */

#ifndef VICAM_H
#define VICAM_H

#define clamp( x, l, h ) max_t( long, min_t( long, ( x ), ( h ) ), ( l ) )

#define DEBUG_PRINT( format, args... ) \
        printk( KERN_DEBUG __FUNCTION__ ": " format, ##args )

#define USB_3COMHC_VENDOR_ID     0x04C1
#define USB_3COMHC_PRODUCT_ID    0x009D

#define VICAM_HEADER_SIZE        64
#define VICAM_FOOTER_SIZE        64

#define VICAM_NUM_FRAMES         4
#define VICAM_FRAME_SIZE         ( 3 * 320 * 242 )
#define VICAM_INPUT_SIZE         ( 512 * 242 + \
                                   VICAM_HEADER_SIZE + VICAM_FOOTER_SIZE )

#define VICAM_CNTRL_BUF_SIZE     PAGE_SIZE
#define VICAM_FRAME_BUF_SIZE     PAGE_ALIGN( VICAM_NUM_FRAMES * \
                                             VICAM_FRAME_SIZE )

#define VICAM_REQ_VENDOR         0xFF
#define VICAM_REQ_POWER          0x50
#define VICAM_REQ_CAPTURE        0x51
#define VICAM_REQ_LED            0x55

struct vicam_usb_data {
	struct video_device vicam_v4l_dev;
};

struct vicam_v4l_data {
	struct usb_device *usb_dev;
	struct urb *urb;
	struct semaphore dev_mutex;
	struct semaphore busy_mutex;
	struct semaphore decode_mutex[VICAM_NUM_FRAMES];
	struct tasklet_struct decode;
	u8 *cntrl_buf;
	u8 *input_buf[VICAM_NUM_FRAMES];
	u8 *frame_buf;
	unsigned long current_frame;
	unsigned long decode_frames;
	u32 width, height;
	u16 speed;
	u8 gain;
	u8 bulk_endpoint;
};

static const u8 setup1[] = {
	0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64, 0xE7, 0x67,
	0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09, 0xDE, 0x00,
	0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03, 0xC0, 0x17,
	0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07, 0x00, 0x00,
	0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00
};

static const u8 setup2[] = {
	0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64, 0x18, 0x00,
	0x00, 0x00
};

static const u8 setup3[] = {
	0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
};

static const u8 setup4[] = {
	0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64, 0xE7, 0x07,
	0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07, 0x00, 0x00,
	0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01, 0xAA, 0x00,
	0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00, 0xE7, 0x07,
	0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07, 0x7C, 0x00,
	0x16, 0x00, 0xE7, 0x07, 0x56, 0x00, 0x18, 0x00,
	0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0, 0xE7, 0x07,
	0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF,
	0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00, 0x24, 0xC0,
	0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0, 0xE7, 0x07,
	0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87, 0x01, 0x00,
	0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09, 0x00, 0xC0,
	0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0, 0x09, 0xC1,
	0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01, 0xE7, 0x09,
	0x04, 0x06, 0x26, 0x01, 0xE7, 0x07, 0x07, 0x00,
	0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0, 0xC0, 0xDF,
	0x97, 0xCF, 0x17, 0x00, 0x57, 0x00, 0x17, 0x02,
	0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00,
	0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57, 0xFF, 0xFF,
	0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57, 0x00, 0x00,
	0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF, 0xC6, 0x00,
	0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05, 0xC1, 0x05,
	0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF, 0x27, 0xDA,
	0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x0B, 0x06,
	0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01, 0x9F, 0xAF,
	0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09, 0xFC, 0x05,
	0x24, 0x01, 0xE7, 0x09, 0x02, 0x06, 0x26, 0x01,
	0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0, 0xE7, 0x09,
	0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09, 0x02, 0x06,
	0x04, 0x06, 0xE7, 0x09, 0x00, 0x06, 0xFC, 0x05,
	0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06, 0x27, 0xDA,
	0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00, 0xFA, 0x05,
	0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF, 0x9F, 0xAF,
	0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07, 0x40, 0x00,
	0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05, 0x9F, 0xAF,
	0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17, 0x02, 0x00,
	0xEF, 0x57, 0x81, 0x00, 0x09, 0x06, 0x9F, 0xA0,
	0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00, 0x09, 0x06,
	0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57, 0x01, 0x00,
	0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03, 0xE7, 0x07,
	0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF, 0x47, 0xAF,
	0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07, 0x2E, 0x00,
	0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06,
	0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00, 0xC0, 0x57,
	0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02, 0xC0, 0x57,
	0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57, 0x55, 0x00,
	0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00, 0x9F, 0xC0,
	0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02, 0xC1, 0x0B,
	0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90, 0x08, 0x06,
	0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06,
	0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF, 0x2F, 0x0E,
	0x02, 0x00, 0x08, 0x06, 0xC0, 0x07, 0x08, 0x00,
	0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05,
	0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E, 0x02, 0x00,
	0x09, 0x06, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06,
	0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67, 0x7F, 0xFF,
	0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD, 0x22, 0xC0,
	0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0, 0xE7, 0x87,
	0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF, 0xB8, 0x05,
	0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0, 0x9F, 0xAF,
	0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00, 0x24, 0xC0,
	0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0, 0xC8, 0x07,
	0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC1, 0x07,
	0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0x9F, 0xAF,
	0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00, 0x9F, 0xAF,
	0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0,
	0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01,
	0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02, 0x0F, 0xC1,
	0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x67,
	0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x08, 0x00,
	0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1, 0xEF, 0x07,
	0x80, 0x00, 0x09, 0x06, 0x97, 0xCF, 0xEF, 0x07,
	0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF, 0xEF, 0x07,
	0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07, 0x00, 0x00,
	0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF, 0x09, 0x06,
	0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xE7, 0x67,
	0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67, 0x17, 0xD8,
	0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0,
	0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0, 0x97, 0xCF,
	0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF, 0xDA, 0x02,
	0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07,
	0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0x0E, 0x06,
	0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02, 0xF8, 0x05,
	0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80,
	0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C, 0x02, 0x00,
	0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00, 0x06, 0x06,
	0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07,
	0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0xE2, 0x05,
	0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02, 0xF8, 0x05,
	0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80,
	0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF, 0x66, 0x04,
	0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF, 0x97, 0xCF,
	0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B, 0x0C, 0x06,
	0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
	0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0x68, 0x00,
	0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF, 0x44, 0x05,
	0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00, 0xE0, 0x07,
	0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06, 0xE8, 0x07,
	0x00, 0x00, 0xE0, 0x07, 0x00, 0x02, 0xE0, 0x07,
	0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF, 0x97, 0xCF,
	0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05, 0xEF, 0x07,
	0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07, 0x0E, 0x06,
	0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06, 0xFE, 0x05,
	0xE7, 0x07, 0x40, 0x00, 0x26, 0x01, 0xE7, 0x07,
	0x40, 0x00, 0x04, 0x06, 0xE7, 0x07, 0x07, 0x00,
	0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07, 0x02, 0x00,
	0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01, 0xEF, 0x77,
	0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0, 0x14, 0x04,
	0xEF, 0x77, 0x01, 0x00, 0x07, 0x06, 0x37, 0xC0,
	0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06, 0x0F, 0xC1,
	0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06, 0xC0, 0x07,
	0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, 0x9F, 0xAF,
	0x28, 0x05, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07,
	0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07,
	0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07,
	0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x77,
	0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA, 0x75, 0xC1,
	0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1, 0xC0, 0x07,
	0x01, 0x00, 0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF,
	0x28, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x06, 0x06,
	0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07,
	0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07,
	0x00, 0x00, 0x06, 0x06, 0x22, 0xCF, 0xEF, 0x07,
	0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57, 0x01, 0x00,
	0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07, 0x01, 0x00,
	0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05,
	0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00,
	0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, 0xFF, 0x4F,
	0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, 0x38, 0x00,
	0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67, 0x03, 0x00,
	0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0, 0x08, 0xDA,
	0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07,
	0x12, 0x00, 0xEF, 0x57, 0x00, 0x00, 0x06, 0x06,
	0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00, 0x9F, 0xAF,
	0x28, 0x05, 0xC0, 0x07, 0x14, 0x00, 0xC1, 0x0B,
	0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
	0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x09,
	0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8, 0xFA, 0x05,
	0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05, 0xE7, 0x07,
	0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07, 0x40, 0x00,
	0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05, 0x9F, 0xAF,
	0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B, 0xE2, 0x05,
	0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17, 0x23, 0x00,
	0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07, 0x04, 0x00,
	0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF, 0x28, 0x05,
	0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09, 0xE6, 0x05,
	0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x07, 0x00,
	0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1, 0x9F, 0xAF,
	0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00, 0xC1, 0x09,
	0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
	0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05, 0xC1, 0xD1,
	0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0D, 0x00,
	0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF, 0x28, 0x05,
	0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07, 0x32, 0x00,
	0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0F, 0x00,
	0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF, 0x28, 0x05,
	0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9, 0x24, 0xC0,
	0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00, 0xC0, 0x67,
	0x00, 0x02, 0x27, 0x80, 0x24, 0xC0, 0xE7, 0x87,
	0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xF9,
	0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA, 0x72, 0xC1,
	0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0, 0x97, 0xCF,
	0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87, 0xFF, 0x00,
	0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF, 0x24, 0xC0,
	0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0xE7, 0x87,
	0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF,
	0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0, 0xE7, 0x67,
	0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00,
	0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0,
	0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x67,
	0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE,
	0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0,
	0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01,
	0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07, 0x40, 0x00,
	0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67, 0x00, 0xFF,
	0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0,
	0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x67,
	0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA, 0xE8, 0x09,
	0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0,
	0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, 0x00, 0xDA,
	0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1, 0xE7, 0x87,
	0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xE7, 0x07,
	0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80,
	0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0xE7, 0x07,
	0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80,
	0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0x09, 0x02,
	0x19, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09,
	0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
	0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

static const u8 setup5[] = {
	0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64, 0x0E, 0x00,
	0x14, 0x00, 0x1A, 0x00, 0x20, 0x00, 0x26, 0x00,
	0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00, 0x92, 0x00,
	0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00, 0xB8, 0x00,
	0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00, 0xCE, 0x00,
	0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00, 0xE0, 0x00,
	0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00, 0xEC, 0x00,
	0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01, 0x0A, 0x01,
	0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01, 0x22, 0x01,
	0x28, 0x01, 0x2C, 0x01, 0x32, 0x01, 0x36, 0x01,
	0x44, 0x01, 0x50, 0x01, 0x5E, 0x01, 0x72, 0x01,
	0x76, 0x01, 0x7A, 0x01, 0x80, 0x01, 0x88, 0x01,
	0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01, 0xA0, 0x01,
	0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01, 0xB4, 0x01,
	0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01, 0xF6, 0x01,
	0xFA, 0x01, 0x02, 0x02, 0x34, 0x02, 0x3C, 0x02,
	0x44, 0x02, 0x4A, 0x02, 0x50, 0x02, 0x56, 0x02,
	0x74, 0x02, 0x78, 0x02, 0x7E, 0x02, 0x84, 0x02,
	0x8A, 0x02, 0x88, 0x02, 0x90, 0x02, 0x8E, 0x02,
	0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02, 0xAE, 0x02,
	0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02, 0xC0, 0x02,
	0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02, 0xD4, 0x02,
	0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02, 0xF8, 0x02,
	0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03, 0x24, 0x03,
	0x28, 0x03, 0x30, 0x03, 0x2E, 0x03, 0x3C, 0x03,
	0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03, 0x58, 0x03,
	0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03, 0x7A, 0x03,
	0x86, 0x03, 0x8E, 0x03, 0x96, 0x03, 0xB2, 0x03,
	0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03, 0xD4, 0x03,
	0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03, 0xFC, 0x03,
	0x04, 0x04, 0x20, 0x04, 0x2A, 0x04, 0x32, 0x04,
	0x36, 0x04, 0x3E, 0x04, 0x44, 0x04, 0x42, 0x04,
	0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04, 0x54, 0x04,
	0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04, 0x62, 0x04,
	0x68, 0x04, 0x74, 0x04, 0x7C, 0x04, 0x80, 0x04,
	0x88, 0x04, 0x8C, 0x04, 0x94, 0x04, 0x9A, 0x04,
	0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04, 0xB4, 0x04,
	0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04, 0x2A, 0x05,
	0x46, 0x05, 0x6C, 0x05, 0x00, 0x00
};

static const u8 setup6[] = {
	0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
};

static const struct {
	const u8 * const firmware;
	const size_t size;
} setup[] = {
	{
		.firmware = setup1,
		.size = sizeof( setup1 )
	}, {
		.firmware = setup2,
		.size = sizeof( setup2 )
	}, {
		.firmware = setup3,
		.size = sizeof( setup3 )
	}, {
		.firmware = setup4,
		.size = sizeof( setup4 )
	}, {
		.firmware = setup5,
		.size = sizeof( setup5 )
	}, {
		.firmware = setup6,
		.size = sizeof( setup6 )
	}, {
		.firmware = NULL,
		.size = 0
	}
};

static inline unsigned long kvirt_to_pa(unsigned long adr)
{
        unsigned long kva, ret;

        kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
        kva |= adr & (PAGE_SIZE-1); /* restore the offset */
        ret = __pa(kva);
        return ret;
}

static void *rvmalloc(unsigned long size)
{
        void *mem;
        unsigned long adr;

        size = PAGE_ALIGN(size);

        mem = vmalloc_32(size);
        if (!mem)
                return NULL;

        memset(mem, 0, size); /* Clear the ram out, no junk to the user */
        adr = (unsigned long) mem;
        while ((long) size > 0) {
                mem_map_reserve(vmalloc_to_page((void *)adr));
                adr += PAGE_SIZE;
                size -= PAGE_SIZE;
        }

        return mem;
}

static void rvfree(void *mem, unsigned long size)
{
        unsigned long adr;

        if (!mem)
                return;

	size = PAGE_ALIGN(size);

        adr = (unsigned long) mem;
        while ((long) size > 0) {
                mem_map_unreserve(vmalloc_to_page((void *)adr));
                adr += PAGE_SIZE;
                size -= PAGE_SIZE;
        }

        vfree(mem);
}

#endif

[-- Attachment #3: Type: TEXT/PLAIN, Size: 16781 bytes --]

/* -*- linux-c -*-
 * vicam.c - ViCAM/3Com HomeConnect USB Camera Driver
 *
 * Copyright (c) 2002 John Tyner <jtyner@cs.ucr.edu>
 *
 * This driver is for the 3Com HomeConnect USB Camera and
 * as far as I know, the Vista Imaging ViCAM Camera. The
 * code is based on the driver created by the team at
 * homeconnectusb.sourceforge.net. They deserve the credit
 * for the decoding algorithm and for figuring out what values
 * and messages need to be sent to the camera and what data
 * is actually coming back.
 *
 * This file is woefully underdocumented and doesn't print
 * any information. I apologize for that, but the driver is
 * working (for me), and doesn't need them. *duck*
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/videodev.h>
#include <linux/vmalloc.h>
#include <linux/wrapper.h>

#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "vicam.h"

int reverse_rgb = 0;
int shutter_speed = 30;

static int vicam_send_control_msg( struct vicam_v4l_data *vicam_v4l_priv,
				   u8 request, u16 value, u16 size )
{
	struct usb_device *usb_dev = vicam_v4l_priv->usb_dev;
	int ret;

	/* for some reason we can't pass data directly
	 * to usb_control_msg. therefore, we assume that
	 * the caller has already placed their data into
	 * the cntrl_buf.
	 */

	ret = usb_control_msg(
		usb_dev,
		usb_sndctrlpipe( usb_dev, 0 ),
		request,
		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
		value,
		0,
		vicam_v4l_priv->cntrl_buf,
		size,
		HZ );

	return min( ret, 0 );
}

static void vicam_urb_complete( struct urb *vicam_v4l_urb )
{
	struct vicam_v4l_data *vicam_v4l_priv;

	vicam_v4l_priv = vicam_v4l_urb->context;

	if ( !vicam_v4l_urb->status ) {
		set_bit( vicam_v4l_priv->current_frame,
			 &vicam_v4l_priv->decode_frames );
		tasklet_schedule( &vicam_v4l_priv->decode );
	}

	up( &vicam_v4l_priv->busy_mutex );
}

static int vicam_capture_image( struct vicam_v4l_data *vicam_v4l_priv,
				unsigned long frame_num )
{
	u8 * const request = vicam_v4l_priv->cntrl_buf;
	int err;

	if ( vicam_v4l_priv->speed < 2 || vicam_v4l_priv->speed > 60 ) {
		return -EINVAL;
	}

	memset( request, 0, 16 );

	request[0] = vicam_v4l_priv->gain;

	if ( vicam_v4l_priv->width <= 256 ) {
		request[1] |= ( vicam_v4l_priv->width > 128 ) ? 0x01 : 0x03;
	}

	if ( vicam_v4l_priv->height <= 180 ) {
		request[1] |= ( vicam_v4l_priv->height > 121 ) ? 0x20 : 0x30;
	}

	request[2] = 0x90;
	request[3] = 0x07;

	*( ( u16 * )( &request[6] ) ) =
		cpu_to_le16( 15600 / vicam_v4l_priv->speed - 1 );

	err = down_interruptible( &vicam_v4l_priv->busy_mutex );
	if ( err ) {
		return err;
	}

	vicam_v4l_priv->current_frame = frame_num;

	err = vicam_send_control_msg( vicam_v4l_priv,
				      VICAM_REQ_CAPTURE,
				      0x80,
				      16 );

	if ( err ) {
		goto done;
	}

	usb_fill_bulk_urb( vicam_v4l_priv->urb,
			   vicam_v4l_priv->usb_dev,
			   usb_rcvbulkpipe( vicam_v4l_priv->usb_dev,
					    vicam_v4l_priv->bulk_endpoint ),
			   vicam_v4l_priv->input_buf[frame_num],
			   vicam_v4l_priv->width * vicam_v4l_priv->height +
			   VICAM_HEADER_SIZE + VICAM_FOOTER_SIZE,
			   vicam_urb_complete,
			   vicam_v4l_priv );

	err = usb_submit_urb( vicam_v4l_priv->urb );

 done:
	if ( err ) {
		up( &vicam_v4l_priv->busy_mutex );
	}

	return err;
}

static void vicam_decode_frame( struct vicam_v4l_data *vicam_v4l_priv,
				unsigned long frame_num )
{
	const u32 width = vicam_v4l_priv->width,
		height = vicam_v4l_priv->height;
	u8 *ibuf, *obuf;
	int y;

	ibuf = &vicam_v4l_priv->input_buf[frame_num][VICAM_HEADER_SIZE];
	obuf = vicam_v4l_priv->frame_buf + ( VICAM_FRAME_SIZE * frame_num );

	for ( y = 0; y < height; y++, ibuf += width ) {
		int dy = width;
		int x_out;

		if ( unlikely( y >= height - 1 ) ) {
			dy = -dy;
		}

		for ( x_out = 0; x_out < 320; x_out++ ) {
			int dx, cx, cy, luma, x, i;
			int rgbc[3];
			u8 sp[4];

			x = ( x_out * width ) / 320;

			dx = 1;

			if ( unlikely( x >= width - 1 ) ) {
				dx = -dx;
			}

			i = ( y & 1 ) | ( ( x & 1 ) << 1 );

			sp[i ^ 0] = ibuf[x];
			sp[i ^ 1] = ibuf[x + dy];
			sp[i ^ 2] = ibuf[x + dx];
			sp[i ^ 3] = ibuf[x + dy + dx];

			cx = sp[3] - sp[1];
			cy = sp[2] - sp[0];

			rgbc[0] = cy + ( cx / 2 );

			cy /= -2;
			cx *= 13; cx /= 15;

			rgbc[1] = cy + cx;
			rgbc[2] = cy - cx;

			luma = ( int )( ibuf[x] + ibuf[x + dx] ) >> 1;

			for ( i = 0; i < 3; i++, obuf++ ) {
				int j = i;

				if ( reverse_rgb ) {
					j = ( 2 - i );
				}

				*obuf = clamp( luma + rgbc[j], 0, 255 );
			}
		}
	}

	up( &vicam_v4l_priv->decode_mutex[frame_num] );
}

static void vicam_bh_handler( unsigned long _vicam_v4l_priv )
{
	struct vicam_v4l_data *vicam_v4l_priv =
		( struct vicam_v4l_data * )( _vicam_v4l_priv );
	int i;

	do {
		i = ffs( vicam_v4l_priv->decode_frames );
		vicam_decode_frame( vicam_v4l_priv, i - 1 );
		clear_bit( i - 1, &vicam_v4l_priv->decode_frames );
	} while ( vicam_v4l_priv->decode_frames );
}

static int vicam_v4l_open( struct video_device *dev, int mode )
{
	struct vicam_v4l_data *vicam_v4l_priv = dev->priv;
	int err;

	err = down_interruptible( &vicam_v4l_priv->dev_mutex );
	if ( err ) {
		return err;
	}

	err = vicam_send_control_msg( vicam_v4l_priv, VICAM_REQ_POWER, 1, 0 );
	vicam_send_control_msg( vicam_v4l_priv, VICAM_REQ_LED, 3, 0 );

	if ( err ) {
		up( &vicam_v4l_priv->dev_mutex );
	}

	return err;
}

static void vicam_v4l_close( struct video_device *dev )
{
	struct vicam_v4l_data *vicam_v4l_priv = dev->priv;

	vicam_send_control_msg( vicam_v4l_priv, VICAM_REQ_POWER, 0, 0 );

	up( &vicam_v4l_priv->dev_mutex );
}

static long vicam_v4l_write( struct video_device *dev, const char *buf,
			     unsigned long count, int noblock )
{
	return -EPERM;
}

static int vicam_v4l_ioctl( struct video_device *dev, unsigned int cmd,
			    void *arg )
{
	struct vicam_v4l_data *vicam_v4l_priv;
	struct video_capability *vcap;
	struct video_channel *vchan;
	struct video_mbuf *vmbuf;
	struct video_picture *vpic;
	struct video_mmap *vmmap;
	struct video_window *vwin;
	void *buf;
	int buf_size, err, i;

	vicam_v4l_priv = dev->priv;

	err = 0;

	buf_size = max_t( int, sizeof( *vcap ),
			  max_t( int, sizeof( *vchan ),
				 max_t( int, sizeof( *vmbuf ),
					max_t( int, sizeof( *vpic ),
					       max_t( int, sizeof( *vmmap ),
						      sizeof( *vwin ) ) ) ) ) );

	if ( unlikely( buf_size > VICAM_CNTRL_BUF_SIZE ) ) {
		return -ENOMEM;
	}

	buf = vicam_v4l_priv->cntrl_buf;
	buf_size = 0;

	vcap  = buf;
	vchan = buf;
	vmbuf = buf;
	vpic  = buf;
	vmmap = buf;
	vwin  = buf;

	switch ( cmd ) {
	case VIDIOCGCAP:
		buf_size = sizeof( *vcap );

		memset( buf, 0, buf_size );

		strcpy( vcap->name, dev->name );
		vcap->type = dev->type;

		vcap->channels = 1;
		vcap->audios = 0;

		vcap->maxwidth = 320;
		vcap->maxheight = 242;

		vcap->minwidth = 320;
		vcap->minheight = 242;

		break;
	case VIDIOCGCHAN:
		if ( copy_from_user( vchan, arg, sizeof( *vchan ) ) ) {
			err = -EFAULT;
			break;
		}

		if ( vchan->channel ) {
			err = -EINVAL;
			break;
		}

		strcpy( vchan->name, dev->name );

		vchan->flags = 0;
		vchan->tuners = 0;

		vchan->type = VIDEO_TYPE_CAMERA;

		vchan->norm = 0;

		buf_size = sizeof( *vchan );

		break;
	case VIDIOCSCHAN:
		if ( copy_from_user( vchan, arg, sizeof( *vchan ) ) ) {
			err = -EFAULT;
			break;
		}

		if ( vchan->channel ) {
			err = -EINVAL;
			break;
		}

		break;
	case VIDIOCGPICT:
		buf_size = sizeof( *vpic );

		memset( vpic, 0, buf_size );

		vpic->brightness = vicam_v4l_priv->gain << 8;
		vpic->depth = 24;
		vpic->palette = VIDEO_PALETTE_RGB24;

		break;
	case VIDIOCSPICT:
		if ( copy_from_user( vpic, arg, sizeof( *vpic ) ) ) {
			err = -EFAULT;
			break;
		}

		if ( vpic->depth != 24 ||
		     vpic->palette != VIDEO_PALETTE_RGB24 ) {
			err = -EINVAL;
		}

		vicam_v4l_priv->gain = vpic->brightness >> 8;

		break;
	case VIDIOCGWIN:
		buf_size = sizeof( *vwin );

		memset( vwin, 0, buf_size );

		vwin->x = 0;
		vwin->y = 0;

		vwin->width = 320;
		vwin->height = vicam_v4l_priv->height;

		vwin->chromakey = 0;
		vwin->flags = 0;
		vwin->clips = NULL;
		vwin->clipcount = 0;

		break;
	case VIDIOCSWIN:
		if ( vwin->x != 0 || vwin->y != 0 ) {
			err = -EINVAL;
		}

		if ( vwin->width != 320 ||
		     vwin->height != vicam_v4l_priv->height ) {
			err = -EINVAL;
		}

		break;
	case VIDIOCGMBUF:
		buf_size = sizeof( *vmbuf );

		memset( vmbuf, 0, buf_size );

		vmbuf->frames = min( VICAM_NUM_FRAMES, VIDEO_MAX_FRAME );
		vmbuf->size = VICAM_FRAME_SIZE * vmbuf->frames;

		for ( i = 0; i < vmbuf->frames; i++ ) {
			vmbuf->offsets[i] = VICAM_FRAME_SIZE * i;
		}

		break;
	case VIDIOCMCAPTURE:
		if ( copy_from_user( vmmap, arg, sizeof( *vmmap ) ) ) {
			err = -EFAULT;
			break;
		}

		if ( vmmap->frame > VICAM_NUM_FRAMES ||
		     vmmap->format != VIDEO_PALETTE_RGB24 ) {
			err = -EINVAL;
			break;
		}

		err = vicam_capture_image( vicam_v4l_priv, vmmap->frame );

		break;
	case VIDIOCSYNC:
		if ( copy_from_user( &i, arg, sizeof( i ) ) ) {
			err = -EFAULT;
			break;
		}

		if ( i > VICAM_NUM_FRAMES ) {
			err = -EINVAL;
			break;
		}

		err = down_interruptible( &vicam_v4l_priv->decode_mutex[i] );

		break;
	default:
		err = -ENOIOCTLCMD;
		break;
	}

	if ( !err && buf_size ) {
		err = copy_to_user( arg, buf, buf_size ) ? -EFAULT : 0;
	}

	return err;
}

static int vicam_v4l_mmap( struct video_device *dev, const char *addr,
			   unsigned long size )
{
	struct vicam_v4l_data *vicam_v4l_priv = dev->priv;
	unsigned long start = ( unsigned long )( addr );
	unsigned long pos;

	if ( size > VICAM_FRAME_BUF_SIZE ) {
		return -EINVAL;
	}

	pos = ( unsigned long )( vicam_v4l_priv->frame_buf );

	while ( size > 0 ) {
		unsigned long page = kvirt_to_pa( pos );

		if ( remap_page_range( start,
				       page,
				       PAGE_SIZE,
				       PAGE_SHARED ) ){
			return -EAGAIN;
		}

		start += PAGE_SIZE;
		pos += PAGE_SIZE;

		/* in case size isn't a multiple of PAGE_SIZE */

		size -= min( size, PAGE_SIZE );
	}

	return 0;
}

static int __devinit vicam_v4l_initialize( struct video_device *dev )
{
	struct vicam_v4l_data *vicam_v4l_priv;
	int err;
	int i;

	vicam_v4l_priv = kmalloc( sizeof( *vicam_v4l_priv ), GFP_KERNEL );
	if ( !vicam_v4l_priv ) {
		return -ENOMEM;
	}

	memset( vicam_v4l_priv, 0, sizeof( *vicam_v4l_priv ) );

	err = -ENOMEM;

	vicam_v4l_priv->urb = usb_alloc_urb( 0 );
	if ( !vicam_v4l_priv->urb ) {
		goto done;
	}

	vicam_v4l_priv->frame_buf = rvmalloc( VICAM_FRAME_BUF_SIZE );
	if ( !vicam_v4l_priv->frame_buf ) {
		goto done;
	}

	vicam_v4l_priv->cntrl_buf = kmalloc( VICAM_CNTRL_BUF_SIZE,
					     GFP_KERNEL );
	if ( !vicam_v4l_priv->cntrl_buf ) {
		goto done;
	}

	for ( i = 0; i < VICAM_NUM_FRAMES; i++ ) {
		vicam_v4l_priv->input_buf[i] = kmalloc( VICAM_INPUT_SIZE,
							GFP_KERNEL );
		if ( !vicam_v4l_priv->input_buf[i] ) {
			goto done;
		}
	}

	/* the usb_device was passed to us from the vicam_usb_probe
	 * function through our dev->priv pointer.
	 */

	vicam_v4l_priv->usb_dev = dev->priv;
	dev->priv = vicam_v4l_priv;

	tasklet_init( &vicam_v4l_priv->decode,
		      vicam_bh_handler,
		      ( unsigned long )( vicam_v4l_priv ) );

	init_MUTEX( &vicam_v4l_priv->dev_mutex );
	init_MUTEX( &vicam_v4l_priv->busy_mutex );

	for ( i = 0; i < VICAM_NUM_FRAMES; i++ ) {
		init_MUTEX_LOCKED( &vicam_v4l_priv->decode_mutex[i] );
	}

	vicam_v4l_priv->width  = 512;
	vicam_v4l_priv->height = 242;
	vicam_v4l_priv->speed  = shutter_speed;

	for ( i = 0, err = 0; setup[i].firmware && !err; i++ ) {
		memcpy( vicam_v4l_priv->cntrl_buf,
			setup[i].firmware,
			setup[i].size );

		err = vicam_send_control_msg( vicam_v4l_priv,
					      VICAM_REQ_VENDOR,
					      0,
					      setup[i].size );
	}

 done:
	if ( err ) {
		for ( i = 0; i < VICAM_NUM_FRAMES; i++ ) {
			if ( vicam_v4l_priv->input_buf[i] ) {
				kfree( vicam_v4l_priv->input_buf[i] );
			}
		}

		if ( vicam_v4l_priv->cntrl_buf ) {
			kfree( vicam_v4l_priv->cntrl_buf );
		}

		if ( vicam_v4l_priv->frame_buf ) {
			rvfree( vicam_v4l_priv->frame_buf,
				VICAM_FRAME_BUF_SIZE );
		}

		if ( vicam_v4l_priv->urb ) {
			usb_free_urb( vicam_v4l_priv->urb );
		}

		kfree( vicam_v4l_priv );
	}

	return err;
}

static void * __devinit vicam_usb_probe( struct usb_device *dev,
					 unsigned int ifnum,
					 const struct usb_device_id *devid )
{
	struct vicam_usb_data *vicam_usb_priv;
	const struct usb_interface *usb_iface;
	int err;
	u8 endpt_addr, endpt_attr;

	vicam_usb_priv = NULL;

	usb_iface = usb_ifnum_to_if( dev, ifnum );

	if ( usb_iface->num_altsetting != 1 ) {
		return NULL;
	}

	endpt_addr = usb_iface->altsetting[0].endpoint[0].bEndpointAddress;
	endpt_attr = usb_iface->altsetting[0].endpoint[0].bmAttributes;

	if ( !( endpt_addr & USB_ENDPOINT_DIR_MASK ) ||
	     ( endpt_attr & USB_ENDPOINT_XFERTYPE_MASK ) !=
	     USB_ENDPOINT_XFER_BULK ) {
		return NULL;
	}

	vicam_usb_priv = kmalloc( sizeof( *vicam_usb_priv ), GFP_KERNEL );
	if ( !vicam_usb_priv ) {
		return NULL;
	}

	memset( vicam_usb_priv, 0, sizeof( *vicam_usb_priv ) );

	usb_string( dev, dev->descriptor.iProduct,
		    vicam_usb_priv->vicam_v4l_dev.name, 32 );

	vicam_usb_priv->vicam_v4l_dev.type       = VID_TYPE_CAPTURE;
	vicam_usb_priv->vicam_v4l_dev.hardware   = 0; /* need own id */

	vicam_usb_priv->vicam_v4l_dev.initialize = vicam_v4l_initialize;
	vicam_usb_priv->vicam_v4l_dev.open       = vicam_v4l_open;
	vicam_usb_priv->vicam_v4l_dev.close      = vicam_v4l_close;
	vicam_usb_priv->vicam_v4l_dev.write      = vicam_v4l_write;
	vicam_usb_priv->vicam_v4l_dev.ioctl      = vicam_v4l_ioctl;
	vicam_usb_priv->vicam_v4l_dev.mmap       = vicam_v4l_mmap;

	vicam_usb_priv->vicam_v4l_dev.owner      = THIS_MODULE;

	/* we need to get the usb_device struct to the
	 * vicam_v4l_initialize function [preferably]
	 * without using globals.
	 */

	vicam_usb_priv->vicam_v4l_dev.priv       = dev;

	err = video_register_device ( &vicam_usb_priv->vicam_v4l_dev,
				      VFL_TYPE_GRABBER, -1 );

	if ( err ) {
		kfree( vicam_usb_priv );
		vicam_usb_priv = NULL;
	} else {
		struct vicam_v4l_data *vicam_v4l_priv =
			vicam_usb_priv->vicam_v4l_dev.priv;

		vicam_v4l_priv->bulk_endpoint = endpt_addr;
	}

	return vicam_usb_priv;
}

static void __devexit vicam_usb_disconnect( struct usb_device *dev, void *data )
{
	struct vicam_usb_data *vicam_usb_priv;
	struct vicam_v4l_data *vicam_v4l_priv;
	int i;

	vicam_usb_priv = data;
	vicam_v4l_priv = vicam_usb_priv->vicam_v4l_dev.priv;

	/* what happens if a disconnect occurs while the device is open? */

	video_unregister_device( &vicam_usb_priv->vicam_v4l_dev );

	tasklet_kill( &vicam_v4l_priv->decode );

	usb_free_urb( vicam_v4l_priv->urb );
	rvfree( vicam_v4l_priv->frame_buf, VICAM_FRAME_BUF_SIZE );
	kfree( vicam_v4l_priv->cntrl_buf );

	for ( i = 0; i < VICAM_NUM_FRAMES; i++ ) {
		if ( vicam_v4l_priv->input_buf[i] ) {
			kfree( vicam_v4l_priv->input_buf[i] );
		}
	}

	kfree( vicam_v4l_priv );
	kfree( vicam_usb_priv );
}

static struct usb_device_id __devinitdata vicam_usb_table[] = {
	{ USB_DEVICE( USB_3COMHC_VENDOR_ID, USB_3COMHC_PRODUCT_ID ) },
	/* That's all folks */
	{ .match_flags = 0 }
};

static struct usb_driver vicam_usb_drv = {
	.name = "vicam_usb",
	.probe = vicam_usb_probe,
	.disconnect = __devexit_p( vicam_usb_disconnect ),
	.id_table = vicam_usb_table
};

static int __init vicam_usb_init( void )
{
	reverse_rgb = clamp( reverse_rgb, 0, 1 );
	shutter_speed = clamp( shutter_speed, 2, 60 );

	return usb_register( &vicam_usb_drv );
}

static void __exit vicam_usb_exit( void )
{
	usb_deregister( &vicam_usb_drv );
}

module_init( vicam_usb_init );
module_exit( vicam_usb_exit );

MODULE_DEVICE_TABLE( usb, vicam_usb_table );

MODULE_PARM( reverse_rgb, "i" );
MODULE_PARM_DESC( reverse_rgb, "Perform RGB to BGR conversion" );

MODULE_PARM( shutter_speed, "i" );

MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "John Tyner <jtyner@cs.ucr.edu>" );
MODULE_DESCRIPTION( "ViCAM USB/V4L Driver" );

[-- Attachment #4: Type: TEXT/PLAIN, Size: 213 bytes --]

INCLUDE = -I /usr/src/linux/include
DEFINES = -D__KERNEL__ -DMODULE
CFLAGS = -O2 -Wall -Werror

vicam.o: vicam.c vicam.h Makefile
	gcc $(CFLAGS) $(INCLUDE) $(DEFINES) -c $< -o $@

clean:
	rm -f *.o *~ *#

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2002-10-08 22:07 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-10-06  2:58 Vicam/3com homeconnect usb camera driver John Tyner
2002-10-06 10:11 ` Oliver Neukum
2002-10-06 17:35   ` John Tyner
2002-10-06 22:01     ` Oliver Neukum
2002-10-06 23:25       ` John Tyner
2002-10-07  4:55         ` Oliver Neukum
2002-10-07  9:43         ` Oliver Neukum
2002-10-07 21:51           ` John Tyner
     [not found]           ` <001901c26e8d$3f62f850$0a00a8c0@refresco>
2002-10-08 16:30             ` [linux-usb-devel] " Oliver Neukum
  -- strict thread matches above, loose matches on Subject: below --
2002-10-06 21:32 John Tyner
2002-10-06 21:44 ` Oliver Neukum
2002-10-04  3:59 John Tyner
2002-10-04  4:50 ` Greg KH
2002-10-04  5:05   ` John Tyner
2002-10-04  5:22     ` Greg KH
2002-10-06  2:57   ` John Tyner
2002-10-06 21:06 ` Pavel Machek
2002-10-07 18:43   ` John Tyner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).