aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Jacquet <royale@zerezo.com>2007-04-27 11:30:59 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-04-27 14:43:17 -0400
commitb7eee616ad8db5db5441a7d82083003df3ab6d3b (patch)
tree10cf679ba1ff49a14f335e4147df52a69e9f1ff5
parenta205752d1ad2d37d6597aaae5a56fc396a770868 (diff)
V4L/DVB (5257): USB: add zr364xx V4L2 driver
This patch adds a V4L2 driver giving support for USB webcams based on the zr364xx chipsets. Signed-off-by: Antoine Jacquet <royale@zerezo.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--Documentation/video4linux/zr364xx.txt65
-rw-r--r--MAINTAINERS32
-rw-r--r--drivers/media/video/Kconfig12
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/zr364xx.c929
5 files changed, 1027 insertions, 12 deletions
diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt
new file mode 100644
index 000000000000..c76992d0ff4d
--- /dev/null
+++ b/Documentation/video4linux/zr364xx.txt
@@ -0,0 +1,65 @@
1Zoran 364xx based USB webcam module version 0.72
2site: http://royale.zerezo.com/zr364xx/
3mail: royale@zerezo.com
4
5introduction:
6This brings support under Linux for the Aiptek PocketDV 3300 in webcam mode.
7If you just want to get on your PC the pictures and movies on the camera, you should use the usb-storage module instead.
8The driver works with several other cameras in webcam mode (see the list below).
9Maybe this code can work for other JPEG/USB cams based on the Coach chips from Zoran?
10Possible chipsets are : ZR36430 (ZR36430BGC) and maybe ZR36431, ZR36440, ZR36442...
11You can try the experience changing the vendor/product ID values (look at the source code).
12You can get these values by looking at /var/log/messages when you plug your camera, or by typing : cat /proc/bus/usb/devices.
13If you manage to use your cam with this code, you can send me a mail (royale@zerezo.com) with the name of your cam and a patch if needed.
14This is a beta release of the driver.
15Since version 0.70, this driver is only compatible with V4L2 API and 2.6.x kernels.
16If you need V4L1 or 2.4x kernels support, please use an older version, but the code is not maintained anymore.
17Good luck!
18
19install:
20In order to use this driver, you must compile it with your kernel.
21Location: Device Drivers -> Multimedia devices -> Video For Linux -> Video Capture Adapters -> V4L USB devices
22
23usage:
24modprobe zr364xx debug=X mode=Y
25 - debug : set to 1 to enable verbose debug messages
26 - mode : 0 = 320x240, 1 = 160x120, 2 = 640x480
27You can then use the camera with V4L2 compatible applications, for example Ekiga.
28To capture a single image, try this: dd if=/dev/video0 of=test.jpg bs=1 count=1
29
30links :
31http://mxhaard.free.fr/ (support for many others cams including some Aiptek PocketDV)
32http://www.harmwal.nl/pccam880/ (this project also supports cameras based on this chipset)
33
34supported devices:
35------ ------- ----------- -----
36Vendor Product Distributor Model
37------ ------- ----------- -----
380x08ca 0x0109 Aiptek PocketDV 3300
390x08ca 0x0109 Maxell Maxcam PRO DV3
400x041e 0x4024 Creative PC-CAM 880
410x0d64 0x0108 Aiptek Fidelity 3200
420x0d64 0x0108 Praktica DCZ 1.3 S
430x0d64 0x0108 Genius Digital Camera (?)
440x0d64 0x0108 DXG Technology Fashion Cam
450x0546 0x3187 Polaroid iON 230
460x0d64 0x3108 Praktica Exakta DC 2200
470x0d64 0x3108 Genius G-Shot D211
480x0595 0x4343 Concord Eye-Q Duo 1300
490x0595 0x4343 Concord Eye-Q Duo 2000
500x0595 0x4343 Fujifilm EX-10
510x0595 0x4343 Ricoh RDC-6000
520x0595 0x4343 Digitrex DSC 1300
530x0595 0x4343 Firstline FDC 2000
540x0bb0 0x500d Concord EyeQ Go Wireless
550x0feb 0x2004 CRS Electronic 3.3 Digital Camera
560x0feb 0x2004 Packard Bell DSC-300
570x055f 0xb500 Mustek MDC 3000
580x08ca 0x2062 Aiptek PocketDV 5700
590x052b 0x1a18 Chiphead Megapix V12
600x04c8 0x0729 Konica Revio 2
610x04f2 0xa208 Creative PC-CAM 850
620x0784 0x0040 Traveler Slimline X5
630x06d6 0x0034 Trust Powerc@m 750
640x0a17 0x0062 Pentax Optio 50L
65
diff --git a/MAINTAINERS b/MAINTAINERS
index 5519d257b556..456a3dbcc447 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -55,7 +55,7 @@ trivial patch so apply some common sense.
55 55
568. Happy hacking. 568. Happy hacking.
57 57
58 ----------------------------------- 58 -----------------------------------
59 59
60Maintainers List (try to look for most precise areas first) 60Maintainers List (try to look for most precise areas first)
61 61
@@ -907,7 +907,7 @@ L: linux-cifs-client@lists.samba.org
907L: samba-technical@lists.samba.org 907L: samba-technical@lists.samba.org
908W: http://us1.samba.org/samba/Linux_CIFS_client.html 908W: http://us1.samba.org/samba/Linux_CIFS_client.html
909T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git 909T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
910S: Supported 910S: Supported
911 911
912CONFIGFS 912CONFIGFS
913P: Joel Becker 913P: Joel Becker
@@ -1549,19 +1549,19 @@ P: Chirag Kantharia
1549M: chirag.kantharia@hp.com 1549M: chirag.kantharia@hp.com
1550L: iss_storagedev@hp.com 1550L: iss_storagedev@hp.com
1551S: Maintained 1551S: Maintained
1552 1552
1553HEWLETT-PACKARD SMART2 RAID DRIVER 1553HEWLETT-PACKARD SMART2 RAID DRIVER
1554P: Chirag Kantharia 1554P: Chirag Kantharia
1555M: chirag.kantharia@hp.com 1555M: chirag.kantharia@hp.com
1556L: iss_storagedev@hp.com 1556L: iss_storagedev@hp.com
1557S: Maintained 1557S: Maintained
1558 1558
1559HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss) 1559HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss)
1560P: Mike Miller 1560P: Mike Miller
1561M: mike.miller@hp.com 1561M: mike.miller@hp.com
1562L: iss_storagedev@hp.com 1562L: iss_storagedev@hp.com
1563S: Supported 1563S: Supported
1564 1564
1565HOST AP DRIVER 1565HOST AP DRIVER
1566P: Jouni Malinen 1566P: Jouni Malinen
1567M: jkmaline@cc.hut.fi 1567M: jkmaline@cc.hut.fi
@@ -1673,7 +1673,7 @@ P: Jack Hammer
1673P: Dave Jeffery 1673P: Dave Jeffery
1674M: ipslinux@adaptec.com 1674M: ipslinux@adaptec.com
1675W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html 1675W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html
1676S: Supported 1676S: Supported
1677 1677
1678IDE SUBSYSTEM 1678IDE SUBSYSTEM
1679P: Bartlomiej Zolnierkiewicz 1679P: Bartlomiej Zolnierkiewicz
@@ -1975,7 +1975,7 @@ M: kai@germaschewski.name
1975P: Sam Ravnborg 1975P: Sam Ravnborg
1976M: sam@ravnborg.org 1976M: sam@ravnborg.org
1977T: git kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git 1977T: git kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git
1978S: Maintained 1978S: Maintained
1979 1979
1980KERNEL JANITORS 1980KERNEL JANITORS
1981P: Several 1981P: Several
@@ -2155,7 +2155,7 @@ S: Maintained
2155LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) 2155LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks)
2156P: Richard Russon (FlatCap) 2156P: Richard Russon (FlatCap)
2157M: ldm@flatcap.org 2157M: ldm@flatcap.org
2158L: ldm-devel@lists.sourceforge.net 2158L: ldm-devel@lists.sourceforge.net
2159W: http://ldm.sourceforge.net 2159W: http://ldm.sourceforge.net
2160S: Maintained 2160S: Maintained
2161 2161
@@ -2504,13 +2504,13 @@ P: Kurt Hackel
2504M: kurt.hackel@oracle.com 2504M: kurt.hackel@oracle.com
2505L: ocfs2-devel@oss.oracle.com 2505L: ocfs2-devel@oss.oracle.com
2506W: http://oss.oracle.com/projects/ocfs2/ 2506W: http://oss.oracle.com/projects/ocfs2/
2507S: Supported 2507S: Supported
2508 2508
2509OLYMPIC NETWORK DRIVER 2509OLYMPIC NETWORK DRIVER
2510P: Peter De Shrijver 2510P: Peter De Shrijver
2511M: p2@ace.ulyssis.student.kuleuven.ac.be 2511M: p2@ace.ulyssis.student.kuleuven.ac.be
2512P: Mike Phillips 2512P: Mike Phillips
2513M: mikep@linuxtr.net 2513M: mikep@linuxtr.net
2514L: netdev@vger.kernel.org 2514L: netdev@vger.kernel.org
2515L: linux-tr@linuxtr.net 2515L: linux-tr@linuxtr.net
2516W: http://www.linuxtr.net 2516W: http://www.linuxtr.net
@@ -3045,7 +3045,7 @@ SIS FRAMEBUFFER DRIVER
3045P: Thomas Winischhofer 3045P: Thomas Winischhofer
3046M: thomas@winischhofer.net 3046M: thomas@winischhofer.net
3047W: http://www.winischhofer.net/linuxsisvga.shtml 3047W: http://www.winischhofer.net/linuxsisvga.shtml
3048S: Maintained 3048S: Maintained
3049 3049
3050SIS USB2VGA DRIVER 3050SIS USB2VGA DRIVER
3051P: Thomas Winischhofer 3051P: Thomas Winischhofer
@@ -3649,6 +3649,14 @@ L: linux-usb-devel@lists.sourceforge.net
3649W: http://linux-lc100020.sourceforge.net 3649W: http://linux-lc100020.sourceforge.net
3650S: Maintained 3650S: Maintained
3651 3651
3652USB ZR364XX DRIVER
3653P: Antoine Jacquet
3654M: royale@zerezo.com
3655L: linux-usb-devel@lists.sourceforge.net
3656L: video4linux-list@redhat.com
3657W: http://royale.zerezo.com/zr364xx/
3658S: Maintained
3659
3652USER-MODE LINUX 3660USER-MODE LINUX
3653P: Jeff Dike 3661P: Jeff Dike
3654M: jdike@karaya.com 3662M: jdike@karaya.com
@@ -3656,7 +3664,7 @@ L: user-mode-linux-devel@lists.sourceforge.net
3656L: user-mode-linux-user@lists.sourceforge.net 3664L: user-mode-linux-user@lists.sourceforge.net
3657W: http://user-mode-linux.sourceforge.net 3665W: http://user-mode-linux.sourceforge.net
3658S: Maintained 3666S: Maintained
3659 3667
3660FAT/VFAT/MSDOS FILESYSTEM: 3668FAT/VFAT/MSDOS FILESYSTEM:
3661P: OGAWA Hirofumi 3669P: OGAWA Hirofumi
3662M: hirofumi@mail.parknet.co.jp 3670M: hirofumi@mail.parknet.co.jp
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 7a6105153f23..fa0a87679190 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -761,6 +761,18 @@ source "drivers/media/video/zc0301/Kconfig"
761 761
762source "drivers/media/video/pwc/Kconfig" 762source "drivers/media/video/pwc/Kconfig"
763 763
764config USB_ZR364XX
765 tristate "USB ZR364XX Camera support"
766 depends on USB && VIDEO_V4L2
767 ---help---
768 Say Y here if you want to connect this type of camera to your
769 computer's USB port.
770 See <file:Documentation/video4linux/zr364xx.txt> for more info
771 and list of supported cameras.
772
773 To compile this driver as a module, choose M here: the
774 module will be called zr364xx.
775
764endmenu # V4L USB devices 776endmenu # V4L USB devices
765 777
766endmenu 778endmenu
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 44ccaed40b49..384f01c133c5 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_USB_OV511) += ov511.o
99obj-$(CONFIG_USB_SE401) += se401.o 99obj-$(CONFIG_USB_SE401) += se401.o
100obj-$(CONFIG_USB_STV680) += stv680.o 100obj-$(CONFIG_USB_STV680) += stv680.o
101obj-$(CONFIG_USB_W9968CF) += w9968cf.o 101obj-$(CONFIG_USB_W9968CF) += w9968cf.o
102obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
102 103
103obj-$(CONFIG_USB_SN9C102) += sn9c102/ 104obj-$(CONFIG_USB_SN9C102) += sn9c102/
104obj-$(CONFIG_USB_ET61X251) += et61x251/ 105obj-$(CONFIG_USB_ET61X251) += et61x251/
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
new file mode 100644
index 000000000000..c055cf017cf5
--- /dev/null
+++ b/drivers/media/video/zr364xx.c
@@ -0,0 +1,929 @@
1/*
2 * Zoran 364xx based USB webcam module version 0.72
3 *
4 * Allows you to use your USB webcam with V4L2 applications
5 * This is still in heavy developpement !
6 *
7 * Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com>
8 * http://royale.zerezo.com/zr364xx/
9 *
10 * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers
11 * V4L2 version inspired by meye.c driver
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28
29#include <linux/version.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/usb.h>
33#include <linux/vmalloc.h>
34#include <linux/slab.h>
35#include <linux/proc_fs.h>
36#include <media/v4l2-common.h>
37
38
39/* Version Information */
40#define DRIVER_VERSION "v0.72"
41#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
42#define DRIVER_DESC "Zoran 364xx"
43
44
45/* Camera */
46#define FRAMES 2
47#define MAX_FRAME_SIZE 100000
48#define BUFFER_SIZE 0x1000
49#define CTRL_TIMEOUT 500
50
51
52/* Debug macro */
53#define DBG(x...) if (debug) info(x)
54
55
56/* Init methods, need to find nicer names for these
57 * the exact names of the chipsets would be the best if someone finds it */
58#define METHOD0 0
59#define METHOD1 1
60#define METHOD2 2
61
62
63/* Module parameters */
64static int debug = 0;
65static int mode = 0;
66
67
68/* Module parameters interface */
69module_param(debug, int, 0644);
70MODULE_PARM_DESC(debug, "Debug level");
71module_param(mode, int, 0644);
72MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480");
73
74
75/* Devices supported by this driver
76 * .driver_info contains the init method used by the camera */
77static struct usb_device_id device_table[] = {
78 {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 },
79 {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 },
80 {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 },
81 {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 },
82 {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 },
83 {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 },
84 {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 },
85 {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 },
86 {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 },
87 {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 },
88 {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 },
89 {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 },
90 {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 },
91 {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 },
92 {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 },
93 {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 },
94 {} /* Terminating entry */
95};
96
97MODULE_DEVICE_TABLE(usb, device_table);
98
99
100/* Camera stuff */
101struct zr364xx_camera {
102 struct usb_device *udev; /* save off the usb device pointer */
103 struct usb_interface *interface;/* the interface for this device */
104 struct video_device *vdev; /* v4l video device */
105 u8 *framebuf;
106 int nb;
107 unsigned char *buffer;
108 int skip;
109 int brightness;
110 int width;
111 int height;
112 int method;
113 struct mutex lock;
114};
115
116
117/* function used to send initialisation commands to the camera */
118static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
119 u16 index, unsigned char *cp, u16 size)
120{
121 int status;
122
123 unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL);
124 if (!transfer_buffer) {
125 info("kmalloc(%d) failed", size);
126 return -ENOMEM;
127 }
128
129 memcpy(transfer_buffer, cp, size);
130
131 status = usb_control_msg(udev,
132 usb_sndctrlpipe(udev, 0),
133 request,
134 USB_DIR_OUT | USB_TYPE_VENDOR |
135 USB_RECIP_DEVICE, value, index,
136 transfer_buffer, size, CTRL_TIMEOUT);
137
138 kfree(transfer_buffer);
139
140 if (status < 0)
141 info("Failed sending control message, error %d.", status);
142
143 return status;
144}
145
146
147/* Control messages sent to the camera to initialize it
148 * and launch the capture */
149typedef struct {
150 unsigned int value;
151 unsigned int size;
152 unsigned char *bytes;
153} message;
154
155/* method 0 */
156static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
157static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 };
158static unsigned char m0d3[] = { 0, 0 };
159static message m0[] = {
160 {0x1f30, 0, NULL},
161 {0xd000, 0, NULL},
162 {0x3370, sizeof(m0d1), m0d1},
163 {0x2000, 0, NULL},
164 {0x2f0f, 0, NULL},
165 {0x2610, sizeof(m0d2), m0d2},
166 {0xe107, 0, NULL},
167 {0x2502, 0, NULL},
168 {0x1f70, 0, NULL},
169 {0xd000, 0, NULL},
170 {0x9a01, sizeof(m0d3), m0d3},
171 {-1, -1, NULL}
172};
173
174/* method 1 */
175static unsigned char m1d1[] = { 0xff, 0xff };
176static unsigned char m1d2[] = { 0x00, 0x00 };
177static message m1[] = {
178 {0x1f30, 0, NULL},
179 {0xd000, 0, NULL},
180 {0xf000, 0, NULL},
181 {0x2000, 0, NULL},
182 {0x2f0f, 0, NULL},
183 {0x2650, 0, NULL},
184 {0xe107, 0, NULL},
185 {0x2502, sizeof(m1d1), m1d1},
186 {0x1f70, 0, NULL},
187 {0xd000, 0, NULL},
188 {0xd000, 0, NULL},
189 {0xd000, 0, NULL},
190 {0x9a01, sizeof(m1d2), m1d2},
191 {-1, -1, NULL}
192};
193
194/* method 2 */
195static unsigned char m2d1[] = { 0xff, 0xff };
196static message m2[] = {
197 {0x1f30, 0, NULL},
198 {0xf000, 0, NULL},
199 {0x2000, 0, NULL},
200 {0x2f0f, 0, NULL},
201 {0x2650, 0, NULL},
202 {0xe107, 0, NULL},
203 {0x2502, sizeof(m2d1), m2d1},
204 {0x1f70, 0, NULL},
205 {-1, -1, NULL}
206};
207
208/* init table */
209static message *init[3] = { m0, m1, m2 };
210
211
212/* JPEG static data in header (Huffman table, etc) */
213static unsigned char header1[] = {
214 0xFF, 0xD8,
215 /*
216 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F',
217 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88,
218 */
219 0xFF, 0xDB, 0x00, 0x84
220};
221static unsigned char header2[] = {
222 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01,
223 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
225 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
226 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01,
227 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
228 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
229 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33,
230 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25,
231 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
232 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54,
233 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
234 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
235 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94,
236 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
237 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
238 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
239 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
240 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3,
241 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F,
242 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
243 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
244 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5,
245 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
246 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
247 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
248 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1,
249 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16,
250 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27,
251 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
252 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57,
253 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
254 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84,
255 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
256 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
257 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA,
258 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3,
259 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5,
260 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
261 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01,
262 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01,
263 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
264 0x00, 0x3F, 0x00
265};
266static unsigned char header3;
267
268
269
270/********************/
271/* V4L2 integration */
272/********************/
273
274/* this function reads a full JPEG picture synchronously
275 * TODO: do it asynchronously... */
276static int read_frame(struct zr364xx_camera *cam, int framenum)
277{
278 int i, n, temp, head, size, actual_length;
279 unsigned char *ptr = NULL, *jpeg, swap;
280
281 redo:
282 /* hardware brightness */
283 n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
284 temp = (0x60 << 8) + 127 - cam->brightness;
285 n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
286
287 /* during the first loop we are going to insert JPEG header */
288 head = 0;
289 /* this is the place in memory where we are going to build
290 * the JPEG image */
291 jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE;
292 /* read data... */
293 do {
294 n = usb_bulk_msg(cam->udev,
295 usb_rcvbulkpipe(cam->udev, 0x81),
296 cam->buffer, BUFFER_SIZE, &actual_length,
297 CTRL_TIMEOUT);
298 DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]);
299 DBG("bulk : n=%d size=%d", n, actual_length);
300 if (n < 0) {
301 info("error reading bulk msg");
302 return 0;
303 }
304 if (actual_length < 0 || actual_length > BUFFER_SIZE) {
305 info("wrong number of bytes");
306 return 0;
307 }
308
309 /* swap bytes if camera needs it */
310 if (cam->method == METHOD0)
311 for (i = 0; i < BUFFER_SIZE; i += 2) {
312 swap = cam->buffer[i];
313 cam->buffer[i] = cam->buffer[i + 1];
314 cam->buffer[i + 1] = swap;
315 }
316
317 /* write the JPEG header */
318 if (!head) {
319 DBG("jpeg header");
320 ptr = jpeg;
321 memcpy(ptr, header1, sizeof(header1));
322 ptr += sizeof(header1);
323 header3 = 0;
324 memcpy(ptr, &header3, 1);
325 ptr++;
326 memcpy(ptr, cam->buffer, 64);
327 ptr += 64;
328 header3 = 1;
329 memcpy(ptr, &header3, 1);
330 ptr++;
331 memcpy(ptr, cam->buffer + 64, 64);
332 ptr += 64;
333 memcpy(ptr, header2, sizeof(header2));
334 ptr += sizeof(header2);
335 memcpy(ptr, cam->buffer + 128,
336 actual_length - 128);
337 ptr += actual_length - 128;
338 head = 1;
339 DBG("header : %d %d %d %d %d %d %d %d %d",
340 cam->buffer[0], cam->buffer[1], cam->buffer[2],
341 cam->buffer[3], cam->buffer[4], cam->buffer[5],
342 cam->buffer[6], cam->buffer[7], cam->buffer[8]);
343 } else {
344 memcpy(ptr, cam->buffer, actual_length);
345 ptr += actual_length;
346 }
347 }
348 /* ... until there is no more */
349 while (actual_length == BUFFER_SIZE);
350
351 /* we skip the 2 first frames which are usually buggy */
352 if (cam->skip) {
353 cam->skip--;
354 goto redo;
355 }
356
357 /* go back to find the JPEG EOI marker */
358 size = ptr - jpeg;
359 ptr -= 2;
360 while (ptr > jpeg) {
361 if (*ptr == 0xFF && *(ptr + 1) == 0xD9
362 && *(ptr + 2) == 0xFF)
363 break;
364 ptr--;
365 }
366 if (ptr == jpeg)
367 DBG("No EOI marker");
368
369 /* Sometimes there is junk data in the middle of the picture,
370 * we want to skip this bogus frames */
371 while (ptr > jpeg) {
372 if (*ptr == 0xFF && *(ptr + 1) == 0xFF
373 && *(ptr + 2) == 0xFF)
374 break;
375 ptr--;
376 }
377 if (ptr != jpeg) {
378 DBG("Bogus frame ? %d", cam->nb);
379 goto redo;
380 }
381
382 DBG("jpeg : %d %d %d %d %d %d %d %d",
383 jpeg[0], jpeg[1], jpeg[2], jpeg[3],
384 jpeg[4], jpeg[5], jpeg[6], jpeg[7]);
385
386 return size;
387}
388
389
390static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt,
391 loff_t * ppos)
392{
393 unsigned long count = cnt;
394 struct video_device *vdev = video_devdata(file);
395 struct zr364xx_camera *cam;
396
397 DBG("zr364xx_read: read %d bytes.", (int) count);
398
399 if (vdev == NULL)
400 return -ENODEV;
401 cam = video_get_drvdata(vdev);
402
403 if (!buf)
404 return -EINVAL;
405
406 if (!count)
407 return -EINVAL;
408
409 /* NoMan Sux ! */
410 count = read_frame(cam, 0);
411
412 if (copy_to_user(buf, cam->framebuf, count))
413 return -EFAULT;
414
415 return count;
416}
417
418
419static int zr364xx_vidioc_querycap(struct file *file, void *priv,
420 struct v4l2_capability *cap)
421{
422 memset(cap, 0, sizeof(*cap));
423 strcpy(cap->driver, DRIVER_DESC);
424 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
425 return 0;
426}
427
428static int zr364xx_vidioc_enum_input(struct file *file, void *priv,
429 struct v4l2_input *i)
430{
431 if (i->index != 0)
432 return -EINVAL;
433 memset(i, 0, sizeof(*i));
434 i->index = 0;
435 strcpy(i->name, DRIVER_DESC " Camera");
436 i->type = V4L2_INPUT_TYPE_CAMERA;
437 return 0;
438}
439
440static int zr364xx_vidioc_g_input(struct file *file, void *priv,
441 unsigned int *i)
442{
443 *i = 0;
444 return 0;
445}
446
447static int zr364xx_vidioc_s_input(struct file *file, void *priv,
448 unsigned int i)
449{
450 if (i != 0)
451 return -EINVAL;
452 return 0;
453}
454
455static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
456 struct v4l2_queryctrl *c)
457{
458 struct video_device *vdev = video_devdata(file);
459 struct zr364xx_camera *cam;
460
461 if (vdev == NULL)
462 return -ENODEV;
463 cam = video_get_drvdata(vdev);
464
465 switch (c->id) {
466 case V4L2_CID_BRIGHTNESS:
467 c->type = V4L2_CTRL_TYPE_INTEGER;
468 strcpy(c->name, "Brightness");
469 c->minimum = 0;
470 c->maximum = 127;
471 c->step = 1;
472 c->default_value = cam->brightness;
473 c->flags = 0;
474 break;
475 default:
476 return -EINVAL;
477 }
478 return 0;
479}
480
481static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
482 struct v4l2_control *c)
483{
484 struct video_device *vdev = video_devdata(file);
485 struct zr364xx_camera *cam;
486
487 if (vdev == NULL)
488 return -ENODEV;
489 cam = video_get_drvdata(vdev);
490
491 switch (c->id) {
492 case V4L2_CID_BRIGHTNESS:
493 cam->brightness = c->value;
494 break;
495 default:
496 return -EINVAL;
497 }
498 return 0;
499}
500
501static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
502 struct v4l2_control *c)
503{
504 struct video_device *vdev = video_devdata(file);
505 struct zr364xx_camera *cam;
506
507 if (vdev == NULL)
508 return -ENODEV;
509 cam = video_get_drvdata(vdev);
510
511 switch (c->id) {
512 case V4L2_CID_BRIGHTNESS:
513 c->value = cam->brightness;
514 break;
515 default:
516 return -EINVAL;
517 }
518 return 0;
519}
520
521static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
522 void *priv, struct v4l2_fmtdesc *f)
523{
524 if (f->index > 0)
525 return -EINVAL;
526 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
527 return -EINVAL;
528 memset(f, 0, sizeof(*f));
529 f->index = 0;
530 f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
531 f->flags = V4L2_FMT_FLAG_COMPRESSED;
532 strcpy(f->description, "JPEG");
533 f->pixelformat = V4L2_PIX_FMT_JPEG;
534 return 0;
535}
536
537static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
538 struct v4l2_format *f)
539{
540 struct video_device *vdev = video_devdata(file);
541 struct zr364xx_camera *cam;
542
543 if (vdev == NULL)
544 return -ENODEV;
545 cam = video_get_drvdata(vdev);
546
547 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
548 return -EINVAL;
549 if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
550 return -EINVAL;
551 if (f->fmt.pix.field != V4L2_FIELD_ANY &&
552 f->fmt.pix.field != V4L2_FIELD_NONE)
553 return -EINVAL;
554 f->fmt.pix.field = V4L2_FIELD_NONE;
555 f->fmt.pix.width = cam->width;
556 f->fmt.pix.height = cam->height;
557 f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
558 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
559 f->fmt.pix.colorspace = 0;
560 f->fmt.pix.priv = 0;
561 return 0;
562}
563
564static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
565 struct v4l2_format *f)
566{
567 struct video_device *vdev = video_devdata(file);
568 struct zr364xx_camera *cam;
569
570 if (vdev == NULL)
571 return -ENODEV;
572 cam = video_get_drvdata(vdev);
573
574 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
575 return -EINVAL;
576 memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
577 f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
578 f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
579 f->fmt.pix.field = V4L2_FIELD_NONE;
580 f->fmt.pix.width = cam->width;
581 f->fmt.pix.height = cam->height;
582 f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
583 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
584 f->fmt.pix.colorspace = 0;
585 f->fmt.pix.priv = 0;
586 return 0;
587}
588
589static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv,
590 struct v4l2_format *f)
591{
592 struct video_device *vdev = video_devdata(file);
593 struct zr364xx_camera *cam;
594
595 if (vdev == NULL)
596 return -ENODEV;
597 cam = video_get_drvdata(vdev);
598
599 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
600 return -EINVAL;
601 if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
602 return -EINVAL;
603 if (f->fmt.pix.field != V4L2_FIELD_ANY &&
604 f->fmt.pix.field != V4L2_FIELD_NONE)
605 return -EINVAL;
606 f->fmt.pix.field = V4L2_FIELD_NONE;
607 f->fmt.pix.width = cam->width;
608 f->fmt.pix.height = cam->height;
609 f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
610 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
611 f->fmt.pix.colorspace = 0;
612 f->fmt.pix.priv = 0;
613 DBG("ok!");
614 return 0;
615}
616
617static int zr364xx_vidioc_streamon(struct file *file, void *priv,
618 enum v4l2_buf_type type)
619{
620 return 0;
621}
622
623static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
624 enum v4l2_buf_type type)
625{
626 return 0;
627}
628
629
630/* open the camera */
631static int zr364xx_open(struct inode *inode, struct file *file)
632{
633 struct video_device *vdev = video_devdata(file);
634 struct zr364xx_camera *cam = video_get_drvdata(vdev);
635 struct usb_device *udev = cam->udev;
636 int i, err;
637
638 DBG("zr364xx_open");
639
640 cam->skip = 2;
641
642 err = video_exclusive_open(inode, file);
643 if (err < 0)
644 return err;
645
646 if (!cam->framebuf) {
647 cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES);
648 if (!cam->framebuf) {
649 info("vmalloc_32 failed!");
650 return -ENOMEM;
651 }
652 }
653
654 mutex_lock(&cam->lock);
655 for (i = 0; init[cam->method][i].size != -1; i++) {
656 err =
657 send_control_msg(udev, 1, init[cam->method][i].value,
658 0, init[cam->method][i].bytes,
659 init[cam->method][i].size);
660 if (err < 0) {
661 info("error during open sequence: %d", i);
662 mutex_unlock(&cam->lock);
663 return err;
664 }
665 }
666
667 file->private_data = vdev;
668
669 /* Added some delay here, since opening/closing the camera quickly,
670 * like Ekiga does during its startup, can crash the webcam
671 */
672 mdelay(100);
673
674 mutex_unlock(&cam->lock);
675 return 0;
676}
677
678
679/* release the camera */
680static int zr364xx_release(struct inode *inode, struct file *file)
681{
682 struct video_device *vdev = video_devdata(file);
683 struct zr364xx_camera *cam;
684 struct usb_device *udev;
685 int i, err;
686
687 DBG("zr364xx_release");
688
689 if (vdev == NULL)
690 return -ENODEV;
691 cam = video_get_drvdata(vdev);
692
693 udev = cam->udev;
694
695 mutex_lock(&cam->lock);
696 for (i = 0; i < 2; i++) {
697 err =
698 send_control_msg(udev, 1, init[cam->method][i].value,
699 0, init[i][cam->method].bytes,
700 init[cam->method][i].size);
701 if (err < 0) {
702 info("error during release sequence");
703 mutex_unlock(&cam->lock);
704 return err;
705 }
706 }
707
708 file->private_data = NULL;
709 video_exclusive_release(inode, file);
710
711 /* Added some delay here, since opening/closing the camera quickly,
712 * like Ekiga does during its startup, can crash the webcam
713 */
714 mdelay(100);
715
716 mutex_unlock(&cam->lock);
717 return 0;
718}
719
720
721static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
722{
723 void *pos;
724 unsigned long start = vma->vm_start;
725 unsigned long size = vma->vm_end - vma->vm_start;
726 struct video_device *vdev = video_devdata(file);
727 struct zr364xx_camera *cam;
728
729 DBG("zr364xx_mmap: %ld\n", size);
730
731 if (vdev == NULL)
732 return -ENODEV;
733 cam = video_get_drvdata(vdev);
734
735 pos = cam->framebuf;
736 while (size > 0) {
737 if (vm_insert_page(vma, start, vmalloc_to_page(pos)))
738 return -EAGAIN;
739 start += PAGE_SIZE;
740 pos += PAGE_SIZE;
741 if (size > PAGE_SIZE)
742 size -= PAGE_SIZE;
743 else
744 size = 0;
745 }
746
747 return 0;
748}
749
750
751static struct file_operations zr364xx_fops = {
752 .owner = THIS_MODULE,
753 .open = zr364xx_open,
754 .release = zr364xx_release,
755 .read = zr364xx_read,
756 .mmap = zr364xx_mmap,
757 .ioctl = video_ioctl2,
758 .llseek = no_llseek,
759};
760
761static struct video_device zr364xx_template = {
762 .owner = THIS_MODULE,
763 .name = DRIVER_DESC,
764 .type = VID_TYPE_CAPTURE,
765 .fops = &zr364xx_fops,
766 .release = video_device_release,
767 .minor = -1,
768
769 .vidioc_querycap = zr364xx_vidioc_querycap,
770 .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap,
771 .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap,
772 .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap,
773 .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap,
774 .vidioc_enum_input = zr364xx_vidioc_enum_input,
775 .vidioc_g_input = zr364xx_vidioc_g_input,
776 .vidioc_s_input = zr364xx_vidioc_s_input,
777 .vidioc_streamon = zr364xx_vidioc_streamon,
778 .vidioc_streamoff = zr364xx_vidioc_streamoff,
779 .vidioc_queryctrl = zr364xx_vidioc_queryctrl,
780 .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl,
781 .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl,
782};
783
784
785
786/*******************/
787/* USB integration */
788/*******************/
789
790static int zr364xx_probe(struct usb_interface *intf,
791 const struct usb_device_id *id)
792{
793 struct usb_device *udev = interface_to_usbdev(intf);
794 struct zr364xx_camera *cam = NULL;
795
796 DBG("probing...");
797
798 info(DRIVER_DESC " compatible webcam plugged");
799 info("model %04x:%04x detected", udev->descriptor.idVendor,
800 udev->descriptor.idProduct);
801
802 if ((cam =
803 kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) {
804 info("cam: out of memory !");
805 return -ENODEV;
806 }
807 memset(cam, 0x00, sizeof(struct zr364xx_camera));
808 /* save the init method used by this camera */
809 cam->method = id->driver_info;
810
811 cam->vdev = video_device_alloc();
812 if (cam->vdev == NULL) {
813 info("cam->vdev: out of memory !");
814 kfree(cam);
815 return -ENODEV;
816 }
817 memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
818 video_set_drvdata(cam->vdev, cam);
819 if (debug)
820 cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
821
822 cam->udev = udev;
823
824 if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) {
825 info("cam->buffer: out of memory !");
826 video_device_release(cam->vdev);
827 kfree(cam);
828 return -ENODEV;
829 }
830
831 switch (mode) {
832 case 1:
833 info("160x120 mode selected");
834 cam->width = 160;
835 cam->height = 120;
836 break;
837 case 2:
838 info("640x480 mode selected");
839 cam->width = 640;
840 cam->height = 480;
841 break;
842 default:
843 info("320x240 mode selected");
844 cam->width = 320;
845 cam->height = 240;
846 break;
847 }
848
849 m0d1[0] = mode;
850 m1[2].value = 0xf000 + mode;
851 m2[1].value = 0xf000 + mode;
852 header2[437] = cam->height / 256;
853 header2[438] = cam->height % 256;
854 header2[439] = cam->width / 256;
855 header2[440] = cam->width % 256;
856
857 cam->nb = 0;
858 cam->brightness = 64;
859 mutex_init(&cam->lock);
860
861 if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) {
862 info("video_register_device failed");
863 video_device_release(cam->vdev);
864 kfree(cam->buffer);
865 kfree(cam);
866 return -ENODEV;
867 }
868
869 usb_set_intfdata(intf, cam);
870
871 info(DRIVER_DESC " controlling video device %d", cam->vdev->minor);
872 return 0;
873}
874
875
876static void zr364xx_disconnect(struct usb_interface *intf)
877{
878 struct zr364xx_camera *cam = usb_get_intfdata(intf);
879 usb_set_intfdata(intf, NULL);
880 dev_set_drvdata(&intf->dev, NULL);
881 info(DRIVER_DESC " webcam unplugged");
882 if (cam->vdev)
883 video_unregister_device(cam->vdev);
884 cam->vdev = NULL;
885 kfree(cam->buffer);
886 if (cam->framebuf)
887 vfree(cam->framebuf);
888 kfree(cam);
889}
890
891
892
893/**********************/
894/* Module integration */
895/**********************/
896
897static struct usb_driver zr364xx_driver = {
898 .name = "zr364xx",
899 .probe = zr364xx_probe,
900 .disconnect = zr364xx_disconnect,
901 .id_table = device_table
902};
903
904
905static int __init zr364xx_init(void)
906{
907 int retval;
908 retval = usb_register(&zr364xx_driver) < 0;
909 if (retval)
910 info("usb_register failed!");
911 else
912 info(DRIVER_DESC " module loaded");
913 return retval;
914}
915
916
917static void __exit zr364xx_exit(void)
918{
919 info(DRIVER_DESC " module unloaded");
920 usb_deregister(&zr364xx_driver);
921}
922
923
924module_init(zr364xx_init);
925module_exit(zr364xx_exit);
926
927MODULE_AUTHOR(DRIVER_AUTHOR);
928MODULE_DESCRIPTION(DRIVER_DESC);
929MODULE_LICENSE("GPL");